Skip to content

fix(sandbox): block snapshot create while shields are up#4508

Merged
cv merged 2 commits into
NVIDIA:mainfrom
Dongni-Yang:fix/4493-snapshot-shields-up-message
May 29, 2026
Merged

fix(sandbox): block snapshot create while shields are up#4508
cv merged 2 commits into
NVIDIA:mainfrom
Dongni-Yang:fix/4493-snapshot-shields-up-message

Conversation

@Dongni-Yang
Copy link
Copy Markdown
Contributor

@Dongni-Yang Dongni-Yang commented May 29, 2026

Summary

nemoclaw <name> snapshot create while shields are up previously exited 1 with the generic wording Snapshot failed. Failed directories: ..., violating spec T5999692 which requires the error to contain shields / audit / lock so the operator knows to run shields down. This PR front-checks shields state in the create case and emits the spec's "Preferred future" wording (Cannot create snapshot while shields are up. Run \nemoclaw shields down` first, then retry.) before backupSandboxState` ever runs.

Related Issue

Fixes #4493
Fixes #4496

Changes

  • src/lib/actions/sandbox/snapshot.ts: import isShieldsDown from ../../shields; in case "create" after the gateway/liveness checks and before the "Creating snapshot…" log, exit early with the shields-aware error when shields are up.
  • test/snapshot-shields-guard.test.ts (new): two CLI-level integration tests modeled on the existing test/snapshot-gateway-guard.test.ts pattern — (1) shields up → exit 1, output contains shields are up + shields down, does NOT contain the old generic Failed directories: wording; (2) shields not configured → guard does NOT fire (no shields are up in output).

Type of Change

  • Code change (feature, bug fix, or refactor)
  • Code change with doc updates
  • Doc only (prose changes, no code sample modifications)
  • Doc only (includes code sample changes)

Verification

  • npx prek run --all-files passes
  • npm test passes
  • Tests added or updated for new or changed behavior
  • No secrets, API keys, or credentials committed
  • Docs updated for user-facing behavior changes
  • npm run docs builds without warnings (doc changes only)
  • Doc pages follow the style guide (doc changes only)
  • New doc pages include SPDX header and frontmatter (new pages only)

Verification details (what actually ran locally)

  • npx vitest run --project cli test/snapshot-shields-guard.test.ts — 2/2 pass; the locked-shields case prints exactly Cannot create snapshot while shields are up. + Run \nemoclaw alpha shields down` first, then retry.and the assertion that the oldFailed directories:` wording is absent holds.
  • npx vitest run --project cli test/snapshot.test.ts test/snapshot-gateway-guard.test.ts test/snapshot-restore-existing-dest.test.ts — 53/53 pass (regression check on adjacent snapshot suites).
  • npm run typecheck:cli — clean.
  • npx prek run --all-files — fails to bootstrap on this Ubuntu 20.04 host (Python 3.8 vs hook requirement ≥3.9; with Python 3.12 on PATH the Go toolchain download fails through the corp proxy). Same as prior PRs (fix(inference): validate Ollama /api/tags response body in health probes #4295) — relying on CI.

Signed-off-by: Dongni Yang dongniy@nvidia.com

Summary by CodeRabbit

  • Bug Fixes

    • Snapshot creation now refuses to run when a sandbox is protected by shields, displaying an instructional error telling users to disable shields first before retrying.
  • Tests

    • Added regression tests covering snapshot creation with shields up and with shields absent, ensuring correct exit behavior and user-facing messages in both scenarios.

Review Change Stack

The snapshot create path attempts SSH + tar through the state dirs that
shields-up locks down. The SSH dir-check failure path in
state/sandbox.ts:1080 returns failedDirs without an `error` field, so the
caller falls into the generic "Snapshot failed. Failed directories: ..."
branch — violating spec T5999692, which requires the error to contain
shields/audit/lock so the operator can map symptom to recovery.

Front-check shields state in the snapshot create case before
backupSandboxState ever runs and emit the spec's "Preferred future"
wording with the exact recovery command. Surgical 1-file source change;
isShieldsDown is already a public export from src/lib/shields.

Fixes NVIDIA#4493
Fixes NVIDIA#4496

Signed-off-by: Dongni Yang <dongniy@nvidia.com>
@Dongni-Yang Dongni-Yang self-assigned this May 29, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 29, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 29a3125d-9e95-4f64-8c79-eb986331e098

📥 Commits

Reviewing files that changed from the base of the PR and between c322d8d and 1b6fdf9.

📒 Files selected for processing (1)
  • test/snapshot-shields-guard.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/snapshot-shields-guard.test.ts

📝 Walkthrough

Walkthrough

Adds a shields precondition to snapshot create that refuses snapshot creation when shields are up, prints guidance to run snapshot <name> shields down, exits with code 1, and adds regression tests covering shields-up and shields-unconfigured scenarios.

Changes

Shields-aware snapshot creation gating

Layer / File(s) Summary
Shields precondition check in snapshot create
src/lib/actions/sandbox/snapshot.ts
Imports isShieldsDown and inserts a precondition guard in the snapshot create handler; if shields are not down it prints an error instructing snapshot <name> shields down and exits with code 1.
Regression test suite for shields-gated snapshot creation
test/snapshot-shields-guard.test.ts
Adds test scaffolding (CLI runner, mocked openshell/docker, sandbox registry, shields state file) and two tests: one asserting snapshot create fails with shields-specific output when shields are locked, and one asserting no shields message when shields are not configured.

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels

fix, NemoClaw CLI, Sandbox, v0.0.51

Suggested reviewers

  • ericksoa
  • cv

Poem

🐰 I sniff the code and test the ground,
If shields are up, no snap is found.
I hop and print the gentle plea,
"Run shields down" — then try again with glee. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% 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 accurately summarizes the main change: blocking snapshot creation when shields are up, which directly addresses the core issue.
Linked Issues check ✅ Passed The PR implements all core requirements: shields-aware front-check before audit, exits with actionable messaging containing 'shields' keyword, and preserves normal behavior when shields are down/unconfigured.
Out of Scope Changes check ✅ Passed All changes are directly scoped to addressing issues #4493 and #4496: the snapshot.ts change adds the shields gate, and the test file provides regression coverage with no unrelated modifications.

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

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

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
test/snapshot-shields-guard.test.ts (1)

20-43: ⚡ Quick win

Prefer execFileSync with argument array to avoid command injection risk.

While args comes from test code here, using string interpolation with execSync can introduce shell injection vulnerabilities if the pattern is copied elsewhere. The static analysis hint is correct.

🔒 Proposed fix using execFileSync
-function runCli(args: string, env: Record<string, string | undefined> = {}): CliRunResult {
+function runCli(args: string[], env: Record<string, string | undefined> = {}): CliRunResult {
   try {
-    const out = execSync(`node "${CLI}" ${args}`, {
+    const out = execFileSync("node", [CLI, ...args], {
       encoding: "utf-8",
       timeout: execTimeout(),
       env: {

Update call sites:

-    const r = runCli("alpha snapshot create", env);
+    const r = runCli(["alpha", "snapshot", "create"], env);
-    const r = runCli("alpha snapshot create", env);
+    const r = runCli(["alpha", "snapshot", "create"], env);

You'll also need to add execFileSync to the imports:

-import { execSync } from "node:child_process";
+import { execFileSync } from "node:child_process";
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/snapshot-shields-guard.test.ts` around lines 20 - 43, The runCli helper
currently calls execSync with a shell-interpolated command which can enable
injection; replace the execSync(`node "${CLI}" ${args}`, ...) call in runCli
with execFileSync and pass the executable and argument array (e.g., ['node',
CLI, ...parsedArgs]) so arguments are not shell-interpolated, add execFileSync
to the imports from child_process, preserve the same options (encoding, timeout,
env with NEMOCLAW_* overrides), and keep the existing catch logic that reads
status/stdout/stderr from the thrown error object (ensure the thrown error from
execFileSync is handled identically).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@test/snapshot-shields-guard.test.ts`:
- Around line 20-43: The runCli helper currently calls execSync with a
shell-interpolated command which can enable injection; replace the
execSync(`node "${CLI}" ${args}`, ...) call in runCli with execFileSync and pass
the executable and argument array (e.g., ['node', CLI, ...parsedArgs]) so
arguments are not shell-interpolated, add execFileSync to the imports from
child_process, preserve the same options (encoding, timeout, env with NEMOCLAW_*
overrides), and keep the existing catch logic that reads status/stdout/stderr
from the thrown error object (ensure the thrown error from execFileSync is
handled identically).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: ab8b86db-80b7-4069-a5be-fab2cbaa2748

📥 Commits

Reviewing files that changed from the base of the PR and between faa0b8e and c322d8d.

📒 Files selected for processing (2)
  • src/lib/actions/sandbox/snapshot.ts
  • test/snapshot-shields-guard.test.ts

execFileSync passes argv as an array instead of interpolating into a
shell string, so the helper no longer depends on the caller producing
shell-safe args. Caller sites pass an array instead of a space-separated
string. No behavior change to what the tests assert; both cases pass.

--no-verify used because pre-commit hook fails on pre-existing
test/generate-openclaw-config.test.ts cases that need Python >=3.11
(local host is 3.8); CI runs under correct Python.

Refs NVIDIA#4493

Signed-off-by: Dongni Yang <dongniy@nvidia.com>
@Dongni-Yang Dongni-Yang added the v0.0.55 Release target label May 29, 2026
@wscurran wscurran added fix NemoClaw CLI Use this label to identify issues with the NemoClaw command-line interface (CLI). Sandbox Use this label to identify issues related to the NemoClaw isolated environment based on OpenShell. labels May 29, 2026
@jyaunches jyaunches added R3 v0.0.56 Release target and removed v0.0.55 Release target R3 labels May 29, 2026
@cv cv merged commit 488fb3c into NVIDIA:main May 29, 2026
30 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fix NemoClaw CLI Use this label to identify issues with the NemoClaw command-line interface (CLI). Sandbox Use this label to identify issues related to the NemoClaw isolated environment based on OpenShell. v0.0.56 Release target

Projects

None yet

4 participants