Skip to content

fix(sandbox): block tmp hardlink alias escapes in media path resolution#25820

Merged
steipete merged 2 commits intoopenclaw:mainfrom
bmendonca3:bm/security-path-traversal-20260224
Feb 25, 2026
Merged

fix(sandbox): block tmp hardlink alias escapes in media path resolution#25820
steipete merged 2 commits intoopenclaw:mainfrom
bmendonca3:bm/security-path-traversal-20260224

Conversation

@bmendonca3
Copy link

@bmendonca3 bmendonca3 commented Feb 24, 2026

Summary

  • Harden sandboxed media path resolution by blocking hard-linked files when the os.tmpdir() bypass path is used.
  • Prevent aliasing an out-of-sandbox file via a tmp hard link (the prior logic only blocked symlink traversal).
  • Add a regression test that reproduces and asserts rejection of the hard-link alias case.

Change Type

  • Security fix (filesystem containment hardening)
  • Regression test

Scope

  • src/agents/sandbox-paths.ts
  • src/agents/sandbox-paths.test.ts

Security Impact

  • Boundary crossed before fix: sandbox/workspace media path containment.
  • Impact: a tmp hard link could alias an out-of-sandbox readable file and be accepted as a valid media source.
  • Worst case: secret/file exfiltration via media send flows that trust resolveSandboxedMediaSource output.

Repro + Verification

  1. Create a file outside sandbox root.
  2. Create a hard link to it under os.tmpdir().
  3. Call resolveSandboxedMediaSource({ media: <hardlinkPath>, sandboxRoot: <sandboxRoot> }).
  4. Before fix: path resolves and can be read, leaking external content.
  5. After fix: throws with a hard-link rejection error.

Automated verification:

  • pnpm exec vitest run src/agents/sandbox-paths.test.ts --maxWorkers=1

Evidence

  • Local PoC (before fix) resolved a tmp hard-link path and read TOP-SECRET from an out-of-sandbox source file.
  • Added test: rejects hardlinked tmpdir paths to outside files.

Human Verification

  1. Run the hard-link PoC locally (outside file + tmp hard link + resolveSandboxedMediaSource).
  2. Confirm rejection error references hard-linked tmp media paths.
  3. Re-run the targeted Vitest command above.

Compatibility / Migration

  • No config migration required.
  • Only tmp files with nlink > 1 are newly rejected in this path; standard tmp files continue to work.

Failure Recovery

  • If a workflow hits this guard, replace hard-link usage with a real copied file in tmp/workspace and retry.

Risks and Mitigations

  • Risk: legitimate tmp files intentionally managed as hard links may now fail.
  • Mitigation: guard is scoped to the tmp bypass path only, and regular copied files remain unaffected.

Greptile Summary

Closes a sandbox escape vector where a hard link under os.tmpdir() could alias an out-of-sandbox file, bypassing the existing symlink-only traversal check in resolveAllowedTmpMediaPath. The fix adds assertNoHardlinkedFinalPath which uses lstat().nlink > 1 to detect and reject hard-linked files in the tmp media path. A regression test reproduces the attack (create outside file, hard-link it into tmpdir, call resolveSandboxedMediaSource) and verifies rejection.

  • Adds assertNoHardlinkedFinalPath() in sandbox-paths.ts that rejects regular files with nlink > 1 in the tmp bypass path
  • Hooks the new check into resolveAllowedTmpMediaPath after the existing assertNoSymlinkEscape call
  • New test creates a real hard link and asserts rejection, with proper guards for Windows, cross-device links, and path overlap edge cases
  • No changes to the sandbox root path or workspace path resolution — only the tmp bypass path is affected

Confidence Score: 5/5

  • This PR is safe to merge — it adds a narrowly scoped security check with no risk to existing functionality.
  • The change is minimal and well-targeted: a single new guard function (assertNoHardlinkedFinalPath) hooked into one specific code path (resolveAllowedTmpMediaPath). The nlink > 1 check is the standard approach for detecting hard links on Unix. The function correctly uses lstat (not stat), handles missing files and non-regular files gracefully, and only affects the tmp bypass path. The regression test is thorough with proper edge-case handling. No existing behavior is altered for normal (non-hardlinked) tmp files.
  • No files require special attention.

Last reviewed commit: 68ca732

@openclaw-barnacle openclaw-barnacle bot added docker Docker and sandbox tooling agents Agent runtime and tooling size: S trusted-contributor labels Feb 24, 2026
@steipete steipete force-pushed the bm/security-path-traversal-20260224 branch from 68ca732 to bf7f9b3 Compare February 25, 2026 01:40
@steipete steipete merged commit 6fa7226 into openclaw:main Feb 25, 2026
26 checks passed
@steipete
Copy link
Contributor

steipete commented Feb 25, 2026

Landed via temp rebase onto main.

  • Gate: pnpm check && pnpm build && pnpm test (plus targeted rerun: pnpm exec vitest run src/gateway/server.auth.test.ts --maxWorkers=1)
  • Land commit: bf7f9b3
  • Merge commit: 6fa7226

Thanks @bmendonca3!

margulans pushed a commit to margulans/Neiron-AI-assistant that referenced this pull request Feb 25, 2026
Jackson3195 pushed a commit to Jackson3195/openclaw-with-a-personal-touch that referenced this pull request Feb 25, 2026
brianleach pushed a commit to brianleach/openclaw that referenced this pull request Feb 26, 2026
execute008 pushed a commit to execute008/openclaw that referenced this pull request Feb 27, 2026
r4jiv007 pushed a commit to r4jiv007/openclaw that referenced this pull request Feb 28, 2026
hughdidit pushed a commit to hughdidit/DAISy-Agency that referenced this pull request Mar 1, 2026
(cherry picked from commit 6fa7226)

# Conflicts:
#	CHANGELOG.md
hughdidit pushed a commit to hughdidit/DAISy-Agency that referenced this pull request Mar 3, 2026
(cherry picked from commit 6fa7226)

# Conflicts:
#	CHANGELOG.md
zooqueen pushed a commit to hanzoai/bot that referenced this pull request Mar 6, 2026
thebenjaminlee pushed a commit to escape-velocity-ventures/openclaw that referenced this pull request Mar 7, 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 docker Docker and sandbox tooling size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants