Summary
When an AI agent running inside a sandbox container attempts to write files using container-internal paths (e.g., /workspace/test.svg), the path validation in wrapSandboxPathGuard fails with "Path escapes sandbox root" error, even though the path is valid within the container.
Environment
- OpenClaw version: latest (commit 1b0ffeeb3)
- Sandbox mode:
all
- Workspace access:
rw
- Docker sandbox with default workdir:
/workspace
Steps to Reproduce
-
Configure an agent with sandbox enabled:
{
"agents": {
"defaults": {
"sandbox": {
"mode": "all",
"workspaceAccess": "rw",
"scope": "agent"
}
}
}
}
-
Ask the AI to create/write a file, e.g., "create an image file at /workspace/test.svg"
-
The AI attempts to use the write tool with path /workspace/test.svg
Expected Behavior
The write operation should succeed because:
/workspace is the container's workdir (mapped to the actual workspace directory)
workspaceAccess is set to rw
- The path is within the sandbox's allowed area
Actual Behavior
The operation fails with:
[tools] write failed: Path escapes sandbox root (~/.openclaw/workspace/team): /workspace/test_image.svg
Root Cause Analysis
The issue is in src/agents/pi-tools.read.ts in the wrapSandboxPathGuard function:
function wrapSandboxPathGuard(tool: AnyAgentTool, root: string): AnyAgentTool {
return {
...tool,
execute: async (toolCallId, args, signal, onUpdate) => {
const filePath = record?.path; // e.g., "/workspace/test.svg"
if (typeof filePath === "string" && filePath.trim()) {
await assertSandboxPath({ filePath, cwd: root, root }); // Direct validation without path mapping
}
return tool.execute(toolCallId, normalized ?? args, signal, onUpdate);
},
};
}
The assertSandboxPath function in src/agents/sandbox-paths.ts then does:
export function resolveSandboxPath(params: { filePath: string; cwd: string; root: string }) {
const resolved = resolveToCwd(params.filePath, params.cwd); // "/workspace/test.svg" (absolute, unchanged)
const rootResolved = path.resolve(params.root); // "/home/node/.openclaw/workspace/team"
const relative = path.relative(rootResolved, resolved); // "../../workspace/test.svg" (starts with "..")
if (relative.startsWith("..") || path.isAbsolute(relative)) {
throw new Error(`Path escapes sandbox root...`); // ERROR!
}
}
The problem: The AI uses container-internal paths (/workspace/...), but the path validation compares these directly against the host filesystem path (/home/node/.openclaw/workspace/team). There's no mapping from the container workdir (/workspace) to the actual workspace directory.
Suggested Fix
Before validating the path, map container-internal paths to the actual filesystem path:
function wrapSandboxPathGuard(tool: AnyAgentTool, root: string, containerWorkdir?: string): AnyAgentTool {
return {
...tool,
execute: async (toolCallId, args, signal, onUpdate) => {
let filePath = record?.path;
// Map container-internal paths to actual filesystem paths
const workdir = containerWorkdir ?? '/workspace';
if (typeof filePath === "string" && filePath.startsWith(workdir + '/')) {
filePath = path.join(root, filePath.slice(workdir.length + 1));
} else if (filePath === workdir) {
filePath = root;
}
if (typeof filePath === "string" && filePath.trim()) {
await assertSandboxPath({ filePath, cwd: root, root });
}
return tool.execute(toolCallId, normalized ?? args, signal, onUpdate);
},
};
}
The containerWorkdir can be obtained from SandboxContext.containerWorkdir (which comes from cfg.sandbox.docker.workdir, defaulting to /workspace).
Workaround
Currently, the workaround is to instruct the AI to use relative paths (e.g., media/test.jpg) instead of absolute container paths (/workspace/media/test.jpg). However, this is not intuitive for the AI since it sees /workspace as its working directory inside the container.
Related Code
src/agents/pi-tools.read.ts: wrapSandboxPathGuard, createSandboxedWriteTool, createSandboxedEditTool
src/agents/sandbox-paths.ts: resolveSandboxPath, assertSandboxPath
src/agents/sandbox/types.ts: SandboxContext.containerWorkdir
Summary
When an AI agent running inside a sandbox container attempts to write files using container-internal paths (e.g.,
/workspace/test.svg), the path validation inwrapSandboxPathGuardfails with "Path escapes sandbox root" error, even though the path is valid within the container.Environment
allrw/workspaceSteps to Reproduce
Configure an agent with sandbox enabled:
{ "agents": { "defaults": { "sandbox": { "mode": "all", "workspaceAccess": "rw", "scope": "agent" } } } }Ask the AI to create/write a file, e.g., "create an image file at /workspace/test.svg"
The AI attempts to use the
writetool with path/workspace/test.svgExpected Behavior
The write operation should succeed because:
/workspaceis the container's workdir (mapped to the actual workspace directory)workspaceAccessis set torwActual Behavior
The operation fails with:
Root Cause Analysis
The issue is in
src/agents/pi-tools.read.tsin thewrapSandboxPathGuardfunction:The
assertSandboxPathfunction insrc/agents/sandbox-paths.tsthen does:The problem: The AI uses container-internal paths (
/workspace/...), but the path validation compares these directly against the host filesystem path (/home/node/.openclaw/workspace/team). There's no mapping from the container workdir (/workspace) to the actual workspace directory.Suggested Fix
Before validating the path, map container-internal paths to the actual filesystem path:
The
containerWorkdircan be obtained fromSandboxContext.containerWorkdir(which comes fromcfg.sandbox.docker.workdir, defaulting to/workspace).Workaround
Currently, the workaround is to instruct the AI to use relative paths (e.g.,
media/test.jpg) instead of absolute container paths (/workspace/media/test.jpg). However, this is not intuitive for the AI since it sees/workspaceas its working directory inside the container.Related Code
src/agents/pi-tools.read.ts:wrapSandboxPathGuard,createSandboxedWriteTool,createSandboxedEditToolsrc/agents/sandbox-paths.ts:resolveSandboxPath,assertSandboxPathsrc/agents/sandbox/types.ts:SandboxContext.containerWorkdir