Skip to content

fix(sandbox): allow intra-sandbox symlinks in safeTarExtract audit (#2268)#2308

Merged
ericksoa merged 1 commit into
mainfrom
fix/2268-safeTarExtract-sandbox-symlinks
Apr 23, 2026
Merged

fix(sandbox): allow intra-sandbox symlinks in safeTarExtract audit (#2268)#2308
ericksoa merged 1 commit into
mainfrom
fix/2268-safeTarExtract-sandbox-symlinks

Conversation

@cjagwani

@cjagwani cjagwani commented Apr 22, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes #2268 — P1 regression in v0.0.22.

`safeTarExtract`'s post-extraction symlink audit (added in #2163 to block tar path-traversal attacks) flagged every symlink whose target resolved outside the extraction temp directory. The sandbox base image legitimately ships intra-sandbox symlinks like `/sandbox/.openclaw → /sandbox/.openclaw-data` — those resolve outside the host temp dir but inside the canonical `/sandbox` root, where they'll be correctly resolved when the backup is restored into a fresh sandbox.

Result: the audit nuked every backup, breaking `nemoclaw rebuild` and `nemoclaw snapshot create` on v0.0.22. The only workaround was `destroy + onboard` (loses all state).

Fix

  • `auditExtractedSymlinks(dirPath, allowedRoots: string[])` — now takes an array of allowed roots instead of a single one
  • `safeTarExtract` passes `[targetDir, "/sandbox"]` so intra-sandbox absolute symlinks are honored while genuine escape attempts (e.g. symlink to `/etc/passwd` or `../../.ssh/authorized_keys`) still abort the extraction

The security guardrail is intact: `/sandbox` is a subtree root, so crafted symlinks with absolute targets outside `/sandbox` (e.g. `/etc/passwd`) are still rejected.

Test plan

  • New test: `allows symlinks whose target resolves within /sandbox (intra-sandbox layout)` — locks the regression fix
  • New test: `blocks symlinks that escape /sandbox even with an absolute target` — confirms `/etc/passwd` symlinks still rejected
  • Existing `blocks symlink escaping target directory` (relative `../../`) still passes
  • All 21 sandbox-tar security tests pass
  • Full CLI suite: 1783 passed

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Security & Bug Fixes
    • Improved symlink validation during tar archive extraction to correctly allow absolute symlink targets that resolve within the sandbox environment.
    • Enhanced security checks now reject symlinks with targets outside permitted directories.

…2268)

Closes #2268 (P1 regression in v0.0.22).

PR #2163's post-extraction symlink audit treated every target outside
the extraction temp dir as an escape. The sandbox base image legitimately
places intra-sandbox symlinks (e.g. /sandbox/.openclaw →
/sandbox/.openclaw-data) — those point outside the host extraction dir
but INSIDE the canonical /sandbox root, where they'll be correctly
resolved on restore. The audit flagged them as escapes, nuked the
extraction, and broke every rebuild + snapshot create on v0.0.22.

Fix: auditExtractedSymlinks now accepts an array of allowed roots.
safeTarExtract passes [targetDir, "/sandbox"] so intra-sandbox layout
is honored while genuine escapes (symlinks to /etc/passwd, ../../.ssh,
etc.) still abort. Two new tests lock in both behaviours.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Apr 22, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 02ce4a80-c2c2-4e56-9ac3-b6bf74549f0c

📥 Commits

Reviewing files that changed from the base of the PR and between 752bfb3 and e54d96c.

📒 Files selected for processing (2)
  • src/lib/sandbox-state.ts
  • test/security-sandbox-tar-traversal.test.ts

📝 Walkthrough

Walkthrough

The symlink validation logic in sandbox extraction is enhanced to accept multiple allowed root paths instead of a single root. The safeTarExtract function now permits symlink targets that resolve to either the target extraction directory or the /sandbox root, rather than exclusively the target directory.

Changes

Cohort / File(s) Summary
Symlink Validation Enhancement
src/lib/sandbox-state.ts
Modified auditExtractedSymlinks to accept allowedRoots array; validation now passes when resolved symlink targets exist within any of the allowed roots. Updated Phase 3 post-extraction audit in safeTarExtract to permit targets in both targetDir and /sandbox root.
Symlink Validation Tests
test/security-sandbox-tar-traversal.test.ts
Added two test cases: one verifying absolute symlinks resolving within /sandbox are accepted, another confirming absolute symlinks resolving outside /sandbox (e.g., /etc/passwd) are rejected with symlink-related error message.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~15 minutes

Poem

🐰 Symlinks dance through /sandbox paths,
No longer bound by single roots,
Multiple homes now welcome them,
While dangerous escapes are barred,
Security and flexibility bloom! 🌿

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title accurately summarizes the main change: allowing intra-sandbox symlinks in the safeTarExtract audit by modifying symlink validation logic.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/2268-safeTarExtract-sandbox-symlinks

Comment @coderabbitai help to get the list of available commands and usage tips.

@ericksoa ericksoa left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM. The allowedRoots array approach is the right fix — it preserves the security guardrail for genuine escape attempts while correctly allowing intra-sandbox absolute symlinks that are legitimate in the restored sandbox filesystem. The two new tests properly cover both the regression (intra-sandbox symlinks pass) and the security invariant (escape to /etc/passwd still blocked). All 20 CI checks green.

@ericksoa ericksoa merged commit 4e4efcb into main Apr 23, 2026
39 of 41 checks passed
ericksoa added a commit that referenced this pull request Apr 23, 2026
## Summary

Fixes four nightly E2E failures that have been masking each other since
April 21–22. Supersedes #2350 (which added the diagnostics that helped
identify these).

### snapshot-commands-e2e
- **Root cause:** PR #2184 changed the CLI success output from `Snapshot
created` to `Snapshot v1 created` (version string inserted), but the
test's `grep -q "Snapshot created"` assertion was never updated
- **Previous masking:** This was hidden behind the `safeTarExtract`
symlink regression (#2268, fixed by #2308) — the snapshot never got far
enough to print the success message. Now that #2308 landed, the snapshot
succeeds but the grep fails
- **Fix:** Change grep to `"Snapshot.*created"` to match the versioned
output format
- Also includes the `run_capture` helper and Phase 2b diagnostics from
#2350 so future failures are visible

### deployment-services-e2e
- **Root cause:** `SANDBOX_NAME` and `LOG_FILE` are never defined — the
script uses `set -euo pipefail` (`-u` = nounset) and crashes immediately
at line 70
- **Fix:** Add variable definitions matching the pattern used by other
E2E tests; pass `NEMOCLAW_SANDBOX_NAME` and `NEMOCLAW_RECREATE_SANDBOX`
in the workflow

### network-policy-e2e
- **Root cause:** `setup_sandbox()` destroys the sandbox, then calls
`nemoclaw onboard` which detects the stale registry entry and fails with
"Sandbox already exists but is not ready"
- **Fix:** Add `NEMOCLAW_RECREATE_SANDBOX=1` to the onboard env so it
overwrites the stale entry

### cloud-experimental-e2e (not addressed here)
Two unrelated failures remain: Landlock regression on `/sandbox`
writability and CLI/docs command reference drift. These need separate
investigation.

## Test plan

- [ ] Trigger nightly E2E on this branch — snapshot-commands,
deployment-services, and network-policy jobs should pass
- [x] `shellcheck` clean on all three modified test scripts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Tests**
* More reliable E2E runs via consistent sandbox naming and automatic
sandbox recreation.
* Timestamped logs, a capture wrapper that preserves command output and
exit codes.
* Expanded pre-snapshot diagnostics and richer failure logs for clearer
snapshot troubleshooting.
* **Chores**
* CI updated to disable an experimental E2E job and adjust failure
notifications to avoid false triggers.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
jyaunches pushed a commit that referenced this pull request Apr 23, 2026
)

## Summary

The \`test-snapshot-commands.sh\` nightly E2E was failing despite
\`nemoclaw snapshot create\` actually succeeding. The test greps for the
literal string \`\"Snapshot created\"\` but the real success output
includes a version token between:

\`\`\`
✓ Snapshot v1 created (12 directories)
\`\`\`

Grep didn't match → false red in nightly despite the command succeeding.

Switching to regex \`\"Snapshot v[0-9]+.*created\"\` that tolerates the
version token.

## Why found now

Investigating #2268 revealed this test reporting FAIL on a run where the
operation had actually succeeded per the log. Pre-existing on main —
predates both #2308 and any recent PR.

## Test plan

- [x] Shell syntax valid (\`bash -n\`)
- [ ] Nightly E2E shows \`snapshot-commands-e2e\` green after merge

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Improved snapshot creation detection to recognize success messages
that include version tokens, making snapshot-related validation more
tolerant and tests more reliable.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Prekshi Vyas <34834085+prekshivyas@users.noreply.github.com>
@wscurran wscurran added the bug-fix PR fixes a bug or regression label Jun 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug-fix PR fixes a bug or regression

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[brev][Sandbox] nemoclaw rebuild and snapshot create fail — safeTarExtract rejects sandbox symlinks as escape violations

3 participants