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
Collaborator

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
@wscurran wscurran added NV QA Bugs found by the NVIDIA QA Team UAT Issues flagged for User Acceptance Testing. labels Jun 26, 2026
@cv
cv deleted the fix/2268-safeTarExtract-sandbox-symlinks branch June 28, 2026 00:21
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 NV QA Bugs found by the NVIDIA QA Team UAT Issues flagged for User Acceptance Testing.

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