Skip to content

[Ubuntu 24.04][Sandbox] openshell sandbox download produces a directory instead of file content + does not reject path-traversal targets #3345

Closed
NVIDIA/OpenShell
#1353

Description

@wangericnv

Description

Description

On NemoClaw v0.0.38 / OpenShell 0.0.36 on Ubuntu 24.04, `openshell sandbox download   ` exhibits two related defects:

1) Even for a known-good in-sandbox file (e.g. /sandbox/.openclaw/workspace/hello.txt that the sandbox user just wrote "trust me" to), the command exits 0 with "Download complete", but the resulting host destination /tmp/hello.txt is created as a directory, not a file — so `cat /tmp/hello.txt` fails with "Is a directory" and the file content is unreachable.

2) Path-traversal source paths that should be refused are NOT rejected: /etc/passwd and /sandbox/../etc/passwd both succeed with EXIT=0 and "Download complete". Only /var/lib/openshell/supervisor/creds.json is properly rejected.

Net effect: host-side automation that uses `openshell sandbox download` cannot reliably pull files out of the sandbox, AND the workspace-boundary check on the source path is only partially enforced — increasing the risk that scripted host pipelines silently read non-workspace files from the sandbox image.
Environment
Device:        Ubuntu workstation 2u1g-x570-1795 (10.63.136.90)
OS:            Ubuntu 24.04.4 LTS (kernel 6.17.0-19-generic)
Architecture:  x86_64
GPU:           NVIDIA RTX 6000 Ada Generation, 46068 MiB
Node.js:       v22.22.2
npm:           10.9.7
Docker:        29.4.3 (build 055a478)
OpenShell CLI: openshell 0.0.36
NemoClaw:      v0.0.38
OpenClaw:      2026.4.24 (cbcfdf6) — running inside sandbox
Sandbox:       ollama-base (Ollama / qwen2.5:7b)
Steps to Reproduce
A. Path-traversal not rejected:
  $ nemoclaw ollama-base connect
  sandbox$ mkdir -p /sandbox/.openclaw/workspace
  sandbox$ echo "trust me" > /sandbox/.openclaw/workspace/hello.txt
  sandbox$ exit
  $ rm -f /tmp/x.out
  $ openshell sandbox download ollama-base /etc/passwd /tmp/x.out
    Downloading sandbox:/etc/passwd -> /tmp/x.out
    ✓ Download complete
  $ echo $?  -> 0
  $ ls -la /tmp/x.out
    drwxrwxr-x  2 local-lynnh local-lynnh 4096 May 11 09:34 /tmp/x.out  (a DIRECTORY)
  Repeat with /sandbox/../etc/passwd — same result: EXIT=0, /tmp/x.out is a directory.

B. Known-good download produces wrong content:
  $ rm -f /tmp/hello.txt
  $ openshell sandbox download ollama-base /sandbox/.openclaw/workspace/hello.txt /tmp/hello.txt
    Downloading sandbox:/sandbox/.openclaw/workspace/hello.txt -> /tmp/hello.txt
    ✓ Download complete
  $ echo $?  -> 0
  $ cat /tmp/hello.txt
    cat: /tmp/hello.txt: Is a directory   (file content "trust me" is unreachable)
Expected Result
A. Source paths that resolve outside the sandbox workspace (e.g. /etc/passwd inside the sandbox, /sandbox/../etc/passwd, etc.) MUST be rejected with a non-zero exit and a user-facing message such as "source path is outside the sandbox workspace". The host destination file MUST NOT be created.

B. For a valid in-workspace source file, `openshell sandbox download` MUST create the destination as a regular file on the host, with byte-identical content to the source. sha256sum on both sides must match.
Actual Result
A. /etc/passwd traversal:
   $ openshell sandbox download ollama-base /etc/passwd /tmp/x.out
   Downloading sandbox:/etc/passwd -> /tmp/x.out
   ✓ Download complete
   EXIT=0
   $ ls -la /tmp/x.out
   total 12
   drwxrwxr-x  2 local-lynnh local-lynnh 4096 May 11 09:34 .
   drwxrwxrwt 26 root        root        4096 May 11 09:34 ..
   head: error reading '/tmp/x.out': Is a directory

B. Known-good hello.txt:
   $ openshell sandbox download ollama-base /sandbox/.openclaw/workspace/hello.txt /tmp/hello.txt
   Downloading sandbox:/sandbox/.openclaw/workspace/hello.txt -> /tmp/hello.txt
   ✓ Download complete
   EXIT=0
   (then) cat /tmp/hello.txt → cat: /tmp/hello.txt: Is a directory

C. Companion T560926 finding: when downloading a 1 MiB sandbox file
   /sandbox/download.txt to host /tmp/download.txt, the resulting host
   sha256 does NOT match the sandbox-side sha256. Same root cause:
   the download path is producing a directory entry rather than a regular
   file with the source bytes.

Only one of the five attempted traversal/known-good combinations behaved
correctly: /var/lib/openshell/supervisor/creds.json was rejected. The
other four produced EXIT=0 with the wrong destination shape.
Logs
Full per-step capture: /home/lab/day0-automation/20260511/report-T560929.txt
File-share companion finding: /home/lab/day0-automation/20260511/report-T560926.txt

T560929 verdict table (4 expected; 1 PASS, 3 FAIL):
  1 | Known-good /sandbox/...workspace/hello.txt download succeeds | FAIL
  2 | Refuses traversal: host /etc/passwd                          | FAIL
  3 | Refuses traversal: parent-relative traversal                 | FAIL
  4 | Refuses traversal: supervisor creds path                     | PASS

T560926 verdict table (4 expected; 3 PASS, 1 FAIL):
  1 | Upload host → sandbox via openshell                          | PASS
  2 | Sandbox sees uploaded file w/ matching sha256                | PASS
  3 | Download sandbox → host succeeds                             | PASS
  4 | Downloaded host file matches sandbox sha256                  | FAIL

The traversal-not-rejected behavior is a security/UX concern; the
directory-instead-of-file behavior is a correctness/data-integrity bug.
Both should be fixed in `openshell sandbox download`.

Bug Details

Field Value
Priority Unprioritized
Action Dev - Open - To fix
Disposition Open issue
Module Machine Learning - NemoClaw
Keyword NemoClaw, NEMOCLAW_GH_SYNC_APPROVAL, NemoClaw_Sandbox, NemoClaw_Security

[NVB#6164316]

Activity

  1. self-assigned this
    on May 12, 2026
  2. wangericnv commented on May 15, 2026

    @wangericnv
    CollaboratorAuthor

    Reopen request — still reproduces on v0.0.43 / openshell 0.0.39

    Re-verified 2026-05-15 on DGX Station GB300 (galaxy-sku2-018, host
    10.176.192.158, nvidia user) with NemoClaw v0.0.43 + openshell 0.0.39 +
    Docker 29.5.0. Both defects originally filed against v0.0.38 / openshell
    0.0.36 are still present:

    1. Known-good in-workspace download still produces a directory entry
      (not a regular file) on the host side.
    2. Path-traversal source paths /etc/passwd and /sandbox/../etc/passwd
      are still NOT rejected — they succeed with EXIT=0.

    The host destination ends up as a directory containing the actual file
    content under a same-name sub-entry, which is the exact symptom the
    original report documented.

    Environment (v0.0.43 retest)

    Device: DGX Station GB300 (galaxy-sku2-018, 10.176.192.158)
    OS: Ubuntu 24.04.4 LTS
    Architecture: aarch64
    Kernel: 6.17.0-1014-nvidia-64k
    GPU: NVIDIA GB300 + NVIDIA RTX PRO 6000 Blackwell Max-Q
    NVIDIA driver: 610.39
    Node.js: v22.22.3 (auto-installed by curl|bash)
    npm: 10.9.8
    Docker: 29.5.0 (build 98f1464)
    nvidia-ctk: 1.19.0
    OpenShell CLI: 0.0.39
    NemoClaw: v0.0.43
    Sandbox: my-assistant (vllm-local / Qwen/Qwen3.6-27B-FP8)

    Reproduction transcript — defect A (known-good download → directory)

    Inside sandbox (docker exec -u sandbox openshell-my-assistant-... bash):
    $ mkdir -p /sandbox/.openclaw/workspace
    $ echo trust me > /sandbox/.openclaw/workspace/hello.txt
    $ cat /sandbox/.openclaw/workspace/hello.txt
    trust me

    On host (Station #1 nvidia user):
    $ rm -f /tmp/hello.txt
    $ ~/.local/bin/openshell sandbox download my-assistant
    /sandbox/.openclaw/workspace/hello.txt /tmp/hello.txt
    Downloading sandbox:/sandbox/.openclaw/workspace/hello.txt -> /tmp/hello.txt
    ✓ Download complete
    EXIT=0

    $ file /tmp/hello.txt
    /tmp/hello.txt: directory

    $ find /tmp/hello.txt -maxdepth 2
    /tmp/hello.txt
    /tmp/hello.txt/hello.txt

    $ cat /tmp/hello.txt/hello.txt
    trust me

    The destination /tmp/hello.txt was created as a DIRECTORY containing a
    same-named child file /tmp/hello.txt/hello.txt that holds the real source
    content. A caller running cat /tmp/hello.txt fails with "Is a directory".

    Reproduction transcript — defect B (path-traversal not rejected)

    On host:
    $ rm -f /tmp/x1.out
    $ ~/.local/bin/openshell sandbox download my-assistant /etc/passwd /tmp/x1.out
    Downloading sandbox:/etc/passwd -> /tmp/x1.out
    ✓ Download complete
    EXIT=0

    $ file /tmp/x1.out
    /tmp/x1.out: directory

    $ ls -la /tmp/x1.out
    -rw-r--r-- 1 nvidia nvidia 962 May 12 15:45 passwd

    $ head -1 /tmp/x1.out/passwd
    root:x:0:0:root:/root:/bin/bash

    $ rm -f /tmp/x2.out
    $ ~/.local/bin/openshell sandbox download my-assistant /sandbox/../etc/passwd /tmp/x2.out
    Downloading sandbox:/sandbox/../etc/passwd -> /tmp/x2.out
    ✓ Download complete
    EXIT=0

    Both traversal sources succeeded; the resulting /tmp/x1.out and
    /tmp/x2.out are directories whose contents are the sandbox-side
    /etc/passwd file. The workspace-boundary check is still NOT enforced
    for /etc/passwd or for parent-relative traversal.

    Summary

    Defect v0.0.38 v0.0.43
    A. Known-good download → host destination is a FAIL FAIL (still
    regular file containing source bytes broken)
    B. Path-traversal source rejected (e.g. FAIL FAIL (still
    /etc/passwd, /sandbox/../etc/passwd) broken)

    Suggest moving BugAction back to "Dev - Open - To fix" and confirming
    which release the actual fix is targeted for. The change marked
    Disposition=Bug-Fixed apparently is not present in v0.0.43.

  3. reopened this on May 15, 2026
  4. laitingsheng commented on May 15, 2026

    @laitingsheng
    Contributor

    The feature is only available starting in OpenShell v0.0.41, needs to wait for the integration

  5. wangericnv commented on May 28, 2026

    @wangericnv
    CollaboratorAuthor

    Re-verified on NemoClaw v0.0.52 / OpenShell 0.0.44 (DGX Spark, 10.173.104.110, Ubuntu 24.04 aarch64) — both defects are fixed. Per @laitingsheng's note that the fix needed OpenShell v0.0.41 integration, the current bundled OpenShell (0.0.44) satisfies that requirement and the v0.0.52 NemoClaw release picks it up.

    Defect A — known-good download now produces a regular file

    $ docker exec -u sandbox <my-assistant-container> bash -c \
        'touch /sandbox/hello.txt && echo "trust me" > /sandbox/hello.txt && cat /sandbox/hello.txt'
    trust me
    
    $ rm -rf /tmp/hello-out
    $ openshell sandbox download my-assistant /sandbox/hello.txt /tmp/hello-out
    Downloading sandbox:/sandbox/hello.txt -> /tmp/hello-out
    ✓ Download complete
    
    $ file /tmp/hello-out
    /tmp/hello-out: ASCII text             ← was 'directory' on v0.0.43
    
    $ ls -la /tmp/hello-out
    -rw-r--r-- 1 nvidia nvidia 9 May 28 03:28 /tmp/hello-out
    
    $ cat /tmp/hello-out
    trust me                                ← direct read works, no Is-a-directory error
    
    $ cat /tmp/hello-out/hello.txt
    cat: /tmp/hello-out/hello.txt: Not a directory     ← confirms it's NOT the old dir-with-sub-file shape
    

    Defect B — path-traversal sources rejected with clear error

    B1 — /etc/passwd as source

    $ rm -rf /tmp/x1.out
    $ openshell sandbox download my-assistant /etc/passwd /tmp/x1.out
    Downloading sandbox:/etc/passwd -> /tmp/x1.out
    Error:   × sandbox source path '/etc/passwd' is outside the sandbox workspace (/sandbox)
    [exit=1]
    
    $ ls /tmp/x1.out
    ls: cannot access '/tmp/x1.out': No such file or directory     ← no target file created
    

    B2 — /sandbox/../etc/passwd traversal

    $ rm -rf /tmp/x2.out
    $ openshell sandbox download my-assistant /sandbox/../etc/passwd /tmp/x2.out
    Downloading sandbox:/sandbox/../etc/passwd -> /tmp/x2.out
    Error:   × sandbox source path '/sandbox/../etc/passwd' is outside the sandbox workspace (/sandbox)
    [exit=1]
    
    $ ls /tmp/x2.out
    ls: cannot access '/tmp/x2.out': No such file or directory
    

    Summary

    Defect v0.0.43 / openshell 0.0.39 v0.0.52 / openshell 0.0.44
    A. Known-good download → regular file FAIL (directory with sub-file) FIXED (regular file, direct cat works)
    B1. /etc/passwd source rejected FAIL (succeeded silently, exit 0) FIXED (explicit "outside workspace" error, exit 1)
    B2. /sandbox/../etc/passwd rejected FAIL (succeeded silently, exit 0) FIXED (explicit error, exit 1)

    Closing on my side as verified-fixed. Thanks @laitingsheng for the heads-up on the v0.0.41 integration dependency.

  6. laitingsheng commented on May 28, 2026

    @laitingsheng
    Contributor

    Closed after merging #3832, verified by the reporter

  7. added
    area: sandboxOpenShell sandbox lifecycle, runtime, config, or recovery
    and removed on Jun 3, 2026
  8. added theissue type on Jun 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

NV QABugs found by the NVIDIA QA Teamarea: sandboxOpenShell sandbox lifecycle, runtime, config, or recoveryplatform: ubuntuAffects Ubuntu Linux environments

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions