Skip to content

fix(openshell): pin host writes to sandbox root#69797

Merged
drobison00 merged 4 commits into
openclaw:mainfrom
drobison00:fix-openshell-write-path
Apr 21, 2026
Merged

fix(openshell): pin host writes to sandbox root#69797
drobison00 merged 4 commits into
openclaw:mainfrom
drobison00:fix-openshell-write-path

Conversation

@drobison00

Copy link
Copy Markdown
Contributor

fix(openshell): pin host writes to sandbox root

Summary

Describe the problem and fix in 2–5 bullets:

  • Problem: OpenShellFsBridge.writeFile validated a target path and then performed mkdir, temp-file creation, and rename through the unresolved host path, leaving a symlink-parent race that could escape the mounted root.
  • Why it matters: a sandboxed OpenShell session could create or overwrite host files outside the intended workspace mount if a parent path component was rebound during the write sequence.
  • What changed: the OpenShell write path now delegates to writeFileWithinRoot, which uses the existing root-scoped atomic write helper, and a regression test now asserts that symlink-parent writes are rejected.
  • What did NOT change (scope boundary): this change does not alter OpenShell read/remove/rename behavior, remote command handling, or any CI/workflow/configuration files.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

Root Cause (if applicable)

For bug fixes or regressions, explain why this happened, not just what changed. Otherwise write N/A. If the cause is unclear, write Unknown.

  • Root cause: the OpenShell bridge performed a check-then-use write flow, reusing an unresolved host path after async path validation instead of pinning the destination under the mount root for the actual write.
  • Missing detection / guardrail: the OpenShell bridge did not reuse the existing fs-safe pinned write helper and had no regression test covering a symlink-parent write escape.
  • Contributing context (if known): the vulnerable sequence combined assertLocalPathSafety, mkdir, temp-file creation, and rename across separate filesystem operations.

Regression Test Plan (if applicable)

For bug fixes or regressions, name the smallest reliable test coverage that should catch this. Otherwise write N/A.

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file: extensions/openshell/src/openshell-core.test.ts
  • Scenario the test should lock in: a write to alias/escape.txt must fail when alias is a symlinked parent path, and no file may be created outside the local mount root.
  • Why this is the smallest reliable guardrail: it exercises the exact OpenShellFsBridge.writeFile path with a controlled local filesystem layout and verifies both rejection and no out-of-root write.
  • Existing test that already covers this (if any): the same file already covered the normal local write-and-sync path.
  • If no new test is added, why not: N/A

User-visible / Behavior Changes

Writes through symlinked parent paths in the OpenShell sandbox backend now fail instead of creating files outside the mounted workspace root.

Diagram (if applicable)

Before:
[sandbox write to alias/file] -> [path validated lexically] -> [parent rebound to symlink] -> [host file written outside root]

After:
[sandbox write to alias/file] -> [root-scoped pinned write helper] -> [write rejected] -> [no outside file created]

Security Impact (required)

  • New permissions/capabilities? (Yes/No): No
  • Secrets/tokens handling changed? (Yes/No): No
  • New/changed network calls? (Yes/No): No
  • Command/tool execution surface changed? (Yes/No): No
  • Data access scope changed? (Yes/No): No
  • If any Yes, explain risk + mitigation: N/A

Repro + Verification

Environment

  • OS: Linux 6.8.0-110-generic x86_64
  • Runtime/container: local workspace shell
  • Model/provider: N/A
  • Integration/channel (if any): OpenShell sandbox backend
  • Relevant config (redacted): default test configuration for test/vitest/vitest.extension-misc.config.ts

Steps

  1. Create a temporary workspace root and an outside directory, then symlink a path inside the workspace to the outside directory.
  2. Call OpenShellFsBridge.writeFile with a target under the symlinked parent path.
  3. Verify the write rejects, no outside file is created, and the backend does not sync a host path to the remote workspace.

Expected

  • The write is rejected and no file is created outside the mounted root.

Actual

  • pnpm exec vitest run --config test/vitest/vitest.extension-misc.config.ts openshell/src/openshell-core.test.ts --reporter=verbose passed with 1 test file and 10 tests passing, including the new symlink-parent regression case.

Evidence

Attach at least one:

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios: the targeted openshell unit test file passed on the current upstream/main base; the existing normal write-and-sync path still passed alongside the new rejection case.
  • Edge cases checked: symlink-parent write rejection, no out-of-root file creation, and no remote sync on rejected writes.
  • What you did not verify: live OpenShell end-to-end behavior against a real sandbox runtime.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

If a bot review conversation is addressed by this PR, resolve that conversation yourself. Do not leave bot review conversation cleanup for maintainers.

Compatibility / Migration

  • Backward compatible? (Yes/No): Yes
  • Config/env changes? (Yes/No): No
  • Migration needed? (Yes/No): No
  • If yes, exact upgrade steps: N/A

Risks and Mitigations

List only real risks for this PR. Add/remove entries as needed. If none, write None.

  • Risk: OpenShell writes that rely on symlinked parent aliases now fail where they may previously have succeeded.
    • Mitigation: the new path is intentionally constrained to the mounted root, and the regression test locks in the rejection behavior for out-of-root aliases.

@openclaw-barnacle openclaw-barnacle Bot added size: S maintainer Maintainer-authored PR labels Apr 21, 2026
@greptile-apps

greptile-apps Bot commented Apr 21, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a symlink-parent write escape in OpenShellFsBridge.writeFile by replacing the check-then-use sequence (assertLocalPathSafety → manual mkdir/temp/rename) with a call to the existing writeFileWithinRoot helper, which pins all filesystem operations to the sandbox root via assertNoPathAliasEscape and a native pinned-write helper. A regression test verifies that writes through symlinked parent directories are rejected and no out-of-root file is created.

Confidence Score: 5/5

Safe to merge; the fix is minimal, correct, and backed by a focused regression test.

The only remaining finding is a P2 tracking note about mkdirp and rename still using the check-then-use pattern, which is explicitly out of scope per the PR description. No P0 or P1 issues were found.

No files require special attention; mkdirp and rename in fs-bridge.ts are worth a follow-up hardening pass but are not blocking.

Comments Outside Diff (1)

  1. extensions/openshell/src/fs-bridge.ts, line 85-101 (link)

    P2 Sibling methods retain check-then-use pattern

    mkdirp (line 89–95) and rename (lines 142–155) still follow the assertLocalPathSafetyfsPromises.mkdir / movePathWithCopyFallback sequence, which is the same TOCTOU pattern this PR fixed for writeFile. A symlinked parent could be rebound between the safety check and the actual filesystem operation in either of those paths.

    The PR description explicitly marks this as out of scope, so this is just a tracking note for a follow-up hardening pass.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: extensions/openshell/src/fs-bridge.ts
    Line: 85-101
    
    Comment:
    **Sibling methods retain check-then-use pattern**
    
    `mkdirp` (line 89–95) and `rename` (lines 142–155) still follow the `assertLocalPathSafety``fsPromises.mkdir` / `movePathWithCopyFallback` sequence, which is the same TOCTOU pattern this PR fixed for `writeFile`. A symlinked parent could be rebound between the safety check and the actual filesystem operation in either of those paths.
    
    The PR description explicitly marks this as out of scope, so this is just a tracking note for a follow-up hardening pass.
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: extensions/openshell/src/fs-bridge.ts
Line: 85-101

Comment:
**Sibling methods retain check-then-use pattern**

`mkdirp` (line 89–95) and `rename` (lines 142–155) still follow the `assertLocalPathSafety``fsPromises.mkdir` / `movePathWithCopyFallback` sequence, which is the same TOCTOU pattern this PR fixed for `writeFile`. A symlinked parent could be rebound between the safety check and the actual filesystem operation in either of those paths.

The PR description explicitly marks this as out of scope, so this is just a tracking note for a follow-up hardening pass.

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

Reviews (1): Last reviewed commit: "fix(openshell): pin host writes to sandb..." | Re-trigger Greptile

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5f78e62eb1

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread extensions/openshell/src/fs-bridge.ts Outdated
@drobison00 drobison00 force-pushed the fix-openshell-write-path branch from 5f78e62 to faff642 Compare April 21, 2026 17:51

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1d63d0aa76

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread extensions/openshell/src/fs-bridge.ts
@drobison00 drobison00 force-pushed the fix-openshell-write-path branch 2 times, most recently from 02b10cb to 2dc49e7 Compare April 21, 2026 20:47
@drobison00 drobison00 force-pushed the fix-openshell-write-path branch from 2dc49e7 to 56cb4e3 Compare April 21, 2026 20:54
@drobison00 drobison00 merged commit 7be82d4 into openclaw:main Apr 21, 2026
91 checks passed
amittell pushed a commit to amittell/openclaw that referenced this pull request Apr 23, 2026
* fix(openshell): pin host writes to sandbox root

* fix(openshell): use plugin sdk infra runtime

* fix(openshell): reject symlink write targets

* chore(changelog): note openshell sandbox write fix

(cherry picked from commit 7be82d4)
medikoo pushed a commit to medikoo/openclaw that referenced this pull request Apr 24, 2026
* fix(openshell): pin host writes to sandbox root

* fix(openshell): use plugin sdk infra runtime

* fix(openshell): reject symlink write targets

* chore(changelog): note openshell sandbox write fix
ogt-redknie pushed a commit to ogt-redknie/OPENX that referenced this pull request May 2, 2026
* fix(openshell): pin host writes to sandbox root

* fix(openshell): use plugin sdk infra runtime

* fix(openshell): reject symlink write targets

* chore(changelog): note openshell sandbox write fix
zhonghe0615 pushed a commit to zhonghe0615/openclaw that referenced this pull request May 7, 2026
* fix(openshell): pin host writes to sandbox root

* fix(openshell): use plugin sdk infra runtime

* fix(openshell): reject symlink write targets

* chore(changelog): note openshell sandbox write fix
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 9, 2026
* fix(openshell): pin host writes to sandbox root

* fix(openshell): use plugin sdk infra runtime

* fix(openshell): reject symlink write targets

* chore(changelog): note openshell sandbox write fix
globalcaos pushed a commit to globalcaos/tinkerclaw that referenced this pull request May 13, 2026
* fix(openshell): pin host writes to sandbox root

* fix(openshell): use plugin sdk infra runtime

* fix(openshell): reject symlink write targets

* chore(changelog): note openshell sandbox write fix
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 24, 2026
* fix(openshell): pin host writes to sandbox root

* fix(openshell): use plugin sdk infra runtime

* fix(openshell): reject symlink write targets

* chore(changelog): note openshell sandbox write fix
jameslcowan pushed a commit to jameslcowan/openclaw that referenced this pull request Jun 2, 2026
* fix(openshell): pin host writes to sandbox root

* fix(openshell): use plugin sdk infra runtime

* fix(openshell): reject symlink write targets

* chore(changelog): note openshell sandbox write fix
sablehead pushed a commit to sablehead/openclaw that referenced this pull request Jun 10, 2026
* fix(openshell): pin host writes to sandbox root

* fix(openshell): use plugin sdk infra runtime

* fix(openshell): reject symlink write targets

* chore(changelog): note openshell sandbox write fix
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

maintainer Maintainer-authored PR size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant