Config: newline-join sandbox setupCommand arrays (#31953)

This commit is contained in:
Mark L
2026-03-03 02:11:32 +08:00
committed by GitHub
parent 8b27582509
commit 9b8e642475
4 changed files with 26 additions and 2 deletions

View File

@@ -46,6 +46,7 @@ Docs: https://docs.openclaw.ai
### Fixes
- Sandbox/Docker setup command parsing: accept `agents.*.sandbox.docker.setupCommand` as either a string or a string array, and normalize arrays to newline-delimited shell scripts so multi-step setup commands no longer concatenate without separators. (#31953) Thanks @liuxiaopai-ai.
- Security/Node exec approvals: preserve shell/dispatch-wrapper argv semantics during approval hardening so approved wrapper commands (for example `env sh -c ...`) cannot drift into a different runtime command shape, and add regression coverage for both approval-plan generation and approved runtime execution paths. Thanks @tdjackey for reporting.
- Sandbox/Bootstrap context boundary hardening: reject symlink/hardlink alias bootstrap seed files that resolve outside the source workspace and switch post-compaction `AGENTS.md` context reads to boundary-verified file opens, preventing host file content from being injected via workspace aliasing. Thanks @tdjackey for reporting.
- Browser/Security output boundary hardening: replace check-then-rename output commits with root-bound fd-verified writes, unify install/skills canonical path-boundary checks, and add regression coverage for symlink-rebind race paths across browser output and shared fs-safe write flows. Thanks @tdjackey for reporting.

View File

@@ -7,6 +7,26 @@ import {
import { validateConfigObject } from "./config.js";
describe("sandbox docker config", () => {
it("joins setupCommand arrays with newlines", () => {
const res = validateConfigObject({
agents: {
defaults: {
sandbox: {
docker: {
setupCommand: ["apt-get update", "apt-get install -y curl"],
},
},
},
},
});
expect(res.ok).toBe(true);
if (res.ok) {
expect(res.config.agents?.defaults?.sandbox?.docker?.setupCommand).toBe(
"apt-get update\napt-get install -y curl",
);
}
});
it("accepts safe binds array in sandbox.docker config", () => {
const res = validateConfigObject({
agents: {

View File

@@ -17,7 +17,7 @@ export type SandboxDockerSettings = {
capDrop?: string[];
/** Extra environment variables for sandbox exec. */
env?: Record<string, string>;
/** Optional setup command run once after container creation. */
/** Optional setup command run once after container creation (array entries are joined by newline). */
setupCommand?: string;
/** Limit container PIDs (0 = Docker default). */
pidsLimit?: number;

View File

@@ -102,7 +102,10 @@ export const SandboxDockerSchema = z
user: z.string().optional(),
capDrop: z.array(z.string()).optional(),
env: z.record(z.string(), z.string()).optional(),
setupCommand: z.string().optional(),
setupCommand: z
.union([z.string(), z.array(z.string())])
.transform((value) => (Array.isArray(value) ? value.join("\n") : value))
.optional(),
pidsLimit: z.number().int().positive().optional(),
memory: z.union([z.string(), z.number()]).optional(),
memorySwap: z.union([z.string(), z.number()]).optional(),