Description
nemoclaw <sandbox> rebuild, nemoclaw <sandbox> snapshot create, and nemoclaw backup-all all fail with:
SECURITY: tar extraction blocked: post-extraction symlink audit failed:
symlink escape: /<host>/.nemoclaw/rebuild-backups/<sandbox>/<timestamp>/workspace/media
-> /sandbox/.openclaw-data/media
(resolves to /sandbox/.openclaw-data/media)
Every state subsystem fails together because the audit aborts the entire extraction on the first symlink found:
Failed directories: agents, extensions, workspace, skills, hooks, identity,
devices, canvas, cron, memory, telegram, credentials
Net effect: no sandbox can be rebuilt or snapshotted in 0.0.22. The only recovery path is nemoclaw onboard --recreate-sandbox which loses state entirely.
Why it happens
PR #2163 merged 2026-04-21 (the day before the 0.0.22 tag) hardened src/lib/sandbox-state.ts against tar-slip path traversal (a real HIGH-severity class vuln where a compromised sandbox could write ../../.ssh/authorized_keys-style entries to the host). The new safeTarExtract() function calls fs.realpathSync() on every extracted symlink and rejects any target falling outside the extraction root.
That audit correctly catches the path-traversal class, but over-rejects absolute symlinks whose target exists legitimately inside the sandbox's writable dir. The workspace/media symlink is created at image build time by Dockerfile.base (pattern established in PR #555):
/sandbox/.openclaw/workspace/media -> /sandbox/.openclaw-data/media
This absolute path is valid inside the sandbox, but when the backup is extracted on the host, the target (/sandbox/.openclaw-data/media) doesn't exist on the host and certainly isn't inside the extraction root — so the audit flags it as a true escape.
Same pattern affects at least: workspace/media, exec-approvals.json, telegram/, credentials/, canvas/, cron/, memory/, skills/, flows/, hooks/, identity/, devices/, agents/, extensions/ — essentially every symlink set up by the .openclaw / .openclaw-data split.
Suggested fix
Three viable approaches (ordered by how much I'd recommend them):
Option A — Resolve symlink targets relative to the archive's extraction root
// in src/lib/sandbox-state.ts safeTarExtract post-extraction audit
const target = fs.readlinkSync(symlinkPath);
const resolvedTarget = path.isAbsolute(target)
? path.resolve(extractRoot, target.replace(/^\//, '')) // strip leading / and resolve under extractRoot
: path.resolve(path.dirname(symlinkPath), target);
if (!resolvedTarget.startsWith(extractRoot)) {
// true escape — block
}
This preserves the security guarantee (still blocks paths that would actually escape) while accepting absolute paths that legitimately point into the sandbox-internal dir structure.
Option B — Explicit allowlist of sandbox-internal prefixes
const ALLOWED_INTERNAL_PREFIXES = ['/sandbox/.openclaw-data/'];
if (path.isAbsolute(target) && ALLOWED_INTERNAL_PREFIXES.some(p => target.startsWith(p))) {
// known-safe internal symlink — allow
continue;
}
Coordinate the prefix list with the agent manifest's writableDir so it stays in sync.
Option C — Rewrite absolute symlinks to relative at backup time
Before creating the tar archive, traverse the source tree and replace any absolute symlink like workspace/media -> /sandbox/.openclaw-data/media with a relative one workspace/media -> ../media. Avoids the issue entirely and is more portable across host vs sandbox paths. Most invasive change but cleanest long-term.
Relationship to PR #2227
That PR's mutable-default refactor would eliminate the .openclaw / .openclaw-data split entirely, removing the symlinks that trip the audit. If that lands soon, Option A/B might only need to live long enough to un-brick users who can't immediately migrate to the refactored layout.
Reproduction Steps
- Fresh install NemoClaw 0.0.22 via
curl -fsSL https://www.nvidia.com/nemoclaw.sh | bash. Complete onboarding (any sandbox name, e.g. testbot).
- Run a snapshot:
NEMOCLAW_REBUILD_VERBOSE=1 nemoclaw testbot snapshot create
- Expected: a snapshot is created at
~/.nemoclaw/rebuild-backups/testbot/<timestamp>/ with the 12 state directories.
- Actual:
Creating snapshot of 'testbot'...
[sandbox-state] backupSandboxState: agent=openclaw, writableDir=/sandbox/.openclaw-data, stateDirs=[...]
[sandbox-state] Existing dirs in sandbox: [agents,extensions,workspace,skills,hooks,identity,
devices,canvas,cron,memory,telegram,credentials] (12/12)
[sandbox-state] Downloading via SSH+tar: tar -cf - -C /sandbox/.openclaw-data agents ...
[sandbox-state] SSH+tar download: exit=0, stdout=184320 bytes, stderr=
[sandbox-state] SECURITY: tar extraction blocked: post-extraction symlink audit failed:
symlink escape: .../workspace/media -> /sandbox/.openclaw-data/media
(resolves to /sandbox/.openclaw-data/media)
Snapshot failed.
Failed directories: agents, extensions, workspace, skills, hooks, identity, devices,
canvas, cron, memory, telegram, credentials
Same failure for nemoclaw testbot rebuild and nemoclaw backup-all.
Environment
Hardware: NVIDIA DGX Spark (GB10, aarch64) — but this affects every platform
OS: Ubuntu 24.04 LTS
NemoClaw CLI: v0.0.22 (symlink-audit regression introduced by PR #2163 merged 2026-04-21)
OpenClaw (bundled): 2026.4.2 (d74a122)
Cluster image: ghcr.io/nvidia/openshell/cluster:0.0.29
Install method: curl -fsSL https://www.nvidia.com/nemoclaw.sh | bash
Debug Output
List of absolute symlinks inside a fresh sandbox's `/sandbox/.openclaw/` that all point into the writable dir and will trip the audit:
$ openshell doctor exec -- kubectl exec -n openshell testbot --container agent -- \
ls -la /sandbox/.openclaw/ | awk '$1 ~ /^l/ { print }'
lrwxrwxrwx 1 root root 30 agents -> /sandbox/.openclaw-data/agents
lrwxrwxrwx 1 root root 30 canvas -> /sandbox/.openclaw-data/canvas
lrwxrwxrwx 1 root root 35 credentials -> /sandbox/.openclaw-data/credentials
lrwxrwxrwx 1 root root 28 cron -> /sandbox/.openclaw-data/cron
lrwxrwxrwx 1 root root 31 devices -> /sandbox/.openclaw-data/devices
lrwxrwxrwx 1 root root 43 exec-approvals.json -> /sandbox/.openclaw-data/exec-approvals.json
lrwxrwxrwx 1 root root 34 extensions -> /sandbox/.openclaw-data/extensions
lrwxrwxrwx 1 root root 29 flows -> /sandbox/.openclaw-data/flows
lrwxrwxrwx 1 root root 29 hooks -> /sandbox/.openclaw-data/hooks
lrwxrwxrwx 1 root root 32 identity -> /sandbox/.openclaw-data/identity
lrwxrwxrwx 1 root root 28 logs -> /sandbox/.openclaw-data/logs
lrwxrwxrwx 1 root root 29 media -> /sandbox/.openclaw-data/media
lrwxrwxrwx 1 root root 30 memory -> /sandbox/.openclaw-data/memory
lrwxrwxrwx 1 root root 31 sandbox -> /sandbox/.openclaw-data/sandbox
lrwxrwxrwx 1 root root 30 skills -> /sandbox/.openclaw-data/skills
lrwxrwxrwx 1 root root 32 telegram -> /sandbox/.openclaw-data/telegram
lrwxrwxrwx 1 root root 41 update-check.json -> /sandbox/.openclaw-data/update-check.json
lrwxrwxrwx 1 root root 33 workspace -> /sandbox/.openclaw-data/workspace
Each of these will trigger `safeTarExtract`'s audit as a false-positive escape, so the first one found aborts the whole extraction.
Logs
`NEMOCLAW_REBUILD_VERBOSE=1` shell capture above (same as "Actual" section of Reproduction Steps).
Optional: attach `nemoclaw debug --output /tmp/nemoclaw-debug.tar.gz --sandbox testbot` tarball — includes the full rebuild log and config audit trail.
Description
nemoclaw <sandbox> rebuild,nemoclaw <sandbox> snapshot create, andnemoclaw backup-allall fail with:Every state subsystem fails together because the audit aborts the entire extraction on the first symlink found:
Net effect: no sandbox can be rebuilt or snapshotted in 0.0.22. The only recovery path is
nemoclaw onboard --recreate-sandboxwhich loses state entirely.Why it happens
PR #2163 merged 2026-04-21 (the day before the 0.0.22 tag) hardened
src/lib/sandbox-state.tsagainst tar-slip path traversal (a real HIGH-severity class vuln where a compromised sandbox could write../../.ssh/authorized_keys-style entries to the host). The newsafeTarExtract()function callsfs.realpathSync()on every extracted symlink and rejects any target falling outside the extraction root.That audit correctly catches the path-traversal class, but over-rejects absolute symlinks whose target exists legitimately inside the sandbox's writable dir. The
workspace/mediasymlink is created at image build time byDockerfile.base(pattern established in PR #555):This absolute path is valid inside the sandbox, but when the backup is extracted on the host, the target (
/sandbox/.openclaw-data/media) doesn't exist on the host and certainly isn't inside the extraction root — so the audit flags it as a true escape.Same pattern affects at least:
workspace/media,exec-approvals.json,telegram/,credentials/,canvas/,cron/,memory/,skills/,flows/,hooks/,identity/,devices/,agents/,extensions/— essentially every symlink set up by the.openclaw/.openclaw-datasplit.Suggested fix
Three viable approaches (ordered by how much I'd recommend them):
Option A — Resolve symlink targets relative to the archive's extraction root
This preserves the security guarantee (still blocks paths that would actually escape) while accepting absolute paths that legitimately point into the sandbox-internal dir structure.
Option B — Explicit allowlist of sandbox-internal prefixes
Coordinate the prefix list with the agent manifest's
writableDirso it stays in sync.Option C — Rewrite absolute symlinks to relative at backup time
Before creating the tar archive, traverse the source tree and replace any absolute symlink like
workspace/media -> /sandbox/.openclaw-data/mediawith a relative oneworkspace/media -> ../media. Avoids the issue entirely and is more portable across host vs sandbox paths. Most invasive change but cleanest long-term.Relationship to PR #2227
That PR's mutable-default refactor would eliminate the
.openclaw/.openclaw-datasplit entirely, removing the symlinks that trip the audit. If that lands soon, Option A/B might only need to live long enough to un-brick users who can't immediately migrate to the refactored layout.Reproduction Steps
curl -fsSL https://www.nvidia.com/nemoclaw.sh | bash. Complete onboarding (any sandbox name, e.g.testbot).~/.nemoclaw/rebuild-backups/testbot/<timestamp>/with the 12 state directories.Same failure for
nemoclaw testbot rebuildandnemoclaw backup-all.Environment
Debug Output
Logs