Skip to content

fix(sandbox): surface policy-denial logs breadcrumb in connect shells (#5978) - #5992

Closed
yimoj wants to merge 1 commit into
NVIDIA:mainfrom
yimoj:fix/5978-policy-denial-logs-hint
Closed

fix(sandbox): surface policy-denial logs breadcrumb in connect shells (#5978)#5992
yimoj wants to merge 1 commit into
NVIDIA:mainfrom
yimoj:fix/5978-policy-denial-logs-hint

Conversation

@yimoj

@yimoj yimoj commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

Summary

Sandbox outbound egress is denied-by-default and enforced by the OpenShell L7 proxy. From inside the sandbox, generic CLIs (curl, git, wget, python) see a policy denial only as the opaque CONNECT tunnel failed, response 403, with no pointer to the detailed allow/deny reason that lives in the NemoClaw logs. This adds a tool-agnostic breadcrumb that points the user to nemoclaw <name> logs --tail 50.

Related Issue

Fixes #5978

Changes

  • scripts/nemoclaw-start.sh: add a # nemoclaw-policy-denial-hint stanza to the /tmp/nemoclaw-proxy-env.sh block already sourced by every interactive/login sandbox shell (via /etc/bash.bashrc and /etc/profile.d/nemoclaw-proxy.sh). It prints a one-line breadcrumb naming the CONNECT tunnel failed, response 403 signature and nemoclaw <name> logs --tail 50.
    • Shown once per top-level interactive TTY session (gated on $- interactive, [ -t 2 ], SHLVL <= 1, a proxy being configured, and a once-per-shell guard so a login shell sourcing both /etc/profile and /etc/bash.bashrc does not double-print). Suppressible with NEMOCLAW_NO_POLICY_HINT=1.
    • The sandbox name is derived from OPENSHELL_SANDBOX (OpenShell ≥0.0.44), falling back to <name> on older versions.
    • It deliberately does not wrap curl/git/wget: wrapping them to scan stderr turns their stderr into a pipe, makes the tools drop progress meters and colour, and still misses python. The informational hint leaves every tool's stdout/stderr/TTY behaviour and exit code byte-for-byte unchanged, while covering generic CLIs — not just Node processes (the explicit risk called out in the issue).
  • test/repro-5978-policy-denial-hint.test.ts: executes the actual emitted stanza (extracted from the single-quoted heredoc, byte-identical to runtime) and asserts the breadcrumb content, name derivation/fallback, the suppress/no-proxy/non-interactive/subshell gates, and single-print on double-source.
  • docs/reference/troubleshooting.mdx: new entry "A request inside the sandbox fails with CONNECT tunnel failed, response 403".

nemoclaw <name> logs --tail 50 already surfaces the merged OpenShell denial line, so no logs-path change is required.

Type of Change

  • Code change with doc updates

Quality Gates

  • Tests added or updated for changed behavior
  • Docs updated for user-facing behavior changes
  • Sensitive paths changed (security, policy, credentials, preflight, onboarding, inference, runner, sandbox, or messaging)
  • Sensitive-path review completed or maintainer-approved waiver recorded — justification: additive, informational stderr output only; no policy/enforcement/credential behaviour changes; tools are not wrapped so their behaviour and exit codes are unchanged.

Verification

Real reporter-workflow E2E with the host CLI, against a sandbox built from this branch (the image bakes in the changed nemoclaw-start.sh):

node ./bin/nemoclaw.js onboard --name issue5978-hint --non-interactive --yes --no-gpu --no-sandbox-gpu --agent openclaw
node ./bin/nemoclaw.js issue5978-hint connect
node ./bin/nemoclaw.js issue5978-hint logs --tail 50

The connect shell printed the breadcrumb, a real curl was denied with CONNECT tunnel failed, response 403, and logs --tail 50 showed the denial reason (DENIED on example.com:443, not in policy). Full transcript in the PR comment.

  • PR description includes the DCO sign-off declaration and every commit appears as Verified in GitHub
  • Targeted tests pass — vitest run test/repro-5978-policy-denial-hint.test.ts (13 passing incl. the opt-in real-image container E2E; deterministic under env -u SHLVL)
  • Targeted regression tests pass: repro-4538, runtime-env-alias, slack/telegram/plugin-refresh/reconcile, nemoclaw-start, hermes-start
  • shfmt -i 2 -ci -bn clean; Biome clean; tsc -p jsconfig.json clean; source-shape budget 0; codebase-growth-guardrails pass
  • npm run docs builds without warnings
  • No secrets, API keys, or credentials committed

Signed-off-by: Yimo Jiang yimoj@nvidia.com

Summary by CodeRabbit

  • New Features
    • Added a one-time troubleshooting hint for sandbox outbound network-policy denials during connect sessions, including the CONNECT tunnel failed, response 403 signature and a nemoclaw <sandbox> logs --tail 50 command to locate the denying rule.
  • Bug Fixes
    • Hint output is sanitized for safe sandbox labels, emitted exactly once per login session, and automatically suppressed when policy guidance isn’t applicable.
  • Documentation
    • Expanded troubleshooting with the sandbox CONNECT 403 scenario and remediation via network-policy presets/custom presets.
  • Tests
    • Added regression coverage for interactive vs non-interactive/TTY behavior, suppression conditions, and correct hint content/sanitization.

@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

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: 3d95b38f-db48-40cc-b9c5-84861030d2c9

📥 Commits

Reviewing files that changed from the base of the PR and between dae14e6 and 4067864.

📒 Files selected for processing (5)
  • docs/reference/troubleshooting.mdx
  • scripts/nemoclaw-start.sh
  • src/lib/actions/sandbox/connect-policy-hint.test.ts
  • src/lib/actions/sandbox/connect.ts
  • test/repro-5978-policy-denial-hint.test.ts
✅ Files skipped from review due to trivial changes (2)
  • src/lib/actions/sandbox/connect-policy-hint.test.ts
  • docs/reference/troubleshooting.mdx
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/lib/actions/sandbox/connect.ts
  • scripts/nemoclaw-start.sh

📝 Walkthrough

Walkthrough

Adds a CONNECT 403 policy-denial breadcrumb in connect flows, plus shell startup logic, regression tests, and troubleshooting docs for diagnosis and remediation.

Changes

Policy Denial Hint Breadcrumb

Layer / File(s) Summary
Connect command hint helper
src/lib/actions/sandbox/connect.ts, src/lib/actions/sandbox/connect-policy-hint.test.ts
Adds a helper that formats the CONNECT 403 breadcrumb with nemoclaw <sandbox> logs --tail 50, applies NEMOCLAW_NO_POLICY_HINT suppression, and prints it from interactive connect output; tests assert the sandbox name and 403 signature.
Policy denial hint stanza in nemoclaw-start.sh
scripts/nemoclaw-start.sh
Adds the connect-session shell stanza that sanitizes OPENSHELL_SANDBOX, checks TTY and shell state, and prints the one-time CONNECT 403 breadcrumb to stderr.
Regression tests for hint emission and suppression
test/repro-5978-policy-denial-hint.test.ts
Adds tests that extract the shell stanza, exercise it in PTY and non-interactive shells, and verify sandbox-name handling, suppression, once-only emission, and the 403 breadcrumb text.
Troubleshooting docs for 403 policy denial
docs/reference/troubleshooting.mdx
Adds a troubleshooting subsection for sandbox policy denials, including the CONNECT tunnel failed, response 403 example, log lookup, remediation via policy-add, and hint suppression.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels

area: sandbox

Suggested reviewers

  • cjagwani
  • prekshivyas

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% 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 clearly describes the main change: adding a policy-denial breadcrumb in sandbox connect shells.
Linked Issues check ✅ Passed The PR adds the requested policy-denial breadcrumb and logs hint for sandbox connect sessions, matching #5978's core requirement.
Out of Scope Changes check ✅ Passed The docs, tests, and host-side helper are all directly tied to the breadcrumb and policy-denial UX change.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@yimoj
yimoj force-pushed the fix/5978-policy-denial-logs-hint branch from d1670d7 to 624f72a Compare June 29, 2026 11:16

@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: 3

🧹 Nitpick comments (1)
test/repro-5978-policy-denial-hint.test.ts (1)

92-96: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Prefer an observable auto-source check over matching implementation text.

This asserts the stanza contains _nemoclaw_maybe_policy_denial_hint, not that sourcing it actually emits the breadcrumb. It also becomes brittle on harmless refactors. Please drive the PTY path and assert the auto-call behavior directly. As per path instructions, “Prefer observable outcomes through the public boundary over source-text, private-shape, or mock-call assertions.”

🤖 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/repro-5978-policy-denial-hint.test.ts` around lines 92 - 96, The current
test in repro-5978-policy-denial-hint.test.ts only matches the source text for
_nemoclaw_maybe_policy_denial_hint, which is brittle and does not prove the
auto-invocation behavior. Update the test to exercise the PTY/sourcing path
through the public boundary and assert the observable breadcrumb is emitted when
the stanza is sourced, rather than checking for the private symbol name in the
stanza string.

Source: Path instructions

🤖 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.

Inline comments:
In `@scripts/nemoclaw-start.sh`:
- Around line 3051-3058: Sanitize the OPENSHELL_SANDBOX value before
_nemoclaw_policy_denial_hint_label prints it, since the current fallback path
echoes it verbatim to the terminal and can be abused with control characters or
newlines. Update the logic in _nemoclaw_policy_denial_hint_label to strip unsafe
characters before emitting the sandbox name, while preserving the existing
placeholder behavior for empty/boolean values. Add a negative-path regression
test that feeds a crafted OPENSHELL_SANDBOX value with control characters and
verifies the output is sanitized and does not leak raw escape sequences.

In `@test/repro-5978-policy-denial-hint.test.ts`:
- Around line 57-85: The shell helpers run commands via runInPty and runPlain
but their callers only assert on captured output, so a failing spawned shell
could still look like a successful silent path. Update the tests that use
runInPty and runPlain to also assert the returned status is zero before treating
empty output as a pass, using the existing status field from these helpers to
validate the public outcome of the spawned bash/script execution.
- Around line 37-46: The helper extractHintStanza currently uses a conditional
guard that the growth-guardrail job rejects, so rewrite it without an if
statement while preserving the same validation and slice behavior. Update
extractHintStanza in the policy-denial-hint test to compute or assert the marker
positions using a guardrail-safe approach, keeping the same begin/end marker
checks and error path without introducing any conditional syntax.

---

Nitpick comments:
In `@test/repro-5978-policy-denial-hint.test.ts`:
- Around line 92-96: The current test in repro-5978-policy-denial-hint.test.ts
only matches the source text for _nemoclaw_maybe_policy_denial_hint, which is
brittle and does not prove the auto-invocation behavior. Update the test to
exercise the PTY/sourcing path through the public boundary and assert the
observable breadcrumb is emitted when the stanza is sourced, rather than
checking for the private symbol name in the stanza string.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 1d816909-1911-4ffa-8fe4-916ebdd21a61

📥 Commits

Reviewing files that changed from the base of the PR and between c6113be and d1670d7.

📒 Files selected for processing (3)
  • docs/reference/troubleshooting.mdx
  • scripts/nemoclaw-start.sh
  • test/repro-5978-policy-denial-hint.test.ts

Comment thread scripts/nemoclaw-start.sh
Comment thread test/repro-5978-policy-denial-hint.test.ts
Comment thread test/repro-5978-policy-denial-hint.test.ts
@yimoj
yimoj force-pushed the fix/5978-policy-denial-logs-hint branch from 624f72a to 8f16de8 Compare June 29, 2026 11:20

@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/repro-5978-policy-denial-hint.test.ts (1)

96-100: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Exercise shell behavior here instead of matching the stanza text.

This assertion is coupled to the private helper name and only proves the source text contains a call site. Source the stanza in the same PTY setup used below and assert the breadcrumb appears without an explicit _nemoclaw_maybe_policy_denial_hint call. As per path instructions, "Prefer observable outcomes through the public boundary over source-text, private-shape, or mock-call assertions."

🤖 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/repro-5978-policy-denial-hint.test.ts` around lines 96 - 100, The
current test is asserting on the stanza source text and a private helper name,
which couples it to implementation details. Update the test in the auto-invokes
gate case to use the same PTY/shell setup as the other tests, source the stanza
through that public boundary, and assert the breadcrumb appears from observable
shell behavior without matching `_nemoclaw_maybe_policy_denial_hint` or the raw
stanza text.

Source: Path instructions

🤖 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/repro-5978-policy-denial-hint.test.ts`:
- Around line 96-100: The current test is asserting on the stanza source text
and a private helper name, which couples it to implementation details. Update
the test in the auto-invokes gate case to use the same PTY/shell setup as the
other tests, source the stanza through that public boundary, and assert the
breadcrumb appears from observable shell behavior without matching
`_nemoclaw_maybe_policy_denial_hint` or the raw stanza text.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: e89f90a1-f10d-4922-a185-532121c2be60

📥 Commits

Reviewing files that changed from the base of the PR and between d1670d7 and 624f72a.

📒 Files selected for processing (3)
  • docs/reference/troubleshooting.mdx
  • scripts/nemoclaw-start.sh
  • test/repro-5978-policy-denial-hint.test.ts
✅ Files skipped from review due to trivial changes (1)
  • docs/reference/troubleshooting.mdx
🚧 Files skipped from review as they are similar to previous changes (1)
  • scripts/nemoclaw-start.sh

@yimoj

yimoj commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator Author

Reporter-workflow E2E evidence (real sandbox base image)

This fix's surface is the in-sandbox connect shell, so there is no host nemoclaw <name> CLI command in its code path — it is shell init emitted into /tmp/nemoclaw-proxy-env.sh and sourced by the image's own /etc/profile.d/nemoclaw-proxy.sh and /etc/bash.bashrc. I exercised the exact reporter workflow end-to-end against the real ghcr.io/nvidia/nemoclaw/sandbox-base:latest image, with a 403-on-CONNECT proxy reproducing the OpenShell L7 denial:

--- image hooks present? ---
/etc/profile.d/nemoclaw-proxy.sh
/etc/bash.bashrc
=== REPORTER WORKFLOW: interactive login connect shell (real image hooks) ===
  Note: this sandbox restricts outbound network access by policy.
  Blocked requests fail with 'CONNECT tunnel failed, response 403'.
  See which rule denied a request:  nemoclaw qa-5978 logs --tail 50
[user] $ curl -sS https://example.com/
curl: (56) CONNECT tunnel failed, response 403
[user] $ git clone https://example.com/foo/bar
Cloning into 'bar'...

The breadcrumb is emitted once at connect-shell init by the real image hooks (dedup holds even though both /etc/profile.d and /etc/bash.bashrc source the file), and a real curl/git surfaces the exact CONNECT tunnel failed, response 403 signature the breadcrumb names.

This is codified as an opt-in container E2E (it.skipIf — skipped in normal CI, mirroring the docker E2E gating in test/repro-4538-raw-doctor-perms.test.ts):

NEMOCLAW_RUN_POLICY_HINT_DOCKER_E2E=1 vitest run \
  test/repro-5978-policy-denial-hint.test.ts --project integration
# → 11 passed (includes "real base image: connect shell shows the breadcrumb once and curl is denied with 403")

A full nemoclaw onboard/connect against a live provisioned sandbox additionally requires provider credentials/GPU and exercises the same image hooks; the container E2E above isolates and proves the connect-shell surface this PR changes.

@yimoj
yimoj force-pushed the fix/5978-policy-denial-logs-hint branch 2 times, most recently from 978ff1c to 2f768ab Compare June 29, 2026 11:37
@yimoj

yimoj commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator Author

Real host-CLI E2E — reporter workflow against a worktree-built sandbox

Stood up an isolated sandbox built from this branch (so the image bakes in the changed scripts/nemoclaw-start.sh) and drove the exact reporter workflow with the real nemoclaw CLI:

$ NEMOCLAW_PROVIDER=ollama NEMOCLAW_MODEL=qwen2.5:0.5b NEMOCLAW_YES=1 \
    node ./bin/nemoclaw.js onboard --name issue5978-hint --non-interactive --yes \
    --no-gpu --no-sandbox-gpu --no-ollama-autostart --agent openclaw
  ... ✓ Sandbox 'issue5978-hint' created   (policies: npm, pypi, huggingface, brew, weather, local-inference, openclaw-pricing)

$ node ./bin/nemoclaw.js issue5978-hint connect
  ✓ Connecting to sandbox 'issue5978-hint'
  Inside the sandbox, run `openclaw tui` to start chatting with the agent.
  Type `/exit` to leave the chat, then `exit` to return to the host shell.
  Note: this sandbox restricts outbound network access by policy.
  Blocked requests fail with 'CONNECT tunnel failed, response 403'.
  See which rule denied a request:  nemoclaw <name> logs --tail 50
$ curl -sS https://example.com/
curl: (56) CONNECT tunnel failed, response 403

$ node ./bin/nemoclaw.js issue5978-hint logs --tail 50
[sandbox] [OCSF] NET:OPEN [MED] DENIED /usr/bin/curl(1614) -> example.com:443 \
  [policy:- engine:opa] [reason:endpoint example.com:443 not in policy 'brew'; ...]

This proves the full acceptance criteria end-to-end through the host CLI: the breadcrumb appears in the nemoclaw connect shell, a real curl is denied with the 403 signature, and nemoclaw <name> logs --tail 50 shows the denial reason (DENIED ... example.com:443 ... not in policy).

Note on the <name> placeholder: on the OpenShell build used here, OPENSHELL_SANDBOX=1 (not the sandbox name) and the container hostname is the container ID, so the label uses the documented <name> fallback (resolve via nemoclaw list). On OpenShell ≥0.0.44, which sets OPENSHELL_SANDBOX to the sandbox name, the breadcrumb prints the real name. The placeholder matches the CLI's existing <name>/<sandbox> help-text convention.

@wscurran wscurran added area: cli Command line interface, flags, terminal UX, or output area: observability Logging, metrics, tracing, diagnostics, or debug output area: policy Network policy, egress rules, presets, or sandbox policy bug-fix PR fixes a bug or regression labels Jun 29, 2026
@wscurran

Copy link
Copy Markdown
Contributor

@yimoj

yimoj commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator Author

Follow-up (head fcc75cce7): based on the real-CLI E2E finding that the deployed OpenShell sets OPENSHELL_SANDBOX=1 (so the in-sandbox breadcrumb falls back to the <name> placeholder), I added a host-side companion in src/lib/actions/sandbox/connect.ts. The nemoclaw <name> connect one-shot hint now also prints the breadcrumb with the real sandbox name via the unit-tested buildPolicyDenialConnectHint() helper, so the reporter's exact flow gets a directly runnable nemoclaw <name> logs --tail 50. Same NEMOCLAW_NO_POLICY_HINT=1 suppression. New connect-policy-hint.test.ts covers it.

@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/repro-5978-policy-denial-hint.test.ts (1)

121-162: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Avoid testing the shell-private helpers directly.

These cases call _nemoclaw_policy_denial_hint_label / _nemoclaw_policy_denial_hint_text instead of exercising the emitted connect-shell behavior, so they lock the suite to internal helper names and can miss regressions in the real breadcrumb path. Prefer driving the same scenarios through _nemoclaw_maybe_policy_denial_hint (or source-time sourcing) and asserting the user-visible breadcrumb text/fallback name. As per path instructions, “Prefer observable outcomes through the public boundary over source-text, private-shape, or mock-call assertions.”

🤖 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/repro-5978-policy-denial-hint.test.ts` around lines 121 - 162, The tests
are targeting shell-private helpers directly, which ties them to internal names
and bypasses the real breadcrumb path. Update the cases around
_nemoclaw_policy_denial_hint_label and _nemoclaw_policy_denial_hint_text to
exercise the emitted behavior through _nemoclaw_maybe_policy_denial_hint or
equivalent source-time sourcing, and assert only the user-visible breadcrumb
text, fallback name, and sanitization outcomes. Keep the same scenarios, but
move the assertions to the public boundary so the suite covers the actual
connect-shell flow.

Source: Path instructions

🤖 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/repro-5978-policy-denial-hint.test.ts`:
- Around line 121-162: The tests are targeting shell-private helpers directly,
which ties them to internal names and bypasses the real breadcrumb path. Update
the cases around _nemoclaw_policy_denial_hint_label and
_nemoclaw_policy_denial_hint_text to exercise the emitted behavior through
_nemoclaw_maybe_policy_denial_hint or equivalent source-time sourcing, and
assert only the user-visible breadcrumb text, fallback name, and sanitization
outcomes. Keep the same scenarios, but move the assertions to the public
boundary so the suite covers the actual connect-shell flow.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: baeb933a-7a9b-4d21-8bc6-fae59d1ace05

📥 Commits

Reviewing files that changed from the base of the PR and between 624f72a and fcc75cc.

📒 Files selected for processing (5)
  • docs/reference/troubleshooting.mdx
  • scripts/nemoclaw-start.sh
  • src/lib/actions/sandbox/connect-policy-hint.test.ts
  • src/lib/actions/sandbox/connect.ts
  • test/repro-5978-policy-denial-hint.test.ts
✅ Files skipped from review due to trivial changes (2)
  • src/lib/actions/sandbox/connect-policy-hint.test.ts
  • docs/reference/troubleshooting.mdx
🚧 Files skipped from review as they are similar to previous changes (1)
  • scripts/nemoclaw-start.sh

@yimoj

yimoj commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed the CodeRabbit nitpick: the breadcrumb tests no longer call the shell-private _nemoclaw_policy_denial_hint_label/_text helpers. All name-derivation, <name> fallback, control-char sanitization, and 403/logs --tail 50 cases now drive the public gate _nemoclaw_maybe_policy_denial_hint under a PTY and assert the user-visible breadcrumb (observable outcome through the boundary). 12 passing incl. the opt-in real-image docker E2E.

@yimoj
yimoj force-pushed the fix/5978-policy-denial-logs-hint branch from fcc75cc to c0577f7 Compare June 29, 2026 18:26

@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: 1

🤖 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.

Inline comments:
In `@test/repro-5978-policy-denial-hint.test.ts`:
- Around line 143-149: The control-character sanitization test only verifies
that ESC is removed, but it still misses the newline-injection path. Update the
existing `gate(...)` assertion in `repro-5978-policy-denial-hint.test.ts` to
also confirm the crafted `OPENSHELL_SANDBOX` value cannot emit a separate
`INJECTED` line through stdout, alongside the current `stdout` checks in the
`it("strips control characters...")` case. Keep the verification at the public
boundary by asserting the observable terminal output, not internal state.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: a92c6b75-0adb-45c0-ae0a-db5018216604

📥 Commits

Reviewing files that changed from the base of the PR and between fcc75cc and c0577f7.

📒 Files selected for processing (5)
  • docs/reference/troubleshooting.mdx
  • scripts/nemoclaw-start.sh
  • src/lib/actions/sandbox/connect-policy-hint.test.ts
  • src/lib/actions/sandbox/connect.ts
  • test/repro-5978-policy-denial-hint.test.ts
✅ Files skipped from review due to trivial changes (1)
  • docs/reference/troubleshooting.mdx
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/lib/actions/sandbox/connect-policy-hint.test.ts
  • src/lib/actions/sandbox/connect.ts
  • scripts/nemoclaw-start.sh

Comment thread test/repro-5978-policy-denial-hint.test.ts
@yimoj
yimoj force-pushed the fix/5978-policy-denial-logs-hint branch from c0577f7 to 16f8e75 Compare June 29, 2026 18:33
@yimoj

yimoj commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai resume

@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Reviews resumed.

@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: 1

🤖 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.

Inline comments:
In `@test/repro-5978-policy-denial-hint.test.ts`:
- Around line 188-203: The test in policy denial hint should model the real
login-shell behavior by re-sourcing the extracted stanza twice instead of
invoking _nemoclaw_maybe_policy_denial_hint twice directly. Update the snippet
setup in the repro test so the stanza is sourced on both passes with SHLVL=1,
then keep asserting the observable public output from stdout only once to verify
the once-per-session sentinel survives a second source.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: a6a7397d-d0b2-495c-8eba-375ad0a33e02

📥 Commits

Reviewing files that changed from the base of the PR and between c0577f7 and 16f8e75.

📒 Files selected for processing (5)
  • docs/reference/troubleshooting.mdx
  • scripts/nemoclaw-start.sh
  • src/lib/actions/sandbox/connect-policy-hint.test.ts
  • src/lib/actions/sandbox/connect.ts
  • test/repro-5978-policy-denial-hint.test.ts
✅ Files skipped from review due to trivial changes (1)
  • docs/reference/troubleshooting.mdx
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/lib/actions/sandbox/connect-policy-hint.test.ts
  • src/lib/actions/sandbox/connect.ts
  • scripts/nemoclaw-start.sh

Comment thread test/repro-5978-policy-denial-hint.test.ts
@yimoj
yimoj force-pushed the fix/5978-policy-denial-logs-hint branch from 16f8e75 to dae14e6 Compare June 29, 2026 18:46

@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: 1

🤖 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.

Inline comments:
In `@test/repro-5978-policy-denial-hint.test.ts`:
- Around line 136-141: The fallback-path tests in the gate behavior only assert
on stdout, so they can pass even if the spawned shell exits with a failure
status. Update the affected cases in the gate/repro policy denial tests to also
capture the process status from gate() and assert it is 0, matching the
surrounding behavioral checks and the public-boundary assertion style.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 591712b7-cf48-485c-a1a0-92abda6c6b51

📥 Commits

Reviewing files that changed from the base of the PR and between 16f8e75 and dae14e6.

📒 Files selected for processing (5)
  • docs/reference/troubleshooting.mdx
  • scripts/nemoclaw-start.sh
  • src/lib/actions/sandbox/connect-policy-hint.test.ts
  • src/lib/actions/sandbox/connect.ts
  • test/repro-5978-policy-denial-hint.test.ts
✅ Files skipped from review due to trivial changes (1)
  • docs/reference/troubleshooting.mdx
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/lib/actions/sandbox/connect-policy-hint.test.ts
  • src/lib/actions/sandbox/connect.ts
  • scripts/nemoclaw-start.sh

Comment thread test/repro-5978-policy-denial-hint.test.ts
Sandbox outbound egress is denied-by-default and enforced by the OpenShell
L7 proxy. From inside the sandbox, generic CLIs (curl, git, wget, python)
see a policy denial only as the opaque "CONNECT tunnel failed, response
403", with no pointer to the detailed allow/deny reason, which lives in the
NemoClaw logs.

Surface a breadcrumb on two complementary surfaces:

- In-sandbox: a `# nemoclaw-policy-denial-hint` stanza emitted into
  /tmp/nemoclaw-proxy-env.sh (sourced by every interactive/login sandbox
  shell), shown once per top-level interactive TTY session and suppressible
  with NEMOCLAW_NO_POLICY_HINT=1. It deliberately does not wrap curl/git/wget
  (wrapping pipes their stderr, dropping TTY progress/colour and missing
  python), so every tool's output and exit code are unchanged. The sandbox
  name comes from OPENSHELL_SANDBOX (sanitized of control characters), with a
  `<name>` fallback on older OpenShell that sets it to "1".

- Host-side: the `nemoclaw <name> connect` one-shot hint now also prints the
  breadcrumb with the real sandbox name, so the reporter's flow gets a
  directly runnable `nemoclaw <name> logs --tail 50` even when the in-sandbox
  value is unavailable.

Adds regression tests (the emitted stanza is executed, plus a real-image
docker E2E behind NEMOCLAW_RUN_POLICY_HINT_DOCKER_E2E=1) and a
troubleshooting doc entry.

Fixes NVIDIA#5978

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Yimo Jiang <yimoj@nvidia.com>
@yimoj

yimoj commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator Author

Closed/recreated because this PR was accidentally opened from fork head yimoj:NemoClaw. Replacement upstream-head PR: #6018 (NVIDIA:fix/5978-policy-denial-logs-hint).

@yimoj yimoj closed this Jun 30, 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 area: observability Logging, metrics, tracing, diagnostics, or debug output area: policy Network policy, egress rules, presets, or sandbox policy bug-fix PR fixes a bug or regression

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Linux][CLI&UX] Sandbox-side policy denial error has no nemoclaw <name> logs --tail breadcrumb hint

2 participants