Skip to content

fix(sandbox): protect DCode managed login probes - #8670

Merged
prekshivyas merged 26 commits into
mainfrom
fix/8624-dcode-probe-login-profile
Aug 13, 2026
Merged

fix(sandbox): protect DCode managed login probes#8670
prekshivyas merged 26 commits into
mainfrom
fix/8624-dcode-probe-login-profile

Conversation

@Dongni-Yang

@Dongni-Yang Dongni-Yang commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Managed Deep Agents Code images now reserve /sandbox/.bash_profile as a root-owned first-match Bash login profile. This prevents sandbox-user startup code from running before NemoClaw-managed DCode route and terminal probes, even though the current OpenShell transport still starts command-bearing sessions with bash -lc.

The image keeps /sandbox writable for normal work with a sticky root-owned directory, repairs and verifies the managed profile before dropping from root to the sandbox user, and fails closed when a sandbox-user startup cannot verify that boundary. Ordinary login commands retain the existing credential-free runtime environment, while managed probes skip it and clear BASH_ENV and ENV.

The earlier defense-in-depth work remains: DCode smoke commands avoid nested login shells, use the image-baked managed launcher, require one ordered diagnostic marker pair, and honor the structured OpenShell process status during both connect and onboarding so forged success output cannot hide a failed transport. String-only DCode smoke evidence is rejected.

Related Issue

Closes #8624

Changes

  • Install /sandbox/.bash_profile as root:root mode 0444 beneath /sandbox as root:sandbox mode 1775.
  • Start the managed DCode image as root only long enough to repair and verify that fixed profile, then immediately use setpriv to run the established entrypoint as sandbox.
  • Fail closed if the protected workspace, image-owned profile source, or installed profile is missing, symlinked, has unsafe metadata, or differs from the reviewed image source.
  • Skip sandbox-owned /tmp/nemoclaw-proxy-env.sh only for commands containing the fixed /usr/local/lib/nemoclaw/dcode-managed-exec boundary; preserve it for ordinary login commands and leave interactive .bashrc behavior unchanged.
  • Route DCode smoke commands through dcode-managed-exec and non-login sh -c, while preserving legacy login-shell behavior for other terminal agents.
  • Preserve structured OpenShell process status through DCode onboarding and reject string-only, missing, duplicate, reordered, or nonzero-transport smoke evidence.
  • Add unit, onboarding, image-contract, container-boundary, and trusted live E2E coverage for hostile startup hooks, exact marker forgery, protected metadata, overwrite refusal, ordinary-login compatibility, and managed-probe success.
  • Document the protected profile and the rebuild requirement for existing DCode sandboxes.

Root Cause and Remaining OpenShell Work

OpenShell v0.0.101 serializes a sandbox exec request into a shell command and starts both pipe and PTY command paths with /bin/bash -lc. Requested --env values are applied inside that command, after Bash has selected its login profile. Changing the requested HOME, BASH_ENV, or ENV therefore cannot prevent the first profile read.

NemoClaw can close the reported DCode path because it owns the DCode image: the first profile is now immutable to the sandbox user and deliberately excludes sandbox startup state from managed probes. This resolves #8624 for new or rebuilt managed DCode sandboxes.

OpenShell#2668 remains desirable as a platform-wide improvement: an explicit non-login/raw exec mode should preserve argv boundaries, install requested environment before process startup, retain OpenShell policy/CA/provider injection, and work consistently for both pipe and PTY paths. NemoClaw does not require that upstream change for this DCode-specific fix, and this PR makes no OpenShell changes.

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)

Quality Gates

  • Tests added or updated for changed behavior
  • Existing tests cover changed behavior — justification:
  • Tests not applicable — justification:
  • Docs updated for user-facing behavior changes
  • Docs not applicable — justification:
  • Sensitive paths changed (security, policy, credentials, preflight, onboarding, inference, runner, sandbox, or messaging)
  • Sensitive-path review completed or maintainer-approved waiver recorded — reviewer/approval link/justification: Codex Desktop reviewed the complete 19-file effective diff at 8556876a5fa431549fce06bb7d7c5abb6db6c923 against all nine repository security categories. The protected profile source and installed copy are root-owned, non-symlink, fixed-path artifacts; /sandbox uses sticky root ownership; startup repairs before immediately dropping privileges; the sandbox-user path fails closed; managed commands do not source sandbox-owned convenience state; and DCode connect plus onboarding require a structured zero transport status. Hostile fallback, BASH_ENV, exact-marker forgery, string-only capture, overwrite, transport-status, initial-start, and respawn-adjacent probe paths are covered. No credential, dependency, cryptographic, network-policy, authorization, logging, or privilege-expansion issue was found.
  • Non-success, skipped, or missing CI check accepted by maintainer — check name, approval link, and follow-up issue:

Documentation Writer Review

  • Documentation writer subagent reviewed the completed changes
  • Result: no-docs-needed
  • Evidence: Independent review: PASS — the CI staging comment and assertion identify the exact libssh2 source mismatch; DCode login-probe documentation and behavior remain unchanged.
  • Agent: Codex Desktop documentation writer

DGX Station Hardware Evidence

  • Tested on DGX Station
  • Tested commit:
  • Station profile/scenario:
  • Result:
  • Supporting evidence:

Verification

  • PR description includes a Signed-off-by: line and every new commit is signed for GitHub verification
  • Normal pre-commit, commit-message, and pre-push hooks passed
  • Focused entrypoint/profile/build-gate tests passed: 59 tests
  • DCode route/smoke CLI tests passed: 67 tests, including the structured onboarding transport-status regression
  • Focused integration tests passed: 3 tests plus the managed login-profile image contract
  • E2E support and migration tests passed on the earlier effective diff: 32 support tests plus 9 migration-review tests
  • Image contract tests passed: 20 applicable tests; one unrelated hash-lock test was excluded locally because the host pip lacks --dry-run
  • Linux container proof passed: sandbox could not replace the protected profile, hostile fallback was not run for managed exec, and ordinary login retained the runtime environment
  • CLI type checking; test-title, project-membership, test-size, source-shape; ShellCheck; shfmt; hadolint; Biome; gitleaks; and all applicable repository hooks passed
  • Documentation build passed with zero errors and two Fern warnings
  • Exact-head ordinary CI, automated review, and managed-image checks are running for 8556876a5fa431549fce06bb7d7c5abb6db6c923
  • Trusted DCode E2E at the prior effective head passed the new protected-profile, overwrite-refusal, hostile-fallback, forged-marker, and probe-only-connect assertions; its later unchanged pre-recreate backup-message assertion failed identically before this profile implementation
  • Quality Gates section completed with required justifications or waivers
  • No secrets, API keys, or credentials committed
  • npm run docs builds without errors
  • Doc pages follow the style guide
  • New doc pages include SPDX header and frontmatter (new pages only)

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

The terminal smoke runner executed every agent's smoke commands through
`sh -lc`, so a sandbox-user `.bash_profile` or `.profile` ran before the
managed command. For Deep Agents Code that startup file can emit output into
the connect probe's evidence and create persistent side effects, which is the
state the managed boundary is meant to bypass.

Deep Agents Code smoke commands now run through the image-baked
dcode-managed-exec launcher with BASH_ENV and ENV cleared and no login shell,
matching how the managed inference route probe already invokes that launcher.

Every other terminal agent keeps `sh -lc`. Their smoke commands depend on
profile-provided PATH entries, and this defect is reported only against the
Deep Agents Code managed boundary.

Refs #8624

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

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

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

Terminal smoke execution now selects shell arguments by agent. Deep Agents Code uses the managed launcher with cleared startup-environment variables and a non-login shell. Other agents retain login-shell execution. Tests cover argument construction, command invocation, and exit-marker validation.

Changes

Terminal smoke execution

Layer / File(s) Summary
Agent-specific smoke command construction and validation
src/lib/agent/terminal-smoke.ts, src/lib/agent/terminal-smoke.test.ts, test/cli/connect-terminal-agent.test.ts
buildAgentSmokeArgs routes Deep Agents Code through the managed launcher with /bin/sh -c, cleared BASH_ENV and ENV, and no login shell. Other agents continue using sandbox exec with sh -lc. Exit parsing now requires exactly one marker.
Proxy shell boundary documentation
src/lib/actions/sandbox/connect-inference-route-probe.ts, src/lib/actions/sandbox/connect-inference-route-probe.test.ts
Comments and test descriptions document OpenShell login-shell behavior, managed launcher handling, and parser protections for contaminated startup output.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main change: protecting DCode managed login probes in the sandbox execution path.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/8624-dcode-probe-login-profile

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

@github-code-quality

github-code-quality Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: TypeScript

TypeScript / code-coverage/plugin

The overall coverage in commit d701f60 in the fix/8624-dcode-probe... branch remains at 96%, unchanged from commit 24eaba1 in the main branch.

TypeScript / code-coverage/cli

The overall coverage in commit d701f60 in the fix/8624-dcode-probe... branch remains at 82%, unchanged from commit 24eaba1 in the main branch.

Show a code coverage summary of the most impacted files.
File main 24eaba1 fix/8624-dcode-probe... d701f60 +/-
src/lib/agent/t...rminal-smoke.ts 100% 96% -4%
src/lib/onboard...tp-readiness.ts 100% 98% -2%
src/lib/agent/onboard.ts 91% 91% 0%
src/lib/actions...-route-probe.ts 100% 100% 0%
src/lib/onboard...hell-version.ts 85% 85% 0%
src/lib/sandbox...rce-identity.ts 86% 86% 0%
src/lib/actions...onnect-probe.ts 69% 92% +23%

Updated August 13, 2026 07:30 UTC

@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

PR Review Advisor — No blocking findings reported

Advisor assessment: No blocking advisor findings reported
Next action: No advisor follow-up needed.
Findings: 0 blockers · 0 warnings · 0 suggestions

Model lanes

  • GPT-5.6 Terra (primary): Completed · medium confidence · 0 blockers · 0 warnings · 0 suggestions
  • Nemotron 3 Ultra (second opinion): Failed

Second-opinion terminology and E2E selections are advisory. Live E2E does not run automatically for pull requests.

2 semantic terminology decisions

Terminology decisions are advisory. They affect the assessment only when a separate finding identifies concrete semantic impact.

  • define — managed login profile at docs/manage-sandboxes/run-deep-agents-code.mdx:147: Define the term where first used with its path, owner, mode, and managed-probe behavior.
  • define — first-match profile at agents/langchain-deepagents-code/dcode-login-profile.sh:7: Define the term with the Bash profile-selection contrast when first used in user-facing documentation.

E2E guidance

Advisory only. A maintainer can dispatch the default E2E suite for the commit under review.

Recommended E2E: managed-image-protected-runtime, inference-routing

Manual-only E2E: cloud-onboard, managed-image-multiarch-startup, security-posture, hermes-e2e, network-policy, onboard-repair, onboard-resume, ubuntu-repo-cloud-langchain-deepagents-code, cloud-inference, full-e2e
The manual PR workflow does not run these selectors for the commit under review. Run them from reviewed code on main.

Workflow run details

This automated review informs maintainers. Warnings and suggestions do not require a response. A maintainer decides whether to merge.

The CLI dispatch stub for terminal agents extracted the smoke command from a
fixed argv position, so it stopped matching once the Deep Agents Code smoke
invocation gained its launcher and environment flags. The stub then produced no
smoke output and the probe reported failure.

The smoke command is always the final argument, so read it from the end. The
stub no longer depends on how many flags precede it.

Refs #8624

Signed-off-by: Dongni Yang <dongniy@nvidia.com>
@wscurran wscurran added area: sandbox OpenShell sandbox lifecycle, runtime, config, or recovery area: security Security controls, permissions, secrets, or hardening bug-fix PR fixes a bug or regression integration: dcode LangChain Deep Code integration behavior labels Aug 10, 2026
@apurvvkumaria apurvvkumaria self-assigned this Aug 10, 2026
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@apurvvkumaria apurvvkumaria changed the title fix(sandbox): run Deep Agents Code smoke commands without a login shell fix(sandbox): avoid nested login shells in Deep Agents Code smoke Aug 10, 2026
@apurvvkumaria

Copy link
Copy Markdown
Collaborator

Security review — BLOCKED\n\nThe updated wording now describes the local hardening accurately: NemoClaw removes two nested login-shell layers, while the OpenShell transport still runs a sandbox-user startup profile before the managed launcher.\n\nOne unresolved trust-boundary blocker remains:\n\n- src/lib/agent/terminal-smoke.ts accepts the first NEMOCLAW_AGENT_SMOKE_EXIT marker found anywhere in captured output.\n- NVIDIA/OpenShell#2668 means a sandbox-user startup profile runs before the requested launcher.\n- That profile can print NEMOCLAW_AGENT_SMOKE_EXIT:0 and exit successfully before the real smoke command starts. The current parser can then report a successful smoke check without evidence that the repository-defined command ran.\n- Removing NemoClaw's nested shells reduces repeated profile execution, but it does not remove this earlier output source or the profile's side effects.\n\nA local fail-closed change could stop Deep Agents Code connect from falling back to smoke when the managed route result is indeterminate. That would change compatibility for older images and other unavailable-probe cases. The alternative is to wait for an upstream non-login exec contract and validate the managed boundary against it. This choice needs an explicit architecture and compatibility decision.\n\nI did not find a secret disclosure, dependency, cryptography, or privilege-escalation defect in the changed argv construction. The focused source tests pass 44/44, the compiled-CLI dispatch test passes, and CLI build, type checking, pre-commit, and pre-push checks pass. PR #8670 must remain unmerged until the trust-evidence decision above is resolved.

@prekshivyas prekshivyas left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Blocking security finding on latest PR commit a19233fb5.

getSmokeExitCode accepts the first NEMOCLAW_AGENT_SMOKE_EXIT marker in combined OpenShell output. The transport login shell runs before the managed launcher, so sandbox-user startup output can forge an earlier :0 marker and make a later failed smoke command report success.

Bind the accepted evidence to a marker only the smoke runner can produce, or reject any pre-runner marker before parsing the runner result. Add a regression with a preamble NEMOCLAW_AGENT_SMOKE_EXIT:0 followed by the runner's nonzero marker and assert runAgentSmokeCommands returns failure.

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
@prekshivyas
prekshivyas dismissed their stale review August 11, 2026 18:36

Addressed by d5039af: duplicate/forged smoke markers are rejected; focused terminal/onboard smoke tests and the exact-head pre-push typecheck passed.

@apurvvkumaria

Copy link
Copy Markdown
Collaborator

Readiness update

The branch now includes the current main revision through an ordinary signed merge. The effective PR diff remains eight files with 185 additions and 44 deletions; this is not a large line-count increase.

Validation on the refreshed branch:

  • focused terminal-smoke and route-probe tests: 56 passed
  • compiled CLI dispatch test: 1 passed
  • shared gateway regression suites, run in isolation: 51 passed, 1 skipped
  • CLI and plugin builds: passed
  • CLI type checking: passed
  • applicable repository hooks: passed
  • GitHub commit verification: every commit is Verified
  • documentation review: no-docs-needed; published CLI behavior is unchanged
  • nine-category security review: passed; Deep Agents Code uses the managed launcher, clears startup environment variables, and requires one ordered begin/exit evidence pair. Missing, duplicated, reordered, or forged evidence fails closed. No credential, authorization, cryptographic, dependency, or privilege-expansion issue was found.

The prior gateway-shard failures were inherited from the older base and are covered by the current main test-infrastructure fix. Fresh GitHub checks are running. Human review remains outstanding; no reviewer routing was changed.

cv
cv previously requested changes Aug 12, 2026

@cv cv left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

src/lib/agent/terminal-smoke.ts:26-34 treats one static NEMOCLAW_AGENT_SMOKE_BEGIN followed by one NEMOCLAW_AGENT_SMOKE_EXIT:0 as managed-runner evidence, while lines 65-70 rely on openshell sandbox exec --env HOME=... to prevent the transport login shell from reading /sandbox/.bash_profile. OpenShell v0.0.101, which this commit pins, serializes request environment entries as assignments inside the command passed to /bin/bash -lc; Bash reads its login profile before those assignments. A sandbox-user profile can therefore print exactly the accepted marker pair and exit before dcode-managed-exec; NemoClaw then reports smoke success although no smoke command ran. test/e2e/e2e-cloud-experimental/checks/04-deepagents-code-fresh-reonboard.sh:206-230 does not cover this regression: its profile forges only the legacy EXIT marker, and its inspection and cleanup commands repeat the ineffective HOME override. Keep this marker diagnostic-only until OpenShell provides a pre-start environment or non-login contract, or bind success at a trust boundary that startup files cannot forge. Add a regression that emits exactly one ordered BEGIN/EXIT:0 pair and terminates before the managed runner, then verifies that connect fails and that cleanup uses an authority not subject to the hostile profile.

Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
@apurvvkumaria
apurvvkumaria requested a review from cv August 12, 2026 19:40
@github-actions

Copy link
Copy Markdown
Contributor

@apurvvkumaria apurvvkumaria changed the title fix(sandbox): avoid nested login shells in Deep Agents Code smoke fix(sandbox): protect DCode managed login probes Aug 12, 2026
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
@apurvvkumaria

Copy link
Copy Markdown
Collaborator

CI classification update

The all-agent managed activation failed in the unchanged OpenClaw path: the sandbox launched, but the gateway never answered and its dashboard forward refused the connection. The direct managed startup checks for OpenClaw, Hermes, and Deep Agents Code all passed, as did staging QA. This PR changes the Deep Agents Code smoke route and does not change OpenClaw startup.

The managed-image workflow is already on attempt two, so the single transient retry is exhausted. I will not rerun it again. A fresh repository-owned run or runtime-infrastructure intervention is required before this gate can pass.

This CI classification does not resolve or waive the separate human security-review state.

Signed-off-by: Carlos Villela <cvillela@nvidia.com>
@cv
cv dismissed their stale review August 13, 2026 07:08

Dismissed as stale at 5e6bedb. Smoke success now depends on the protected root-owned login profile instead of sandbox-user marker output, and the hostile-profile regression verifies fail-closed behavior and cleanup authority.

Signed-off-by: Carlos Villela <cvillela@nvidia.com>
@prekshivyas
prekshivyas enabled auto-merge (squash) August 13, 2026 07:42

@cv cv left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approved at reviewed commit d701f60812787554500b204809d3a12e47ae6e46. The login-profile probe now uses the trusted root-owned shell path, the hostile-profile regression covers the original failure boundary, and the managed-image staging source matches the required libssh2 package revision. All review threads are resolved, and the published advisor assessment reports no findings requiring follow-up.

@prekshivyas
prekshivyas merged commit 5e7d8a0 into main Aug 13, 2026
81 of 83 checks passed
@prekshivyas
prekshivyas deleted the fix/8624-dcode-probe-login-profile branch August 13, 2026 07:47

@prekshivyas prekshivyas left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approved based on code-diff review. Required GitHub checks must pass before merge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: sandbox OpenShell sandbox lifecycle, runtime, config, or recovery area: security Security controls, permissions, secrets, or hardening bug-fix PR fixes a bug or regression integration: dcode LangChain Deep Code integration behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Ubuntu 24.04][Security] DCode connect probe executes sandbox-user login profile before failing closed

5 participants