Skip to content

Sandbox path validation fails for container-internal paths (/workspace/...) #9560

@Explorer1092

Description

@Explorer1092

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

  1. Configure an agent with sandbox enabled:

    {
      "agents": {
        "defaults": {
          "sandbox": {
            "mode": "all",
            "workspaceAccess": "rw",
            "scope": "agent"
          }
        }
      }
    }
  2. Ask the AI to create/write a file, e.g., "create an image file at /workspace/test.svg"

  3. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions