Skip to content

fix(cli): use openshell top-level logs command#350

Closed
WuKongAI-CMU wants to merge 2 commits into
NVIDIA:mainfrom
WuKongAI-CMU:fix-253-logs-command
Closed

fix(cli): use openshell top-level logs command#350
WuKongAI-CMU wants to merge 2 commits into
NVIDIA:mainfrom
WuKongAI-CMU:fix-253-logs-command

Conversation

@WuKongAI-CMU

@WuKongAI-CMU WuKongAI-CMU commented Mar 18, 2026

Copy link
Copy Markdown
Contributor

Fixes #253

Summary

  • route nemoclaw <name> logs --follow through openshell logs instead of the nonexistent openshell sandbox logs subcommand
  • map follow mode to --tail, matching the workaround documented in the issue comments
  • add a CLI regression test that captures the dispatched OpenShell argv

Test plan

  • npm test -- test/cli.test.js
  • npm test

Summary by CodeRabbit

  • Bug Fixes

    • Logs follow behavior now uses a tail-style flag for consistent streaming.
  • Tests

    • Updated tests to assert the logs command invokes the underlying tool in tail follow mode.
    • Improved test harness to accept environment overrides, return the test home path used, and ensure temporary test home directories are cleaned up.
    • Made test PATH handling platform-appropriate by using the system path separator.

@coderabbitai

coderabbitai Bot commented Mar 18, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Changed the logs flag passed to openshell from --follow to --tail when --follow is requested; expanded the CLI test harness to accept/forward env, create and clean up a temporary HOME, and added tests verifying the --tail behavior and HOME cleanup. Also fixed PATH assembly to use path.delimiter in tests.

Changes

Cohort / File(s) Summary
CLI entry
bin/nemoclaw.js
When constructing openshell args for sandbox logs, --follow handling now passes --tail to openshell instead of --follow.
CLI tests & harness
test/cli.test.js
run(args)run(args, env = {}); runWithEnv returns { home }, only creates a temp HOME when not provided, and always cleans it up after the run. Added tests asserting my-assistant logs --follow invokes openshell logs <name> --tail and that internally-created HOME directories are removed post-run.
Test PATH fixes
test/install-preflight.test.js, test/uninstall.test.js
Replaced hardcoded ':' with path.delimiter when composing PATH for spawned processes to ensure platform-correct path separators.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped through flags and tidied HOME,
swapped a follow for a tail to roam.
Tests now build and paths behave,
I cleaned the burrow — no temp to save. 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(cli): use openshell top-level logs command' directly and clearly describes the main change—switching from the non-existent 'openshell sandbox logs' subcommand to the correct top-level 'openshell logs' command, which is the primary objective of this PR.
Linked Issues check ✅ Passed The PR fulfills all requirements from issue #253: it invokes the correct OpenShell top-level 'logs' command, maps --follow to --tail, and adds regression test coverage asserting the dispatched OpenShell argv.
Out of Scope Changes check ✅ Passed All changes are scoped to the objective: fixing the logs command dispatch in bin/nemoclaw.js, adding regression tests in test/cli.test.js, and improving test environment setup for cross-platform PATH handling in other test files.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (1)
test/cli.test.js (1)

13-24: Clean up auto-created temp HOME directories in run().

When extraEnv.HOME is not provided, fs.mkdtempSync(...) allocates a temp directory that is never removed. Consider cleaning up internally-created homes in a finally block.

♻️ Proposed refactor
 function run(args, extraEnv = {}) {
-  const home = extraEnv.HOME || fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-cli-test-"));
+  const providedHome = extraEnv.HOME;
+  const home = providedHome || fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-cli-test-"));
   try {
     const out = execSync(`node "${CLI}" ${args}`, {
       encoding: "utf-8",
       timeout: 10000,
       env: { ...process.env, ...extraEnv, HOME: home },
     });
     return { code: 0, out };
   } catch (err) {
     return { code: err.status, out: (err.stdout || "") + (err.stderr || "") };
+  } finally {
+    if (!providedHome) {
+      fs.rmSync(home, { recursive: true, force: true });
+    }
   }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/cli.test.js` around lines 13 - 24, The run() helper creates a temp HOME
when extraEnv.HOME is not provided but never removes it; update run to track
whether HOME was created locally, wrap execSync in try/finally, and in the
finally block remove the temp directory (use fs.rmSync or fs.rmdirSync with
recursive:true as appropriate) only when you created it (i.e., extraEnv.HOME was
falsy), preserving error/return behavior from the existing try/catch around
execSync and keeping variables like home and extraEnv intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@test/cli.test.js`:
- Around line 13-24: The run() helper creates a temp HOME when extraEnv.HOME is
not provided but never removes it; update run to track whether HOME was created
locally, wrap execSync in try/finally, and in the finally block remove the temp
directory (use fs.rmSync or fs.rmdirSync with recursive:true as appropriate)
only when you created it (i.e., extraEnv.HOME was falsy), preserving
error/return behavior from the existing try/catch around execSync and keeping
variables like home and extraEnv intact.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 93ab0d24-ec1c-400c-b768-cc30667e6713

📥 Commits

Reviewing files that changed from the base of the PR and between 5dbf8bb and 3a1977c.

📒 Files selected for processing (2)
  • bin/nemoclaw.js
  • test/cli.test.js

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@test/cli.test.js`:
- Around line 101-140: The test "logs dispatch uses openshell logs with tail
follow mode" creates temporary fixtures fakeBin and fakeHome but doesn't remove
them; wrap the test body (everything after creating fakeBin and fakeHome) in a
try/finally so the finally block always runs and deletes both directories (use
fs.rmSync(fakeBin, { recursive: true, force: true }) and fs.rmSync(fakeHome, {
recursive: true, force: true }) or equivalent) to ensure cleanup even if
run(...) or assertions fail; reference the variables fakeBin and fakeHome and
keep the rest of the test logic unchanged.
- Around line 101-147: The tests call assert.equal/assert.deepEqual/assert.ok
but never import Node's assert, causing a ReferenceError; fix by either
importing assert (e.g., add a top-level import/require for 'assert') or, to be
consistent with the rest of the file, replace the uses of assert in the "logs
dispatch uses openshell logs with tail follow mode" and "cleans up
internally-created HOME directories after each run" tests with Vitest expect
assertions (e.g., expect(r.code).toBe(0), expect(args).toEqual([...]),
expect(r.home).toBeTruthy(), expect(fs.existsSync(r.home)).toBe(false)); update
the calls around run(), r, and the readFileSync-derived args accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 05256fc0-b0ff-4fc2-9da2-fb22d0dc9139

📥 Commits

Reviewing files that changed from the base of the PR and between 273e829 and 35268f9.

📒 Files selected for processing (2)
  • bin/nemoclaw.js
  • test/cli.test.js
✅ Files skipped from review due to trivial changes (1)
  • bin/nemoclaw.js

Comment thread test/cli.test.js
Comment thread test/cli.test.js
@WuKongAI-CMU

Copy link
Copy Markdown
Contributor Author

Addressed the remaining review items in e534924:

  • clean up the temp CLI fixtures with try/finally
  • switch the new assertions over to Vitest expect()

Verification: npx vitest run test/cli.test.js

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (1)
test/cli.test.js (1)

129-132: Use path.delimiter for cross-platform PATH compatibility.

At line 131, PATH: \${fakeBin}:${process.env.PATH}`uses a hardcoded POSIX delimiter. Replace withpath.delimiter` to ensure the code remains compatible if Windows support is added in the future. This pattern also appears in other test files (uninstall.test.js:43, install-preflight.test.js).

Proposed patch
-      const r = run("my-assistant logs --follow", {
-        HOME: fakeHome,
-        PATH: `${fakeBin}:${process.env.PATH}`,
-      });
+      const r = run("my-assistant logs --follow", {
+        HOME: fakeHome,
+        PATH: `${fakeBin}${path.delimiter}${process.env.PATH || ""}`,
+      });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/cli.test.js` around lines 129 - 132, The test sets the PATH env var with
a hardcoded colon delimiter in the run(...) invocation (e.g., run("my-assistant
logs --follow", { HOME: fakeHome, PATH: `${fakeBin}:${process.env.PATH}` }));
replace the hardcoded ":" concatenation with the platform-aware path delimiter
via path.delimiter (import path if missing) so PATH is built as
`${fakeBin}${path.delimiter}${process.env.PATH}`; apply the same change to the
other test files following this pattern (uninstall.test.js,
install-preflight.test.js) to ensure cross-platform compatibility.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@test/cli.test.js`:
- Around line 129-132: The test sets the PATH env var with a hardcoded colon
delimiter in the run(...) invocation (e.g., run("my-assistant logs --follow", {
HOME: fakeHome, PATH: `${fakeBin}:${process.env.PATH}` })); replace the
hardcoded ":" concatenation with the platform-aware path delimiter via
path.delimiter (import path if missing) so PATH is built as
`${fakeBin}${path.delimiter}${process.env.PATH}`; apply the same change to the
other test files following this pattern (uninstall.test.js,
install-preflight.test.js) to ensure cross-platform compatibility.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2f74ba47-7e65-49d3-99a9-1c44451d96d1

📥 Commits

Reviewing files that changed from the base of the PR and between 35268f9 and e534924.

📒 Files selected for processing (1)
  • test/cli.test.js

@WuKongAI-CMU WuKongAI-CMU force-pushed the fix-253-logs-command branch from e534924 to 75b56d5 Compare March 25, 2026 17:44
@WuKongAI-CMU

Copy link
Copy Markdown
Contributor Author

Rebased this branch onto the current main by replaying the existing commit stack onto the latest base and force-pushed the refreshed branch. The patch content is unchanged in scope; this update is only to keep the PR mergeable against current main.

@WuKongAI-CMU

Copy link
Copy Markdown
Contributor Author

Rebased by replaying the current commit stack onto the latest main and force-pushed the refreshed branch. Scope is unchanged; this update is only to clear the stale/conflicting merge state.

@WuKongAI-CMU WuKongAI-CMU force-pushed the fix-253-logs-command branch from d0e268a to 85a46be Compare March 26, 2026 04:11
@WuKongAI-CMU

Copy link
Copy Markdown
Contributor Author

Replayed this branch onto the latest origin/main and preserved the intended fix: now dispatches through , while the test PATH stubs use for cross-platform safety.

@WuKongAI-CMU

Copy link
Copy Markdown
Contributor Author

Replayed this branch onto the latest origin/main and preserved the intended fix: follow mode now dispatches through openshell logs with --tail, and the test PATH stubs use path.delimiter for cross-platform safety.

@WuKongAI-CMU

Copy link
Copy Markdown
Contributor Author

The branch has already been replayed onto the latest main, and the remaining CodeRabbit nits were addressed in the current tip. Triggering a fresh review so the status reflects the current branch.

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Mar 26, 2026

Copy link
Copy Markdown
Contributor

@WuKongAI-CMU Sure! Kicking off a fresh review of the current branch tip now.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (1)
test/cli.test.js (1)

106-133: Consider extracting sandbox fixture setup into a helper to reduce repetition.

The registry JSON + fake binary setup pattern appears repeatedly in this file; a small helper would cut duplication and simplify future maintenance.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/cli.test.js` around lines 106 - 133, Multiple tests duplicate the
sandbox registry + fake openshell setup (variables fakeBin, fakeHome,
fakeOpenShell, registryDir and the sandboxes.json content for "my-assistant");
extract that logic into a small helper (e.g., createFakeSandboxRegistry or
setupFakeOpenShell) that creates the temp dirs, writes the executable openshell
script and the sandboxes.json payload (including the "my-assistant" entry and
defaultSandbox), and returns the created paths so tests call the helper and
perform cleanup; update existing tests to use this helper to remove repetition.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@test/cli.test.js`:
- Around line 106-133: Multiple tests duplicate the sandbox registry + fake
openshell setup (variables fakeBin, fakeHome, fakeOpenShell, registryDir and the
sandboxes.json content for "my-assistant"); extract that logic into a small
helper (e.g., createFakeSandboxRegistry or setupFakeOpenShell) that creates the
temp dirs, writes the executable openshell script and the sandboxes.json payload
(including the "my-assistant" entry and defaultSandbox), and returns the created
paths so tests call the helper and perform cleanup; update existing tests to use
this helper to remove repetition.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d3360541-cc5a-451f-97db-c4f2fef6bc16

📥 Commits

Reviewing files that changed from the base of the PR and between 35268f9 and 85a46be.

📒 Files selected for processing (4)
  • bin/nemoclaw.js
  • test/cli.test.js
  • test/install-preflight.test.js
  • test/uninstall.test.js
✅ Files skipped from review due to trivial changes (2)
  • test/uninstall.test.js
  • test/install-preflight.test.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • bin/nemoclaw.js

mafueee pushed a commit to mafueee/NemoClaw that referenced this pull request Mar 28, 2026
)

* fix(sandbox): prevent overread request smuggling in L7 REST parser

The HTTP/1.1 parser used a 1024-byte read buffer that could capture
bytes from a pipelined second request. Those overflow bytes were
forwarded upstream as body overflow without L7 policy evaluation,
enabling request smuggling that bypasses per-request method/path
enforcement.

Replace multi-byte read with byte-at-a-time read_u8 that stops exactly
at the CRLFCRLF header terminator. Add regression test proving two
pipelined requests are parsed independently.

Refs: NVIDIA#350

* fix(server): harden sandbox TLS secret volume permissions to 0400

Kubernetes secret volumes default to 0644, allowing the unprivileged
sandbox user to read the mTLS client private key via the Landlock
baseline /etc read set. A compromised sandbox could use the key to
impersonate the control-plane client.

Set defaultMode to 256 (octal 0400, owner-read only) on both the
default and custom pod template paths. The supervisor reads TLS
materials as root before forking, so this does not affect normal
operation.

Refs: NVIDIA#350

* fix(sandbox): stop using cmdline paths for binary policy matching

/proc/pid/cmdline is fully attacker-controlled (argv[0] can be set to
any string via execve) and had no integrity verification, unlike
exec.path and ancestors which are kernel-managed and get TOFU/SHA256
checks. The Rego policy used cmdline_paths as a grant-access signal,
allowing any sandboxed process to claim the identity of an allowed
binary and bypass network restrictions.

Remove the cmdline exact-match rule and exclude cmdline_paths from the
glob-match rule. Only exec.path and exec.ancestors (from /proc/pid/exe)
are now used for binary identity. cmdline_paths remain in the OPA input
for deny-reason diagnostics only.

Refs: NVIDIA#350

* fix(sandbox): reject symlink and non-dir read_write paths before chown

The supervisor runs as root and calls chown on each read_write path.
Since chown follows symlinks, a malicious container image could place a
symlink (e.g. /sandbox -> /etc/shadow) to trick the supervisor into
transferring ownership of arbitrary files to the sandbox user.

Add symlink_metadata (lstat) check before chown to reject symlinks and
non-directory entries. The TOCTOU window is not exploitable because no
untrusted child process has been forked yet at this point.

Refs: NVIDIA#350

* fix(server): redact provider credentials in gRPC CRUD responses

Provider CRUD RPCs (create, get, list, update) returned full Provider
objects including plaintext credentials (API keys, secrets). Any
authenticated client -- including sandbox workloads running untrusted
code -- could read credentials for all providers.

Add redact_provider_credentials helper that clears the credentials map
before returning. Internal server paths (inference routing, sandbox env
injection) read from the store directly and are unaffected. Update
tests to verify redaction and assert persistence via direct store reads.

Refs: NVIDIA#350

* fix(sandbox): enforce non-root fallback when process user unset

drop_privileges silently returned Ok(()) when both run_as_user and
run_as_group were None, even when running as root. In local/dev mode
policies are loaded from disk without passing through the server-side
ensure_sandbox_process_identity normalization, so child processes could
retain root and all capabilities (SYS_ADMIN, NET_ADMIN).

When running as root with no process identity configured, fall back to
sandbox:sandbox instead of no-oping. Non-root runtimes are unaffected.

Refs: NVIDIA#350

* fix(sandbox): deny forward proxy for L7-configured endpoints

The forward proxy path only performed L4 (endpoint) and allowed_ips
checks. If an endpoint had L7 rules (method/path restrictions), a
sandboxed process could bypass them by using HTTP_PROXY with plain
http:// requests instead of CONNECT tunneling, since L7 inspection
only runs in the CONNECT path.

Add a guard in handle_forward_proxy that queries the endpoint's L7
config and returns 403 if any L7 rules are present, forcing traffic
through the CONNECT path where per-request inspection happens.

Refs: NVIDIA#350

* test(e2e): add regression test for forward proxy L7 bypass

Verifies that the forward proxy path (plain http:// via HTTP_PROXY)
returns 403 for endpoints with L7 rules configured, preventing
sandboxed processes from bypassing per-request method/path enforcement
by avoiding the CONNECT tunnel.

Refs: NVIDIA#350

* fix: update tests for cmdline path and TLS volume changes

Update OPA tests to verify cmdline_paths no longer grant access
(regression tests for the security fix). Fix formatting in TLS
volume test.

Refs: NVIDIA#350

* test(e2e): update provider e2e tests for credential redaction

Provider CRUD gRPC responses no longer include credential values.
Update three e2e tests to assert credentials are empty in responses.
Provider functionality is verified by existing e2e tests that check
env var injection into sandboxes (which read from the store directly).

Refs: NVIDIA#350

* chore: reformat for Rust 1.94 assert! macro style

* fix(sandbox): make drop_privileges tests root-aware for CI

CI runs as root but has no 'sandbox' user. The security fix correctly
errors when running as root with no process identity and no fallback
user available -- this is the intended behavior (refuse to run as root).
Update tests to expect that error in root-without-sandbox-user
environments instead of unconditionally asserting Ok.

Refs: NVIDIA#350

* fix(sandbox): allow non-directory read_write entries like /dev/null

The symlink guard incorrectly rejected all non-directory entries.
Character devices like /dev/null are legitimate read_write paths
used in sandbox policies. Only reject symlinks, which are the
actual attack vector for the chown privilege escalation.

Refs: NVIDIA#350
@kjw3

kjw3 commented Mar 30, 2026

Copy link
Copy Markdown
Contributor

@WuKongAI-CMU can you fix your commits so they have verified signatures. Also need to resolve conflict with test/cli.test.js

@WuKongAI-CMU

Copy link
Copy Markdown
Contributor Author

Closing — the core change this PR introduced (switching sandboxLogs to use the top-level openshell logs command) has already been incorporated into main. The current implementation uses runOpenshell(["logs", sandboxName]) with --follow support, which achieves the same goal with safer argument passing. Thanks @kjw3 for the feedback.

@wscurran wscurran added the area: cli Command line interface, flags, terminal UX, or output label Jun 3, 2026
@wscurran wscurran added bug-fix PR fixes a bug or regression and removed NemoClaw CLI labels Jun 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: cli Command line interface, flags, terminal UX, or output bug-fix PR fixes a bug or regression

Projects

None yet

Development

Successfully merging this pull request may close these issues.

unrecognized subcommand 'logs'

3 participants