Skip to content

Comments

security(message-tool): validate filePath/path against sandbox root#6398

Merged
steipete merged 2 commits intoopenclaw:mainfrom
leszekszpunar:fix/3805-message-tool-sandbox-bypass
Feb 1, 2026
Merged

security(message-tool): validate filePath/path against sandbox root#6398
steipete merged 2 commits intoopenclaw:mainfrom
leszekszpunar:fix/3805-message-tool-sandbox-bypass

Conversation

@leszekszpunar
Copy link
Contributor

@leszekszpunar leszekszpunar commented Feb 1, 2026

Summary

Fixes #3805 (CRITICAL sandbox escape via message tool filePath/path params).

Note: Previous PR #6181 was auto-closed by CLAWDINATOR triage bot. This is a critical security fix for an existing open issue, not a feature or improvement. Issue #3805 documents a confirmed sandbox escape with PoC.

The message tool accepted arbitrary file paths (filePath, path) without validating them against the sandbox root directory. A sandboxed agent could read or exfiltrate host files (e.g. /etc/passwd, ~/.ssh/id_rsa) by passing absolute or traversal paths through the message tool's send action.

Changes

  • src/agents/tools/message-tool.ts: Added sandboxRoot option to MessageToolOptions. When set, both filePath and path params are validated via assertSandboxPath() before runMessageAction() is called. This mirrors the pattern already used in image-tool.ts and read/write/edit tools.
  • src/agents/openclaw-tools.ts: Pass sandboxRoot from tool creation options to createMessageTool().
  • src/agents/tools/message-tool.test.ts: Added 4 tests covering sandbox path validation (reject absolute escape, reject traversal, allow valid relative path, skip validation when no sandbox).

Security Impact

Without this fix, any sandboxed agent session could bypass the sandbox boundary via the message tool to access arbitrary host filesystem paths. This is a critical privilege escalation vector.

AI Disclosure

This PR was developed with AI assistance (Claude Code). The code was reviewed, tested, and verified by the author. All tests pass, linting is clean, and the fix follows existing patterns in the codebase (image-tool.ts, read/write tools).

Test Plan

  • pnpm vitest run src/agents/tools/message-tool.test.ts (8/8 pass)
  • npx oxlint --type-aware --tsconfig tsconfig.oxlint.json (0 errors)
  • npx oxfmt --check (all files formatted)
  • Build errors are pre-existing on main (unrelated pi-coding-agent type issues)

Greptile Overview

Greptile Summary

This PR threads a sandboxRoot option into the message agent tool and uses the existing assertSandboxPath() helper to validate filePath/path parameters before dispatching to runMessageAction(), closing a sandbox escape vector where a sandboxed agent could reference arbitrary host paths. createOpenClawTools() now forwards sandboxRoot into createMessageTool(), and new Vitest cases exercise absolute-path and traversal rejection, as well as the “no sandboxRoot” passthrough.

The approach aligns with other sandbox-aware tools (e.g. image-tool.ts and the read/write/edit tools) by enforcing a single sandbox boundary at the tool surface before any plugin/channel code sees the path.

Confidence Score: 4/5

  • This PR is likely safe to merge and meaningfully improves sandbox safety, with a couple of edge cases to confirm.
  • The change is localized and reuses the existing assertSandboxPath() sandbox boundary check, and tests cover key escape inputs. Remaining uncertainty is around how path is interpreted across different message actions/channels and whether an untrimmed sandboxRoot could lead to unexpected behavior in misconfigured callers.
  • src/agents/tools/message-tool.ts

@openclaw-barnacle openclaw-barnacle bot added the agents Agent runtime and tooling label Feb 1, 2026
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +367 to +374
// Validate file paths against sandbox root to prevent host file access.
const sandboxRoot = options?.sandboxRoot;
if (sandboxRoot) {
for (const key of ["filePath", "path"] as const) {
const raw = readStringParam(params, key, { trim: false });
if (raw) {
await assertSandboxPath({ filePath: raw, cwd: sandboxRoot, root: sandboxRoot });
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P0] sandboxRoot is used without trimming/normalizing, unlike image-tool.ts, so a value like ' ' will enable the sandbox branch but effectively set cwd/root to a non-sandbox path (e.g. current working directory), which can cause false rejections or unintended behavior depending on how assertSandboxPath resolves. Consider const sandboxRoot = options?.sandboxRoot?.trim(); and only validating when it's non-empty.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/tools/message-tool.ts
Line: 367:374

Comment:
[P0] `sandboxRoot` is used without trimming/normalizing, unlike `image-tool.ts`, so a value like `'   '` will enable the sandbox branch but effectively set `cwd/root` to a non-sandbox path (e.g. current working directory), which can cause false rejections or unintended behavior depending on how `assertSandboxPath` resolves. Consider `const sandboxRoot = options?.sandboxRoot?.trim();` and only validating when it's non-empty.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +367 to +375
// Validate file paths against sandbox root to prevent host file access.
const sandboxRoot = options?.sandboxRoot;
if (sandboxRoot) {
for (const key of ["filePath", "path"] as const) {
const raw = readStringParam(params, key, { trim: false });
if (raw) {
await assertSandboxPath({ filePath: raw, cwd: sandboxRoot, root: sandboxRoot });
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Validating both filePath and path unconditionally for all actions may break non-file uses of path (e.g. channels/actions that interpret path as something other than a local filesystem path). If path is only meant for attachments on send, it’s safer to scope this validation to action === "send" (and/or only when media/attachments are being used).

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/tools/message-tool.ts
Line: 367:375

Comment:
[P1] Validating both `filePath` and `path` unconditionally for all actions may break non-file uses of `path` (e.g. channels/actions that interpret `path` as something other than a local filesystem path). If `path` is only meant for attachments on `send`, it’s safer to scope this validation to `action === "send"` (and/or only when `media`/attachments are being used).

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +211 to +237
it("allows filePath inside sandbox root", async () => {
mocks.runMessageAction.mockClear();
mocks.runMessageAction.mockResolvedValue({
kind: "send",
action: "send",
channel: "telegram",
to: "telegram:123",
handledBy: "plugin",
payload: {},
dryRun: true,
} satisfies MessageActionRunResult);

const sandboxDir = await fs.mkdtemp(path.join(os.tmpdir(), "msg-sandbox-"));
try {
const tool = createMessageTool({
config: {} as never,
sandboxRoot: sandboxDir,
});

await tool.execute("1", {
action: "send",
target: "telegram:123",
filePath: "./data/file.txt",
message: "",
});

expect(mocks.runMessageAction).toHaveBeenCalledTimes(1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] allows filePath inside sandbox root only asserts that runMessageAction was called, but doesn’t verify the validated path behavior. This can pass even if the validation is accidentally skipped. Consider asserting the call still receives the original relative filePath and (optionally) that an escaping filePath rejects in the same test suite.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/tools/message-tool.test.ts
Line: 211:237

Comment:
[P2] `allows filePath inside sandbox root` only asserts that `runMessageAction` was called, but doesn’t verify the validated path behavior. This can pass even if the validation is accidentally skipped. Consider asserting the call still receives the original relative `filePath` and (optionally) that an escaping `filePath` rejects in the same test suite.

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

@steipete steipete self-assigned this Feb 1, 2026
@steipete steipete merged commit 9b6fffd into openclaw:main Feb 1, 2026
39 of 48 checks passed
@steipete
Copy link
Contributor

steipete commented Feb 1, 2026

Landed via temp rebase onto main (squash merge).

  • Gate: pnpm lint (fails: no-redundant-type-constituents in extensions/*), pnpm build (fails: DefaultResourceLoader type errors), pnpm test (fails: DefaultResourceLoader is not a constructor in pi-embedded-runner tests)
  • Land commit: ${land_sha}
  • Merge commit: ${merge_sha}

Thanks @leszekszpunar!

bennewton999 pushed a commit to bennewton999/openclaw that referenced this pull request Feb 2, 2026
…penclaw#6398)

* security(message-tool): validate filePath/path against sandbox root

* style: translate Polish comments to English for consistency
buiilding pushed a commit to buiilding/openclaw that referenced this pull request Feb 2, 2026
…penclaw#6398)

* security(message-tool): validate filePath/path against sandbox root

* style: translate Polish comments to English for consistency
claudio-neo pushed a commit to claudio-neo/openclaw that referenced this pull request Feb 3, 2026
…penclaw#6398)

* security(message-tool): validate filePath/path against sandbox root

* style: translate Polish comments to English for consistency
HashWarlock pushed a commit to HashWarlock/openclaw that referenced this pull request Feb 4, 2026
…penclaw#6398)

* security(message-tool): validate filePath/path against sandbox root

* style: translate Polish comments to English for consistency
uxcu pushed a commit to uxcu/kook-openclaw that referenced this pull request Feb 5, 2026
…penclaw#6398)

* security(message-tool): validate filePath/path against sandbox root

* style: translate Polish comments to English for consistency
bestNiu pushed a commit to bestNiu/clawdbot that referenced this pull request Feb 5, 2026
…penclaw#6398)

* security(message-tool): validate filePath/path against sandbox root

* style: translate Polish comments to English for consistency
batao9 pushed a commit to batao9/openclaw that referenced this pull request Feb 7, 2026
…penclaw#6398)

* security(message-tool): validate filePath/path against sandbox root

* style: translate Polish comments to English for consistency
hughdidit pushed a commit to hughdidit/DAISy-Agency that referenced this pull request Feb 8, 2026
…penclaw#6398)

* security(message-tool): validate filePath/path against sandbox root

* style: translate Polish comments to English for consistency

(cherry picked from commit 9b6fffd)

# Conflicts:
#	src/agents/tools/message-tool.ts
battman21 pushed a commit to battman21/openclaw that referenced this pull request Feb 12, 2026
…penclaw#6398)

* security(message-tool): validate filePath/path against sandbox root

* style: translate Polish comments to English for consistency
battman21 pushed a commit to battman21/openclaw that referenced this pull request Feb 12, 2026
…penclaw#6398)

* security(message-tool): validate filePath/path against sandbox root

* style: translate Polish comments to English for consistency
jamie-dit pushed a commit to jamie-dit/zulip-claw that referenced this pull request Feb 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Security] message tool filePath bypasses sandbox - arbitrary file read

2 participants