Skip to content

feat(sandbox): import host corporate proxy CA into sandbox trust (#6210) - #6292

Merged
cv merged 34 commits into
mainfrom
fix/6210-corporate-proxy-ca
Jul 10, 2026
Merged

feat(sandbox): import host corporate proxy CA into sandbox trust (#6210)#6292
cv merged 34 commits into
mainfrom
fix/6210-corporate-proxy-ca

Conversation

@yimoj

@yimoj yimoj commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

Summary

On networks where a corporate MITM proxy sits in front of the host and re-signs external TLS with its own root CA, that corporate root is absent from the sandbox trust path. OpenShell injects only its own L7-proxy CA, so external channel endpoints (e.g. api.telegram.org) fail certificate verification even when the network policy allows the connection (logs show NET:OPEN then NET:FAIL). This imports an operator-supplied corporate CA into the sandbox trust bundle without replacing the OpenShell CA.

Related Issue

Fixes #6210

Changes

  • Add src/lib/onboard/corporate-ca.ts: validate a host CA bundle (regular file, non-symlink, non-world-writable, bounded size, contains a PEM certificate) from NEMOCLAW_CORPORATE_CA_BUNDLE (explicit, fail-loud) or the conventional REQUESTS_CA_BUNDLE / CURL_CA_BUNDLE / SSL_CERT_FILE fallbacks (skip-on-invalid). Opt out with NEMOCLAW_CORPORATE_CA_IMPORT=0.
  • Bake the validated CA into the OpenClaw and Hermes images via a base64 NEMOCLAW_CORPORATE_CA_B64 build arg decoded to a root-owned 0444 file (dockerfile-patch.ts + both Dockerfiles).
  • At entrypoint startup (scripts/nemoclaw-start.sh, agents/hermes/start.sh), append the baked CA to the OpenShell bundle into a merged /tmp bundle — never replacing the OpenShell CA (preserves [All Platforms]Proxy CA certificate not injected into sandbox trust store — all TLS connections fail #1828) — and repoint SSL_CERT_FILE, CURL_CA_BUNDLE, REQUESTS_CA_BUNDLE, GIT_SSL_CAINFO, and NODE_EXTRA_CA_CERTS at it, including for connect sessions. No-corporate-CA path is byte-for-byte unchanged.
  • Document the flow in docs/reference/troubleshooting.mdx.

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 — reviewer/approval link/justification: host CA input is validated (no symlink/world-writable/oversize; PEM required); the CA is a public certificate baked as a root-owned 0444 file; OpenShell CA is appended, never replaced; no secrets involved.

Verification

  • PR description includes the DCO sign-off declaration and every commit appears as Verified in GitHub
  • Normal pre-commit, commit-msg, and pre-push hooks passed, or npm run check:diff passed when hooks were skipped or unavailable — all 28 prek pre-commit/pre-push hooks passed (shellcheck, hadolint, biome, source-shape budget, test-file-size budget, env-var docs, test-title-style); commit-msg clean on this commit.
  • Targeted behavior tests pass for the current change set — vitest run on corporate-ca.test.ts, dockerfile-patch.test.ts, corporate-ca-runtime-merge.test.ts, corporate-ca-tls-e2e.test.ts → 52 passed. The TLS test spins a server whose leaf is signed only by a simulated corporate CA and proves verification succeeds only after the real entrypoint merge while the OpenShell root stays trusted.
  • No secrets, API keys, or credentials committed
  • npm run docs builds without warnings (doc changes only)

E2E note

The reporter's DGX Station + real corporate MITM proxy is not available in CI. The trust-store behavior is reproduced hermetically without that hardware: test/corporate-ca-tls-e2e.test.ts generates a corporate root CA, signs a leaf with it, runs the actual merge_corporate_proxy_ca block extracted from scripts/nemoclaw-start.sh, and asserts a Node TLS request verifies the corporate-signed endpoint only after the merge (pre-fix: unable to verify / UNABLE_TO_GET_ISSUER; post-fix: 200), and that the OpenShell root remains trusted through the merged bundle.


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

Summary by CodeRabbit

  • New Features
    • Added operator-controlled corporate proxy CA import with validation and optional image baking, plus host anchor scanning fallback.
    • Merges corporate and OpenShell CA trust at startup and updates TLS trust settings for curl/Python/Git/Node; connect-session shells inherit the merged trust (including Hermes).
  • Bug Fixes
    • Improves reliability of external TLS connections behind corporate MITM proxies while preserving OpenShell-root trust; includes safer merge/guard behavior.
  • Documentation
    • Added troubleshooting guidance for corporate MITM TLS failures and how to supply/override corporate CA trust.
  • Tests
    • Added runtime merge, Dockerfile patching, and end-to-end TLS coverage.

On networks where a corporate MITM proxy sits in front of the host and
re-signs external TLS with its own root, that root is absent from the
sandbox trust path. OpenShell injects only its own L7-proxy CA, so
external endpoints (e.g. api.telegram.org) fail verification even when
the network policy allows the connection (NET:OPEN then NET:FAIL).

Import an operator-supplied corporate CA without replacing the OpenShell
CA:

- Add src/lib/onboard/corporate-ca.ts: validate a host CA bundle
  (regular file, non-symlink, non-world-writable, bounded size, PEM)
  from NEMOCLAW_CORPORATE_CA_BUNDLE (explicit, fail-loud) or the
  conventional REQUESTS_CA_BUNDLE / CURL_CA_BUNDLE / SSL_CERT_FILE
  fallbacks (skip-on-invalid). Opt out with NEMOCLAW_CORPORATE_CA_IMPORT=0.
- Bake it via a base64 NEMOCLAW_CORPORATE_CA_B64 build arg decoded to a
  root-owned 0444 file in the OpenClaw and Hermes Dockerfiles.
- At entrypoint startup, append the baked CA to the OpenShell bundle into
  a merged /tmp bundle (never replacing the OpenShell CA, preserving
  #1828) and repoint SSL_CERT_FILE, CURL_CA_BUNDLE, REQUESTS_CA_BUNDLE,
  GIT_SSL_CAINFO, and NODE_EXTRA_CA_CERTS at it, including connect
  sessions.

Tests: host CA validation unit tests, dockerfile-patch baking, runtime
merge for both entrypoints, a simulated-MITM TLS test proving a
corporate-CA-signed endpoint verifies only after the merge while the
OpenShell root stays trusted, and troubleshooting docs.

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

coderabbitai Bot commented Jul 6, 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: c7db9bd2-4321-4e45-b7f8-ba3b4e7d0d97

📥 Commits

Reviewing files that changed from the base of the PR and between 16a8e53 and 7ddde82.

📒 Files selected for processing (4)
  • docs/reference/troubleshooting.mdx
  • src/lib/onboard/corporate-ca-host-anchors.ts
  • test/e2e/live/cloud-onboard.test.ts
  • test/e2e/live/onboard-repair.test.ts
💤 Files with no reviewable changes (1)
  • src/lib/onboard/corporate-ca-host-anchors.ts
✅ Files skipped from review due to trivial changes (1)
  • docs/reference/troubleshooting.mdx

📝 Walkthrough

Walkthrough

Adds corporate proxy CA discovery, validation, Dockerfile baking, runtime trust-bundle merging, connect-session propagation, host-anchor fallback discovery, troubleshooting documentation, and integration coverage.

Changes

Corporate proxy CA onboarding and runtime trust

Layer / File(s) Summary
Host CA resolution and validation
src/lib/onboard/corporate-ca.ts, src/lib/onboard/corporate-ca-host-anchors.ts
Validates explicit and fallback CA files, discovers administrator-managed anchors, enforces certificate and size limits, and encodes validated PEM data.
Dockerfile patching and image baking
src/lib/onboard/dockerfile-patch.ts, Dockerfile, agents/hermes/Dockerfile
Injects the encoded CA into staged Dockerfiles and conditionally decodes, validates, and stores it as a root-owned read-only image file.
Runtime bundle merge and session propagation
scripts/nemoclaw-start.sh, agents/hermes/start.sh
Merges corporate and OpenShell bundles, exports trust variables, sets a merge marker, and propagates CA settings to connect sessions.
Runtime and TLS validation
test/helpers/corporate-ca-support.ts, test/corporate-ca-runtime-merge.test.ts, test/corporate-ca-tls-e2e.test.ts, src/lib/onboard/dockerfile-patch-corporate-ca.test.ts
Adds certificate fixtures and tests for Dockerfile injection, runtime merging, failure handling, session exports, and HTTPS verification.
Onboarding and repair validation
test/e2e/live/cloud-onboard.test.ts, test/e2e/live/onboard-repair.test.ts
Passes corporate CA fixtures through cloud onboarding and repair flows and probes sandbox trust afterward.
Documentation and recovery test support
docs/reference/troubleshooting.mdx, src/lib/actions/sandbox/rebuild-prepared-recovery.test.ts
Documents corporate proxy TLS remediation and preserves the third-party software environment variable during recovery tests.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Onboard
  participant DockerBuild
  participant SandboxStartup
  participant ConnectSession
  participant TLSClient

  Onboard->>DockerBuild: provide NEMOCLAW_CORPORATE_CA_B64
  DockerBuild->>SandboxStartup: install corporate-ca.pem
  SandboxStartup->>SandboxStartup: merge OpenShell and corporate bundles
  SandboxStartup->>ConnectSession: export merged CA variables
  TLSClient->>SandboxStartup: use merged trust bundle
Loading

Possibly related PRs

  • NVIDIA/NemoClaw#5154: Hardens the staged Dockerfile patching path extended by corporate CA argument injection.

Suggested labels: security

Suggested reviewers: jyaunches, ericksoa, kjw3

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: importing a host corporate proxy CA into sandbox trust.
Linked Issues check ✅ Passed The PR implements corporate-CA discovery, validation, baking, startup merge, and trust re-pointing, matching #6210's goals.
Out of Scope Changes check ✅ Passed The changes are centered on corporate CA import and the added tests/docs support that flow without obvious unrelated scope.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ 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/6210-corporate-proxy-ca

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

@github-code-quality

github-code-quality Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: TypeScript

TypeScript / code-coverage/plugin

The overall coverage remains at 96%, unchanged from the main branch.

TypeScript / code-coverage/cli

The overall coverage in the fix/6210-corporate-p... branch remains at 77%, unchanged from the main branch.

Show a code coverage summary of the most impacted files.
File main 14dc22c fix/6210-corporate-p... 7ddde82 +/-
src/lib/actions...de-preflight.ts 60% 51% -9%
src/lib/actions...e-validation.ts 90% 81% -9%
src/lib/actions...x/mcp-bridge.ts 44% 35% -9%
src/lib/inferen...board-probes.ts 84% 86% +2%
src/lib/onboard...host-anchors.ts 0% 91% +91%
src/lib/onboard...a-validation.ts 0% 100% +100%
src/lib/onboard...te-ca-policy.ts 0% 100% +100%
src/lib/onboard...-ca-fixtures.ts 0% 100% +100%
src/lib/onboard...orate-ca-env.ts 0% 100% +100%
src/lib/onboard...corporate-ca.ts 0% 100% +100%

Updated July 10, 2026 15:26 UTC
Code Coverage is in Public Preview. Learn more and provide us with your feedback.

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

E2E Advisor Recommendation

Required E2E: cloud-onboard, onboard-repair, onboard-resume
Optional E2E: network-policy, messaging-providers, hermes-e2e

Dispatch hint: cloud-onboard,onboard-repair,onboard-resume

Workflow run

Full advisor summary

E2E Recommendation Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required E2E

  • cloud-onboard (high): Required by the deterministic risk plan for platform/install changes in Dockerfile and agents/hermes/Dockerfile, and because corporate CA import is exercised during full hosted onboarding and image build.
  • onboard-repair (medium): Required by the deterministic risk plan for onboarding lifecycle-state changes. Validates that repair converges persisted onboarding metadata, rebuilt image state, and live sandbox status after the new CA build/runtime path is present.
  • onboard-resume (medium): Required by the deterministic risk plan for onboarding lifecycle-state changes. Validates interrupted/resumed onboarding with the new corporate CA Dockerfile patching and state propagation path.

Optional E2E

  • network-policy (medium): Useful adjacent confidence because the CA merge changes TLS behavior on allowed external egress while preserving OpenShell proxy/network-policy enforcement.
  • messaging-providers (medium): Optional validation for external provider/channel traffic that may be affected by corporate MITM TLS trust and CA environment propagation.
  • hermes-e2e (medium): Optional Hermes-specific smoke because agents/hermes/Dockerfile and agents/hermes/start.sh now decode and merge the corporate CA in the Hermes runtime path.

New E2E recommendations

  • corporate-ca-live-tls (high): The PR adds substantial corporate CA import and runtime merge behavior, but the existing live jobs primarily cover general onboarding/resume/repair. A dedicated live job should stand up a local TLS endpoint signed by a test corporate root, bake that root through onboarding, and prove OpenClaw and Hermes connect sessions trust the merged OpenShell-plus-corporate bundle without bypassing network policy.
    • Suggested test: Add a workflow-dispatchable live E2E target for corporate CA TLS trust propagation across onboard, rebuild/resume, runtime startup, and connect-session shell environment.

Dispatch hint

  • Workflow: .github/workflows/e2e.yaml
  • jobs input: cloud-onboard,onboard-repair,onboard-resume

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

E2E Target Recommendation

Required E2E targets: cloud-onboard, onboard-repair, onboard-resume
Optional E2E targets: None

Dispatch required E2E targets:

  • gh workflow run e2e.yaml --ref <pr-head-ref> --field jobs=cloud-onboard
  • gh workflow run e2e.yaml --ref <pr-head-ref> --field jobs=onboard-repair
  • gh workflow run e2e.yaml --ref <pr-head-ref> --field jobs=onboard-resume

Workflow run

Full E2E target advisor summary

E2E Target Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required E2E targets

  • cloud-onboard: Installer and platform changes must work on a clean supported host with the pinned runtime dependencies.
    • Dispatch: gh workflow run e2e.yaml --ref <pr-head-ref> --field jobs=cloud-onboard
  • onboard-repair: Onboarding and sandbox state must converge across persisted metadata, reported status, and the live runtime.
    • Dispatch: gh workflow run e2e.yaml --ref <pr-head-ref> --field jobs=onboard-repair
  • onboard-resume: Onboarding and sandbox state must converge across persisted metadata, reported status, and the live runtime.
    • Dispatch: gh workflow run e2e.yaml --ref <pr-head-ref> --field jobs=onboard-resume

Optional E2E targets

  • None.

Relevant changed files

  • Dockerfile
  • agents/hermes/Dockerfile
  • agents/hermes/start.sh
  • scripts/nemoclaw-start.sh
  • src/lib/onboard/__test-helpers__/corporate-ca-fixtures.ts
  • src/lib/onboard/corporate-ca-env.ts
  • src/lib/onboard/corporate-ca-host-anchors.ts
  • src/lib/onboard/corporate-ca-policy.ts
  • src/lib/onboard/corporate-ca-types.ts
  • src/lib/onboard/corporate-ca-validation.ts
  • src/lib/onboard/corporate-ca.ts
  • src/lib/onboard/dockerfile-patch.ts
  • test/e2e/fixtures/corporate-ca.ts
  • test/e2e/live/cloud-onboard.test.ts
  • test/e2e/live/onboard-repair.test.ts
  • test/e2e/live/onboard-resume.test.ts

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

PR Review Advisor (Nemotron Ultra) — Changes requested

Merge posture: Do not merge yet
Primary next action: Resolve or justify PRA-1: Duplicated merge_corporate_proxy_ca() function across OpenClaw and Hermes entrypoints.
Open items: 0 required · 10 warnings · 1 suggestion · 8 test follow-ups
Since last review: 0 prior items resolved · 7 still apply · 4 new items found

Action checklist

  • PRA-1 Resolve or justify: Duplicated merge_corporate_proxy_ca() function across OpenClaw and Hermes entrypoints in scripts/nemoclaw-start.sh:1380
  • PRA-2 Resolve or justify: Duplicated corporate CA decode/validate RUN block across Dockerfiles in Dockerfile:845
  • PRA-3 Resolve or justify: dockerfile-patch.ts approaching monolith threshold with 8+ patch concerns in src/lib/onboard/dockerfile-patch.ts:285
  • PRA-4 Resolve or justify: Asymmetric fail-loud vs silent no-op for corporate CA ARG in dockerfile-patch.ts in src/lib/onboard/dockerfile-patch.ts:388
  • PRA-5 Resolve or justify: Single-use anchor directory constants add indirection in src/lib/onboard/corporate-ca-host-anchors.ts:29
  • PRA-6 Resolve or justify: Missing regression test for fallback CA + missing ARG on custom Dockerfile in src/lib/onboard/dockerfile-patch-corporate-ca.test.ts:180
  • PRA-8 Resolve or justify: Missing cross-entrypoint merge equivalence regression test in test/corporate-ca-runtime-merge.test.ts:1
  • PRA-9 Resolve or justify: Missing cross-Dockerfile decode equivalence regression test in test/corporate-ca-dockerfile-decode.test.ts:40
  • PRA-10 Resolve or justify: Missing test for host anchor dir path-list override in src/lib/onboard/corporate-ca-host-anchors.test.ts:275
  • PRA-11 Resolve or justify: Missing test for literal /etc/ssl/certs probe gating in src/lib/onboard/corporate-ca-host-anchors.test.ts:269
  • PRA-T1 Add or justify test follow-up: Runtime validation
  • PRA-T2 Add or justify test follow-up: Runtime validation
  • PRA-T3 Add or justify test follow-up: Runtime validation
  • PRA-T4 Add or justify test follow-up: Runtime validation
  • PRA-T5 Add or justify test follow-up: Runtime validation
  • PRA-T6 Add or justify test follow-up: Missing test for host anchor dir path-list override
  • PRA-T7 Add or justify test follow-up: Missing test for literal /etc/ssl/certs probe gating
  • PRA-T8 Add or justify test follow-up: Env fallback chain (corporate-ca-env.ts:44-59)
  • PRA-7 In-scope improvement: Custom Dockerfile contract could explicitly recommend ARG adoption in docs/reference/troubleshooting.mdx:1253

Findings index

ID Severity Category Location Required action
PRA-1 Resolve/justify architecture scripts/nemoclaw-start.sh:1380 Extract the merge logic to a shared helper script (e.g., /usr/local/lib/nemoclaw/merge-corporate-ca.sh) sourced by both entrypoints, or a common shell library file.
PRA-2 Resolve/justify architecture Dockerfile:845 Extract to a shared build-time script (e.g., scripts/decode-corporate-ca.sh) invoked by both Dockerfiles, or a common .dockerfile snippet included via COPY.
PRA-3 Resolve/justify scope src/lib/onboard/dockerfile-patch.ts:285 Split into focused modules per patch concern (e.g., dockerfile-patch-corporate-ca.ts, dockerfile-patch-messaging.ts) with a thin orchestrator, following the existing pattern of dockerfile-patch-corporate-ca.test.ts being separate.
PRA-4 Resolve/justify scope src/lib/onboard/dockerfile-patch.ts:388 Either make fallback env vars also warn when ARG is missing (consistent with explicit), or add prominent WARNING log in the no-op path. Option (a) preferred for consistency.
PRA-5 Resolve/justify architecture src/lib/onboard/corporate-ca-host-anchors.ts:29 Inline the two directory paths (/usr/local/share/ca-certificates with .crt, /etc/pki/ca-trust/source/anchors with .pem/.crt/.cer) directly into the exported array and collection function; remove DEFAULT_HOST_ANCHOR_SPECS and anchorExtensionsFor().
PRA-6 Resolve/justify correctness src/lib/onboard/dockerfile-patch-corporate-ca.test.ts:180 Add a test that sets a fallback env var (e.g., REQUESTS_CA_BUNDLE) with a valid CA, uses a Dockerfile without ARG NEMOCLAW_CORPORATE_CA_B64, and asserts a WARNING is logged (or error thrown per F-004 resolution).
PRA-7 Improvement docs docs/reference/troubleshooting.mdx:1253 Add a code snippet showing the exact ARG + RUN block to add to custom Dockerfiles (mirroring the managed Dockerfiles).
PRA-8 Resolve/justify security test/corporate-ca-runtime-merge.test.ts:1 Add a cross-entrypoint equivalence test in corporate-ca-runtime-merge.test.ts that runs both entrypoints' extracted merge blocks against identical OpenShell + corporate CA inputs and asserts byte-for-byte identical merged bundle and env var exports.
PRA-9 Resolve/justify security test/corporate-ca-dockerfile-decode.test.ts:40 Add cross-Dockerfile equivalence assertions in corporate-ca-dockerfile-decode.test.ts that run both Dockerfiles' extracted decode blocks against identical test vectors and assert identical exit codes, stderr messages, and baked corporate-ca.pem content.
PRA-10 Resolve/justify tests src/lib/onboard/corporate-ca-host-anchors.test.ts:275 Add a test setting NEMOCLAW_CORPORATE_CA_ANCHOR_DIRS to a colon-delimited list with valid CAs in different directories, asserting the first yielding a bundle wins.
PRA-11 Resolve/justify tests src/lib/onboard/corporate-ca-host-anchors.test.ts:269 Add a test where host anchor directory yields a valid CA and literal ssl-certs dir also has valid CAs, asserting the anchor CA wins and literal probe is not used.
Review findings by urgency: 0 required fixes, 10 items to resolve/justify, 1 in-scope improvement

⚠️ Resolve or justify before merge

Investigate these in the current review; either fix them, explain why they are not applicable, or document the accepted risk.

PRA-1 Resolve/justify — Duplicated merge_corporate_proxy_ca() function across OpenClaw and Hermes entrypoints

  • Location: scripts/nemoclaw-start.sh:1380
  • Category: architecture
  • Problem: Identical ~90-line merge_corporate_proxy_ca() function duplicated in scripts/nemoclaw-start.sh:1380 and agents/hermes/start.sh:1380. Any future fix or hardening must be applied in two places, creating drift risk between OpenClaw and Hermes sandbox trust behavior.
  • Impact: Maintenance burden and potential divergence between OpenClaw and Hermes sandbox trust behavior; security fixes applied to one entrypoint may be missed in the other.
  • Recommended action: Extract the merge logic to a shared helper script (e.g., /usr/local/lib/nemoclaw/merge-corporate-ca.sh) sourced by both entrypoints, or a common shell library file.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: diff scripts/nemoclaw-start.sh agents/hermes/start.sh | grep -A 100 'merge_corporate_proxy_ca'
  • Missing regression test: Add a test that validates both entrypoints produce identical merged bundle behavior given the same inputs.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: diff scripts/nemoclaw-start.sh agents/hermes/start.sh | grep -A 100 'merge_corporate_proxy_ca'.
  • Evidence: scripts/nemoclaw-start.sh lines 1380-1470 agents/hermes/start.sh lines 1380-1470 Prior review PRA-1 at headSha 16a8e53

PRA-2 Resolve/justify — Duplicated corporate CA decode/validate RUN block across Dockerfiles

  • Location: Dockerfile:845
  • Category: architecture
  • Problem: Identical ~25-line base64 decode + awk cert extraction + openssl validation RUN block duplicated in Dockerfile:845 and agents/hermes/Dockerfile:225. Any validation improvement must be synchronized across both Dockerfiles.
  • Impact: Drift risk between OpenClaw and Hermes image build validation; double maintenance surface for build-time CA validation.
  • Recommended action: Extract to a shared build-time script (e.g., scripts/decode-corporate-ca.sh) invoked by both Dockerfiles, or a common .dockerfile snippet included via COPY.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: diff Dockerfile agents/hermes/Dockerfile | grep -A 30 'NEMOCLAW_CORPORATE_CA_B64'
  • Missing regression test: Add a test that validates both Dockerfiles reject the same malformed inputs and accept the same valid inputs.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: diff Dockerfile agents/hermes/Dockerfile | grep -A 30 'NEMOCLAW_CORPORATE_CA_B64'.
  • Evidence: Dockerfile lines 845-870 agents/hermes/Dockerfile lines 225-250 Prior review PRA-2 at headSha 16a8e53

PRA-3 Resolve/justify — dockerfile-patch.ts approaching monolith threshold with 8+ patch concerns

  • Location: src/lib/onboard/dockerfile-patch.ts:285
  • Category: scope
  • Problem: dockerfile-patch.ts grew from 361 to 394 lines (+33) handling build-id, tool-disclosure, messaging, extra-agents, web-search, OTEL, dcode auto-approval, and corporate CA — 8 distinct patch concerns in one module.
  • Impact: Increasing cognitive load and change coupling; future patches risk unintended interactions across unrelated concerns.
  • Recommended action: Split into focused modules per patch concern (e.g., dockerfile-patch-corporate-ca.ts, dockerfile-patch-messaging.ts) with a thin orchestrator, following the existing pattern of dockerfile-patch-corporate-ca.test.ts being separate.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: wc -l src/lib/onboard/dockerfile-patch.ts
  • Missing regression test: None — structural refactor; existing tests cover behavior.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: wc -l src/lib/onboard/dockerfile-patch.ts.
  • Evidence: src/lib/onboard/dockerfile-patch.ts (394 lines) Prior review PRA-3 at headSha 16a8e53

PRA-4 Resolve/justify — Asymmetric fail-loud vs silent no-op for corporate CA ARG in dockerfile-patch.ts

  • Location: src/lib/onboard/dockerfile-patch.ts:388
  • Category: scope
  • Problem: Explicit NEMOCLAW_CORPORATE_CA_BUNDLE throws when ARG NEMOCLAW_CORPORATE_CA_B64 missing, but fallback env vars (REQUESTS_CA_BUNDLE, CURL_CA_BUNDLE, SSL_CERT_FILE) silently no-op on custom Dockerfiles without the ARG. Operator using custom Dockerfile with fallback corporate CA env var gets silent failure — CA not baked, no error, external TLS breaks at runtime.
  • Impact: Operator surprise and silent TLS failures when using custom Dockerfiles with conventional CA env vars.
  • Recommended action: Either make fallback env vars also warn when ARG is missing (consistent with explicit), or add prominent WARNING log in the no-op path. Option (a) preferred for consistency.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: grep -n 'custom Dockerfile' src/lib/onboard/dockerfile-patch-corporate-ca.test.ts
  • Missing regression test: Test that fallback env var with missing ARG in custom Dockerfile produces expected behavior (warn or throw).
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: grep -n 'custom Dockerfile' src/lib/onboard/dockerfile-patch-corporate-ca.test.ts.
  • Evidence: src/lib/onboard/dockerfile-patch.ts:388 src/lib/onboard/dockerfile-patch-corporate-ca.test.ts lines 140-155 (explicit throw test only) Prior review PRA-4 at headSha 16a8e53

PRA-5 Resolve/justify — Single-use anchor directory constants add indirection

  • Location: src/lib/onboard/corporate-ca-host-anchors.ts:29
  • Category: architecture
  • Problem: DEFAULT_HOST_ANCHOR_SPECS and anchorExtensionsFor() add indirection for two directory paths that are only used once each in the same file. Inlining would simplify without loss of clarity.
  • Impact: Unnecessary abstraction layer for static, single-use configuration; increases cognitive load for readers.
  • Recommended action: Inline the two directory paths (/usr/local/share/ca-certificates with .crt, /etc/pki/ca-trust/source/anchors with .pem/.crt/.cer) directly into the exported array and collection function; remove DEFAULT_HOST_ANCHOR_SPECS and anchorExtensionsFor().
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: grep -n 'DEFAULT_HOST_ANCHOR_SPECS\|anchorExtensionsFor' src/lib/onboard/corporate-ca-host-anchors.ts
  • Missing regression test: None — simplification; existing tests cover behavior.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: grep -n 'DEFAULT_HOST_ANCHOR_SPECS\|anchorExtensionsFor' src/lib/onboard/corporate-ca-host-anchors.ts.
  • Evidence: src/lib/onboard/corporate-ca-host-anchors.ts:29-37 Prior review PRA-5 at headSha 16a8e53

PRA-6 Resolve/justify — Missing regression test for fallback CA + missing ARG on custom Dockerfile

  • Location: src/lib/onboard/dockerfile-patch-corporate-ca.test.ts:180
  • Category: correctness
  • Problem: The asymmetric behavior (explicit throws, fallback silently no-ops) is documented but lacks a test asserting the fallback path produces a warning (or throws) when ARG NEMOCLAW_CORPORATE_CA_B64 is absent. Current test at line 180 only confirms no-op without verifying operator visibility.
  • Impact: Operator using custom Dockerfile with REQUESTS_CA_BUNDLE/CURL_CA_BUNDLE/SSL_CERT_FILE gets silent TLS failure at runtime — no build-time signal.
  • Recommended action: Add a test that sets a fallback env var (e.g., REQUESTS_CA_BUNDLE) with a valid CA, uses a Dockerfile without ARG NEMOCLAW_CORPORATE_CA_B64, and asserts a WARNING is logged (or error thrown per F-004 resolution).
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: grep -n 'fallback.*missing ARG\|custom Dockerfile.*warn' src/lib/onboard/dockerfile-patch-corporate-ca.test.ts
  • Missing regression test: Test that fallback env var with missing ARG in custom Dockerfile produces a WARNING log (consistent with explicit fail-loud) so operator knows CA was not baked.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: grep -n 'fallback.*missing ARG\|custom Dockerfile.*warn' src/lib/onboard/dockerfile-patch-corporate-ca.test.ts.
  • Evidence: src/lib/onboard/dockerfile-patch.ts:388-390 (asymmetric logic) src/lib/onboard/dockerfile-patch-corporate-ca.test.ts:180 (no-op test only) docs/reference/troubleshooting.mdx:1253-1255 (custom Dockerfile contract docs)

PRA-8 Resolve/justify — Missing cross-entrypoint merge equivalence regression test

  • Location: test/corporate-ca-runtime-merge.test.ts:1
  • Category: security
  • Problem: Both entrypoints' merge_corporate_proxy_ca blocks are tested in isolation, but no test asserts they produce identical merged bundle behavior given the same inputs. Drift between OpenClaw and Hermes merge logic would go undetected, creating divergent sandbox trust behavior.
  • Impact: Security hardening or bug fix applied to one entrypoint's merge logic may not be reflected in the other, creating divergent sandbox trust behavior.
  • Recommended action: Add a cross-entrypoint equivalence test in corporate-ca-runtime-merge.test.ts that runs both entrypoints' extracted merge blocks against identical OpenShell + corporate CA inputs and asserts byte-for-byte identical merged bundle and env var exports.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: grep -n 'cross-entrypoint\|equivalence' test/corporate-ca-runtime-merge.test.ts
  • Missing regression test: Test that OpenClaw and Hermes merge blocks produce identical merged bundle content and CA env var exports for the same input CA and OpenShell bundle.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: grep -n 'cross-entrypoint\|equivalence' test/corporate-ca-runtime-merge.test.ts.
  • Evidence: test/corporate-ca-runtime-merge.test.ts tests OpenClaw and Hermes blocks separately scripts/nemoclaw-start.sh:1380 and agents/hermes/start.sh:1380 contain duplicated merge logic Security Category 8/9: missing equivalence test for trust-boundary code

PRA-9 Resolve/justify — Missing cross-Dockerfile decode equivalence regression test

  • Location: test/corporate-ca-dockerfile-decode.test.ts:40
  • Category: security
  • Problem: Both Dockerfiles' corporate CA decode/validate RUN blocks are parametrized and tested in isolation, but no test asserts they produce identical exit codes, error messages, and baked output for the same test vectors (valid cert, invalid base64, non-certificate PEM, cert request, oversized bundle).
  • Impact: Validation logic drift between Dockerfiles would go undetected; one image could accept malformed input that the other rejects, creating inconsistent build-time trust behavior.
  • Recommended action: Add cross-Dockerfile equivalence assertions in corporate-ca-dockerfile-decode.test.ts that run both Dockerfiles' extracted decode blocks against identical test vectors and assert identical exit codes, stderr messages, and baked corporate-ca.pem content.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: grep -n 'cross-Dockerfile\|equivalence\|identical' test/corporate-ca-dockerfile-decode.test.ts
  • Missing regression test: Test that both Dockerfiles' decode blocks produce identical results for the same input vectors.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: grep -n 'cross-Dockerfile\|equivalence\|identical' test/corporate-ca-dockerfile-decode.test.ts.
  • Evidence: test/corporate-ca-dockerfile-decode.test.ts parametrizes over both Dockerfiles but has no cross-equivalence assertions Dockerfile:845 and agents/hermes/Dockerfile:225 contain duplicated decode block Security Category 8/9: missing equivalence test for trust-boundary code

PRA-10 Resolve/justify — Missing test for host anchor dir path-list override

  • Location: src/lib/onboard/corporate-ca-host-anchors.test.ts:275
  • Category: tests
  • Problem: hostAnchorDirsFromEnv() splits on path.delimiter but no test covers multiple directories in NEMOCLAW_CORPORATE_CA_ANCHOR_DIRS (e.g., '/dir1:/dir2'). Only single-dir override and empty disable are tested.
  • Impact: Operators using multiple custom anchor directories may have only the first scanned; no regression guard for path-list parsing.
  • Recommended action: Add a test setting NEMOCLAW_CORPORATE_CA_ANCHOR_DIRS to a colon-delimited list with valid CAs in different directories, asserting the first yielding a bundle wins.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: grep -n 'path.delimiter\|multiple.*anchor' src/lib/onboard/corporate-ca-host-anchors.test.ts
  • Missing regression test: Test that NEMOCLAW_CORPORATE_CA_ANCHOR_DIRS='dir1:dir2' scans both directories in order and returns the first valid bundle.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: grep -n 'path.delimiter\|multiple.*anchor' src/lib/onboard/corporate-ca-host-anchors.test.ts.
  • Evidence: src/lib/onboard/corporate-ca-host-anchors.ts:218-223 (hostAnchorDirsFromEnv) src/lib/onboard/corporate-ca-host-anchors.test.ts:248-257 (single override test only)

PRA-11 Resolve/justify — Missing test for literal /etc/ssl/certs probe gating

  • Location: src/lib/onboard/corporate-ca-host-anchors.test.ts:269
  • Category: tests
  • Problem: The literal /etc/ssl/certs probe runs only when host anchor scanning is not disabled AND no anchor directory yielded a CA. Tests cover disabled-by-env and anchor-yields-nothing → probe runs, but not anchor-yields-CA → probe skipped.
  • Impact: If anchor scan finds a CA but literal probe has different behavior (e.g., different certs), the precedence logic is untested.
  • Recommended action: Add a test where host anchor directory yields a valid CA and literal ssl-certs dir also has valid CAs, asserting the anchor CA wins and literal probe is not used.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: grep -n 'literal.*anchor\|anchor.*literal' src/lib/onboard/corporate-ca-host-anchors.test.ts
  • Missing regression test: Test that anchor CA takes precedence over literal /etc/ssl/certs CAs when both are present.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: grep -n 'literal.*anchor\|anchor.*literal' src/lib/onboard/corporate-ca-host-anchors.test.ts.
  • Evidence: src/lib/onboard/corporate-ca.ts:88-96 (resolveCorporateCa gating logic) src/lib/onboard/corporate-ca-host-anchors.test.ts:207-247 (literal probe tests, no anchor-precedence test)

💡 In-scope improvements

These are lower-risk, not throwaway. Prefer fixing them in this PR when they are local to changed code; defer only with rationale or a linked follow-up.

PRA-7 Improvement — Custom Dockerfile contract could explicitly recommend ARG adoption

  • Location: docs/reference/troubleshooting.mdx:1253
  • Category: docs
  • Problem: The troubleshooting docs explain the custom Dockerfile silent no-op but do not explicitly recommend adding ARG NEMOCLAW_CORPORATE_CA_B64 and the decode RUN block to custom Dockerfiles. Operators may not realize the fix is a 2-line Dockerfile addition.
  • Impact: Operators with custom Dockerfiles may not know how to enable corporate CA import, leading to prolonged silent TLS failures.
  • Suggested action: Add a code snippet showing the exact ARG + RUN block to add to custom Dockerfiles (mirroring the managed Dockerfiles).
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: grep -A 10 'Custom Dockerfile contract' docs/reference/troubleshooting.mdx
  • Missing regression test: None — documentation improvement.
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: docs/reference/troubleshooting.mdx:1253-1255 (current contract text)
Test follow-ups to resolve or justify

If these cover changed behavior, prefer adding them in this PR; otherwise state why existing coverage is enough or link the follow-up.

  • PRA-T1 Runtime validation — Run the `cloud-onboard` E2E job for Installer and platform changes must work on a clean supported host with the pinned runtime dependencies. Matched files: `agents/hermes/Dockerfile`, `Dockerfile`, `test/e2e/live/cloud-onboard.test.ts`.. Deterministic regression risks require live validation: lifecycle-state, platform-install. Deterministic regression risks require live validation: lifecycle-state and platform-install riskPlan families. Unit/mocked coverage is strong for new corporate-CA logic (95+ test blocks), but required E2E jobs (cloud-onboard, onboard-repair, onboard-resume) are validation floors with no checked-in unit coverage for their invariants.
  • PRA-T2 Runtime validation — Add cross-entrypoint merge equivalence test (F-008) — run both entrypoints' extracted merge blocks against identical inputs, assert byte-for-byte identical merged bundle and env exports.. Deterministic regression risks require live validation: lifecycle-state, platform-install. Deterministic regression risks require live validation: lifecycle-state and platform-install riskPlan families. Unit/mocked coverage is strong for new corporate-CA logic (95+ test blocks), but required E2E jobs (cloud-onboard, onboard-repair, onboard-resume) are validation floors with no checked-in unit coverage for their invariants.
  • PRA-T3 Runtime validation — Run the `onboard-repair` E2E job for Onboarding and sandbox state must converge across persisted metadata, reported status, and the live runtime. Matched files: `src/lib/onboard/__test-helpers__/corporate-ca-fixtures.ts`, `src/lib/onboard/corporate-ca-env.ts`, `src/lib/onboard/corporate-ca-host-anchors.ts`, `src/lib/onboard/corporate-ca-policy.ts`, `src/lib/onboard/corporate-ca-types.ts`.. Deterministic regression risks require live validation: lifecycle-state, platform-install. Deterministic regression risks require live validation: lifecycle-state and platform-install riskPlan families. Unit/mocked coverage is strong for new corporate-CA logic (95+ test blocks), but required E2E jobs (cloud-onboard, onboard-repair, onboard-resume) are validation floors with no checked-in unit coverage for their invariants.
  • PRA-T4 Runtime validation — Add cross-Dockerfile decode equivalence test (F-009) — run both Dockerfiles' extracted decode blocks against identical test vectors, assert identical exit codes, stderr, baked output.. Deterministic regression risks require live validation: lifecycle-state, platform-install. Deterministic regression risks require live validation: lifecycle-state and platform-install riskPlan families. Unit/mocked coverage is strong for new corporate-CA logic (95+ test blocks), but required E2E jobs (cloud-onboard, onboard-repair, onboard-resume) are validation floors with no checked-in unit coverage for their invariants.
  • PRA-T5 Runtime validation — Run the `onboard-resume` E2E job for Onboarding and sandbox state must converge across persisted metadata, reported status, and the live runtime. Matched files: `src/lib/onboard/__test-helpers__/corporate-ca-fixtures.ts`, `src/lib/onboard/corporate-ca-env.ts`, `src/lib/onboard/corporate-ca-host-anchors.ts`, `src/lib/onboard/corporate-ca-policy.ts`, `src/lib/onboard/corporate-ca-types.ts`.. Deterministic regression risks require live validation: lifecycle-state, platform-install. Deterministic regression risks require live validation: lifecycle-state and platform-install riskPlan families. Unit/mocked coverage is strong for new corporate-CA logic (95+ test blocks), but required E2E jobs (cloud-onboard, onboard-repair, onboard-resume) are validation floors with no checked-in unit coverage for their invariants.
  • PRA-T6 Missing test for host anchor dir path-list override — Add a test setting NEMOCLAW_CORPORATE_CA_ANCHOR_DIRS to a colon-delimited list with valid CAs in different directories, asserting the first yielding a bundle wins.
  • PRA-T7 Missing test for literal /etc/ssl/certs probe gating — Add a test where host anchor directory yields a valid CA and literal ssl-certs dir also has valid CAs, asserting the anchor CA wins and literal probe is not used.
  • PRA-T8 Env fallback chain (corporate-ca-env.ts:44-59) — Test that fallback env var with missing ARG in custom Dockerfile produces WARNING log. corporate-ca-env.test.ts:44,57,87 cover warn-and-continue, all-invalid, merged-store-skip
Since last review details

Current findings, using the urgency labels above:

PRA-1 Resolve/justify — Duplicated merge_corporate_proxy_ca() function across OpenClaw and Hermes entrypoints

  • Location: scripts/nemoclaw-start.sh:1380
  • Category: architecture
  • Problem: Identical ~90-line merge_corporate_proxy_ca() function duplicated in scripts/nemoclaw-start.sh:1380 and agents/hermes/start.sh:1380. Any future fix or hardening must be applied in two places, creating drift risk between OpenClaw and Hermes sandbox trust behavior.
  • Impact: Maintenance burden and potential divergence between OpenClaw and Hermes sandbox trust behavior; security fixes applied to one entrypoint may be missed in the other.
  • Recommended action: Extract the merge logic to a shared helper script (e.g., /usr/local/lib/nemoclaw/merge-corporate-ca.sh) sourced by both entrypoints, or a common shell library file.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: diff scripts/nemoclaw-start.sh agents/hermes/start.sh | grep -A 100 'merge_corporate_proxy_ca'
  • Missing regression test: Add a test that validates both entrypoints produce identical merged bundle behavior given the same inputs.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: diff scripts/nemoclaw-start.sh agents/hermes/start.sh | grep -A 100 'merge_corporate_proxy_ca'.
  • Evidence: scripts/nemoclaw-start.sh lines 1380-1470 agents/hermes/start.sh lines 1380-1470 Prior review PRA-1 at headSha 16a8e53

PRA-2 Resolve/justify — Duplicated corporate CA decode/validate RUN block across Dockerfiles

  • Location: Dockerfile:845
  • Category: architecture
  • Problem: Identical ~25-line base64 decode + awk cert extraction + openssl validation RUN block duplicated in Dockerfile:845 and agents/hermes/Dockerfile:225. Any validation improvement must be synchronized across both Dockerfiles.
  • Impact: Drift risk between OpenClaw and Hermes image build validation; double maintenance surface for build-time CA validation.
  • Recommended action: Extract to a shared build-time script (e.g., scripts/decode-corporate-ca.sh) invoked by both Dockerfiles, or a common .dockerfile snippet included via COPY.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: diff Dockerfile agents/hermes/Dockerfile | grep -A 30 'NEMOCLAW_CORPORATE_CA_B64'
  • Missing regression test: Add a test that validates both Dockerfiles reject the same malformed inputs and accept the same valid inputs.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: diff Dockerfile agents/hermes/Dockerfile | grep -A 30 'NEMOCLAW_CORPORATE_CA_B64'.
  • Evidence: Dockerfile lines 845-870 agents/hermes/Dockerfile lines 225-250 Prior review PRA-2 at headSha 16a8e53

PRA-3 Resolve/justify — dockerfile-patch.ts approaching monolith threshold with 8+ patch concerns

  • Location: src/lib/onboard/dockerfile-patch.ts:285
  • Category: scope
  • Problem: dockerfile-patch.ts grew from 361 to 394 lines (+33) handling build-id, tool-disclosure, messaging, extra-agents, web-search, OTEL, dcode auto-approval, and corporate CA — 8 distinct patch concerns in one module.
  • Impact: Increasing cognitive load and change coupling; future patches risk unintended interactions across unrelated concerns.
  • Recommended action: Split into focused modules per patch concern (e.g., dockerfile-patch-corporate-ca.ts, dockerfile-patch-messaging.ts) with a thin orchestrator, following the existing pattern of dockerfile-patch-corporate-ca.test.ts being separate.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: wc -l src/lib/onboard/dockerfile-patch.ts
  • Missing regression test: None — structural refactor; existing tests cover behavior.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: wc -l src/lib/onboard/dockerfile-patch.ts.
  • Evidence: src/lib/onboard/dockerfile-patch.ts (394 lines) Prior review PRA-3 at headSha 16a8e53

PRA-4 Resolve/justify — Asymmetric fail-loud vs silent no-op for corporate CA ARG in dockerfile-patch.ts

  • Location: src/lib/onboard/dockerfile-patch.ts:388
  • Category: scope
  • Problem: Explicit NEMOCLAW_CORPORATE_CA_BUNDLE throws when ARG NEMOCLAW_CORPORATE_CA_B64 missing, but fallback env vars (REQUESTS_CA_BUNDLE, CURL_CA_BUNDLE, SSL_CERT_FILE) silently no-op on custom Dockerfiles without the ARG. Operator using custom Dockerfile with fallback corporate CA env var gets silent failure — CA not baked, no error, external TLS breaks at runtime.
  • Impact: Operator surprise and silent TLS failures when using custom Dockerfiles with conventional CA env vars.
  • Recommended action: Either make fallback env vars also warn when ARG is missing (consistent with explicit), or add prominent WARNING log in the no-op path. Option (a) preferred for consistency.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: grep -n 'custom Dockerfile' src/lib/onboard/dockerfile-patch-corporate-ca.test.ts
  • Missing regression test: Test that fallback env var with missing ARG in custom Dockerfile produces expected behavior (warn or throw).
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: grep -n 'custom Dockerfile' src/lib/onboard/dockerfile-patch-corporate-ca.test.ts.
  • Evidence: src/lib/onboard/dockerfile-patch.ts:388 src/lib/onboard/dockerfile-patch-corporate-ca.test.ts lines 140-155 (explicit throw test only) Prior review PRA-4 at headSha 16a8e53

PRA-5 Resolve/justify — Single-use anchor directory constants add indirection

  • Location: src/lib/onboard/corporate-ca-host-anchors.ts:29
  • Category: architecture
  • Problem: DEFAULT_HOST_ANCHOR_SPECS and anchorExtensionsFor() add indirection for two directory paths that are only used once each in the same file. Inlining would simplify without loss of clarity.
  • Impact: Unnecessary abstraction layer for static, single-use configuration; increases cognitive load for readers.
  • Recommended action: Inline the two directory paths (/usr/local/share/ca-certificates with .crt, /etc/pki/ca-trust/source/anchors with .pem/.crt/.cer) directly into the exported array and collection function; remove DEFAULT_HOST_ANCHOR_SPECS and anchorExtensionsFor().
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: grep -n 'DEFAULT_HOST_ANCHOR_SPECS\|anchorExtensionsFor' src/lib/onboard/corporate-ca-host-anchors.ts
  • Missing regression test: None — simplification; existing tests cover behavior.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: grep -n 'DEFAULT_HOST_ANCHOR_SPECS\|anchorExtensionsFor' src/lib/onboard/corporate-ca-host-anchors.ts.
  • Evidence: src/lib/onboard/corporate-ca-host-anchors.ts:29-37 Prior review PRA-5 at headSha 16a8e53

PRA-6 Resolve/justify — Missing regression test for fallback CA + missing ARG on custom Dockerfile

  • Location: src/lib/onboard/dockerfile-patch-corporate-ca.test.ts:180
  • Category: correctness
  • Problem: The asymmetric behavior (explicit throws, fallback silently no-ops) is documented but lacks a test asserting the fallback path produces a warning (or throws) when ARG NEMOCLAW_CORPORATE_CA_B64 is absent. Current test at line 180 only confirms no-op without verifying operator visibility.
  • Impact: Operator using custom Dockerfile with REQUESTS_CA_BUNDLE/CURL_CA_BUNDLE/SSL_CERT_FILE gets silent TLS failure at runtime — no build-time signal.
  • Recommended action: Add a test that sets a fallback env var (e.g., REQUESTS_CA_BUNDLE) with a valid CA, uses a Dockerfile without ARG NEMOCLAW_CORPORATE_CA_B64, and asserts a WARNING is logged (or error thrown per F-004 resolution).
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: grep -n 'fallback.*missing ARG\|custom Dockerfile.*warn' src/lib/onboard/dockerfile-patch-corporate-ca.test.ts
  • Missing regression test: Test that fallback env var with missing ARG in custom Dockerfile produces a WARNING log (consistent with explicit fail-loud) so operator knows CA was not baked.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: grep -n 'fallback.*missing ARG\|custom Dockerfile.*warn' src/lib/onboard/dockerfile-patch-corporate-ca.test.ts.
  • Evidence: src/lib/onboard/dockerfile-patch.ts:388-390 (asymmetric logic) src/lib/onboard/dockerfile-patch-corporate-ca.test.ts:180 (no-op test only) docs/reference/troubleshooting.mdx:1253-1255 (custom Dockerfile contract docs)

PRA-7 Improvement — Custom Dockerfile contract could explicitly recommend ARG adoption

  • Location: docs/reference/troubleshooting.mdx:1253
  • Category: docs
  • Problem: The troubleshooting docs explain the custom Dockerfile silent no-op but do not explicitly recommend adding ARG NEMOCLAW_CORPORATE_CA_B64 and the decode RUN block to custom Dockerfiles. Operators may not realize the fix is a 2-line Dockerfile addition.
  • Impact: Operators with custom Dockerfiles may not know how to enable corporate CA import, leading to prolonged silent TLS failures.
  • Suggested action: Add a code snippet showing the exact ARG + RUN block to add to custom Dockerfiles (mirroring the managed Dockerfiles).
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: grep -A 10 'Custom Dockerfile contract' docs/reference/troubleshooting.mdx
  • Missing regression test: None — documentation improvement.
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: docs/reference/troubleshooting.mdx:1253-1255 (current contract text)

PRA-8 Resolve/justify — Missing cross-entrypoint merge equivalence regression test

  • Location: test/corporate-ca-runtime-merge.test.ts:1
  • Category: security
  • Problem: Both entrypoints' merge_corporate_proxy_ca blocks are tested in isolation, but no test asserts they produce identical merged bundle behavior given the same inputs. Drift between OpenClaw and Hermes merge logic would go undetected, creating divergent sandbox trust behavior.
  • Impact: Security hardening or bug fix applied to one entrypoint's merge logic may not be reflected in the other, creating divergent sandbox trust behavior.
  • Recommended action: Add a cross-entrypoint equivalence test in corporate-ca-runtime-merge.test.ts that runs both entrypoints' extracted merge blocks against identical OpenShell + corporate CA inputs and asserts byte-for-byte identical merged bundle and env var exports.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: grep -n 'cross-entrypoint\|equivalence' test/corporate-ca-runtime-merge.test.ts
  • Missing regression test: Test that OpenClaw and Hermes merge blocks produce identical merged bundle content and CA env var exports for the same input CA and OpenShell bundle.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: grep -n 'cross-entrypoint\|equivalence' test/corporate-ca-runtime-merge.test.ts.
  • Evidence: test/corporate-ca-runtime-merge.test.ts tests OpenClaw and Hermes blocks separately scripts/nemoclaw-start.sh:1380 and agents/hermes/start.sh:1380 contain duplicated merge logic Security Category 8/9: missing equivalence test for trust-boundary code

PRA-9 Resolve/justify — Missing cross-Dockerfile decode equivalence regression test

  • Location: test/corporate-ca-dockerfile-decode.test.ts:40
  • Category: security
  • Problem: Both Dockerfiles' corporate CA decode/validate RUN blocks are parametrized and tested in isolation, but no test asserts they produce identical exit codes, error messages, and baked output for the same test vectors (valid cert, invalid base64, non-certificate PEM, cert request, oversized bundle).
  • Impact: Validation logic drift between Dockerfiles would go undetected; one image could accept malformed input that the other rejects, creating inconsistent build-time trust behavior.
  • Recommended action: Add cross-Dockerfile equivalence assertions in corporate-ca-dockerfile-decode.test.ts that run both Dockerfiles' extracted decode blocks against identical test vectors and assert identical exit codes, stderr messages, and baked corporate-ca.pem content.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: grep -n 'cross-Dockerfile\|equivalence\|identical' test/corporate-ca-dockerfile-decode.test.ts
  • Missing regression test: Test that both Dockerfiles' decode blocks produce identical results for the same input vectors.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: grep -n 'cross-Dockerfile\|equivalence\|identical' test/corporate-ca-dockerfile-decode.test.ts.
  • Evidence: test/corporate-ca-dockerfile-decode.test.ts parametrizes over both Dockerfiles but has no cross-equivalence assertions Dockerfile:845 and agents/hermes/Dockerfile:225 contain duplicated decode block Security Category 8/9: missing equivalence test for trust-boundary code

PRA-10 Resolve/justify — Missing test for host anchor dir path-list override

  • Location: src/lib/onboard/corporate-ca-host-anchors.test.ts:275
  • Category: tests
  • Problem: hostAnchorDirsFromEnv() splits on path.delimiter but no test covers multiple directories in NEMOCLAW_CORPORATE_CA_ANCHOR_DIRS (e.g., '/dir1:/dir2'). Only single-dir override and empty disable are tested.
  • Impact: Operators using multiple custom anchor directories may have only the first scanned; no regression guard for path-list parsing.
  • Recommended action: Add a test setting NEMOCLAW_CORPORATE_CA_ANCHOR_DIRS to a colon-delimited list with valid CAs in different directories, asserting the first yielding a bundle wins.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: grep -n 'path.delimiter\|multiple.*anchor' src/lib/onboard/corporate-ca-host-anchors.test.ts
  • Missing regression test: Test that NEMOCLAW_CORPORATE_CA_ANCHOR_DIRS='dir1:dir2' scans both directories in order and returns the first valid bundle.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: grep -n 'path.delimiter\|multiple.*anchor' src/lib/onboard/corporate-ca-host-anchors.test.ts.
  • Evidence: src/lib/onboard/corporate-ca-host-anchors.ts:218-223 (hostAnchorDirsFromEnv) src/lib/onboard/corporate-ca-host-anchors.test.ts:248-257 (single override test only)

PRA-11 Resolve/justify — Missing test for literal /etc/ssl/certs probe gating

  • Location: src/lib/onboard/corporate-ca-host-anchors.test.ts:269
  • Category: tests
  • Problem: The literal /etc/ssl/certs probe runs only when host anchor scanning is not disabled AND no anchor directory yielded a CA. Tests cover disabled-by-env and anchor-yields-nothing → probe runs, but not anchor-yields-CA → probe skipped.
  • Impact: If anchor scan finds a CA but literal probe has different behavior (e.g., different certs), the precedence logic is untested.
  • Recommended action: Add a test where host anchor directory yields a valid CA and literal ssl-certs dir also has valid CAs, asserting the anchor CA wins and literal probe is not used.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: grep -n 'literal.*anchor\|anchor.*literal' src/lib/onboard/corporate-ca-host-anchors.test.ts
  • Missing regression test: Test that anchor CA takes precedence over literal /etc/ssl/certs CAs when both are present.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: grep -n 'literal.*anchor\|anchor.*literal' src/lib/onboard/corporate-ca-host-anchors.test.ts.
  • Evidence: src/lib/onboard/corporate-ca.ts:88-96 (resolveCorporateCa gating logic) src/lib/onboard/corporate-ca-host-anchors.test.ts:207-247 (literal probe tests, no anchor-precedence test)

Workflow run details

This is an automated, non-binding review; it still expects maintainers and agents to respond to each required or warning item. Treat suggestions as current-PR improvements when they touch changed code; defer only with maintainer rationale or a linked follow-up. A human maintainer must make the final merge decision.

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

PR Review Advisor — No blocking findings

Merge posture: No blocking advisor findings
Primary next action: Add or justify PRA-T1 and any related test follow-ups.
Open items: 0 required · 0 warnings · 0 suggestions · 5 test follow-ups
Since last review: 1 prior item resolved · 0 still apply · 0 new items found

Action checklist

  • PRA-T1 Add or justify test follow-up: Runtime validation
  • PRA-T2 Add or justify test follow-up: Runtime validation
  • PRA-T3 Add or justify test follow-up: Runtime validation
  • PRA-T4 Add or justify test follow-up: Runtime validation
  • PRA-T5 Add or justify test follow-up: Runtime validation
Test follow-ups to resolve or justify

If these cover changed behavior, prefer adding them in this PR; otherwise state why existing coverage is enough or link the follow-up.

  • PRA-T1 Runtime validation — Run the `cloud-onboard` E2E job for Installer and platform changes must work on a clean supported host with the pinned runtime dependencies. Matched files: `agents/hermes/Dockerfile`, `Dockerfile`, `test/e2e/live/cloud-onboard.test.ts`.. Deterministic regression risks require live validation: lifecycle-state, platform-install. Checked-in unit, shell, TLS, and live-E2E tests cover the changed behavior and every riskPlan invariant, but Dockerfile/entrypoint/onboarding changes still require the deterministic live validation floor. This review did not observe or claim job execution.
  • PRA-T2 Runtime validation — Run the `cloud-onboard` E2E job for the current head SHA to validate the public installer path, clean-host platform install behavior, sandbox health, and explicit corporate-CA merge probe.. Deterministic regression risks require live validation: lifecycle-state, platform-install. Checked-in unit, shell, TLS, and live-E2E tests cover the changed behavior and every riskPlan invariant, but Dockerfile/entrypoint/onboarding changes still require the deterministic live validation floor. This review did not observe or claim job execution.
  • PRA-T3 Runtime validation — Run the `onboard-repair` E2E job for Onboarding and sandbox state must converge across persisted metadata, reported status, and the live runtime. Matched files: `src/lib/onboard/__test-helpers__/corporate-ca-fixtures.ts`, `src/lib/onboard/corporate-ca-env.ts`, `src/lib/onboard/corporate-ca-host-anchors.ts`, `src/lib/onboard/corporate-ca-policy.ts`, `src/lib/onboard/corporate-ca-types.ts`.. Deterministic regression risks require live validation: lifecycle-state, platform-install. Checked-in unit, shell, TLS, and live-E2E tests cover the changed behavior and every riskPlan invariant, but Dockerfile/entrypoint/onboarding changes still require the deterministic live validation floor. This review did not observe or claim job execution.
  • PRA-T4 Runtime validation — Run the `onboard-repair` E2E job for the current head SHA to validate missing-sandbox repair/resume convergence and `REQUESTS_CA_BUNDLE` fallback corporate-CA merge behavior.. Deterministic regression risks require live validation: lifecycle-state, platform-install. Checked-in unit, shell, TLS, and live-E2E tests cover the changed behavior and every riskPlan invariant, but Dockerfile/entrypoint/onboarding changes still require the deterministic live validation floor. This review did not observe or claim job execution.
  • PRA-T5 Runtime validation — Run the `onboard-resume` E2E job for Onboarding and sandbox state must converge across persisted metadata, reported status, and the live runtime. Matched files: `src/lib/onboard/__test-helpers__/corporate-ca-fixtures.ts`, `src/lib/onboard/corporate-ca-env.ts`, `src/lib/onboard/corporate-ca-host-anchors.ts`, `src/lib/onboard/corporate-ca-policy.ts`, `src/lib/onboard/corporate-ca-types.ts`.. Deterministic regression risks require live validation: lifecycle-state, platform-install. Checked-in unit, shell, TLS, and live-E2E tests cover the changed behavior and every riskPlan invariant, but Dockerfile/entrypoint/onboarding changes still require the deterministic live validation floor. This review did not observe or claim job execution.

Workflow run details

This is an automated, non-binding review; it still expects maintainers and agents to respond to each required or warning item. Treat suggestions as current-PR improvements when they touch changed code; defer only with maintainer rationale or a linked follow-up. A human maintainer must make the final merge decision.

Comment thread src/lib/onboard/corporate-ca.ts Fixed
Comment thread test/corporate-ca-tls-e2e.test.ts Fixed
Move the branching openssl/TLS/shell-block helpers out of the corporate
CA .test.ts files into test/helpers/corporate-ca-support.ts so the
changed test files add no if statements (codebase-growth-guardrails
keeps test bodies linear).

Signed-off-by: Yimo Jiang <yimoj@nvidia.com>
export function httpsGetStatus(port: number, caBundlePath: string): Promise<number> {
return new Promise((resolve, reject) => {
const req = https.get(
{ host: "127.0.0.1", port, path: "/", ca: fs.readFileSync(caBundlePath) },

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This is a test-only false positive. corporate-ca-support.ts is a Vitest support helper (never shipped); httpsGetStatus makes a loopback (127.0.0.1) request to an in-test https.createServer and reads the ca bundle from a temp file the test itself generated via openssl. There is no untrusted file data or real outbound egress — the whole point is to verify TLS against a locally-generated corporate CA. Safe to dismiss as test infrastructure.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
test/corporate-ca-tls-e2e.test.ts (1)

1-224: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

CI guardrail failure: added if statements in a test file.

Pipeline reports the file's if count grew from 0 to 5 at PR head (Lines 107, 108, 132, 170, and one more). This is flagged by the Codebase Growth Guardrails check and needs to be resolved or explicitly justified/waived before merge — several of these (e.g., the CI-vs-local skip guard at Lines 107-113) are legitimate environment-detection logic, but the guardrail still needs to pass or be exempted.

🤖 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/corporate-ca-tls-e2e.test.ts` around lines 1 - 224, The test file now
introduces several new if statements that trigger the Codebase Growth Guardrails
check. Refactor the environment/setup flow in corporate-ca-tls-e2e.test.ts by
moving the conditional logic out of the test body into small helper functions or
existing hooks around trySetup, afterAll, and describe.skipIf so the file’s if
count does not increase, or explicitly request a guardrail waiver if the logic
must remain as-is.

Source: Pipeline failures

🧹 Nitpick comments (4)
test/corporate-ca-runtime-merge.test.ts (1)

63-200: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Test titles mostly missing the required (#6210) suffix.

Only the describe blocks and one it title include the issue reference; the other it() titles (Lines 63, 97, 116, 150) omit it. As per coding guidelines, root-level integration tests under test/ should "use behavior-oriented titles with local issue refs in a final (#1234) suffix."

🤖 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/corporate-ca-runtime-merge.test.ts` around lines 63 - 200, Update the
affected `it()` titles in `corporate-ca-runtime-merge.test.ts` so each
behavior-oriented test name ends with the required `(`#6210`)` suffix; keep the
existing meaning but append the local issue reference consistently to every
top-level test case, including the OpenClaw merge, no-op, Hermes merge, and
connect-session merge tests.

Source: Coding guidelines

test/corporate-ca-tls-e2e.test.ts (1)

198-222: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Most it() titles omit the required issue-ref suffix.

Only "still trusts the OpenShell root through the merged bundle (#6210)" (Line 214) carries the suffix; "verifies a corporate-CA-signed endpoint only after the merge" (Line 198) does not. As per coding guidelines, root-level integration tests under test/ should "use behavior-oriented titles with local issue refs in a final (#1234) suffix."

🤖 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/corporate-ca-tls-e2e.test.ts` around lines 198 - 222, The root-level
integration test title in this block is missing the required local issue-ref
suffix; update the unsuffixed it() description in the corporate CA merge test so
it ends with a behavior-oriented “(`#1234`)” style reference, matching the
existing convention already used by the neighboring it() case in
corporate-ca-tls-e2e.test.ts.

Source: Coding guidelines

docs/reference/troubleshooting.mdx (1)

977-987: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Split multi-sentence lines to one sentence per line.

Lines 977 and 986 each pack 3 sentences onto a single source line, violating the docs formatting convention.

📝 Proposed reflow
-On networks where a corporate proxy in front of the host re-signs external TLS with its own root CA, external endpoints such as `api.telegram.org` fail certificate verification even when the network policy allows the connection. Logs show the request opening (`NET:OPEN ... api.telegram.org:443`) followed by `NET:FAIL`. OpenShell injects only its own L7-proxy CA into the sandbox, so the separate corporate root is missing from the trust path.
+On networks where a corporate proxy in front of the host re-signs external TLS with its own root CA, external endpoints such as `api.telegram.org` fail certificate verification even when the network policy allows the connection.
+Logs show the request opening (`NET:OPEN ... api.telegram.org:443`) followed by `NET:FAIL`.
+OpenShell injects only its own L7-proxy CA into the sandbox, so the separate corporate root is missing from the trust path.
-`REQUESTS_CA_BUNDLE`, `CURL_CA_BUNDLE`, and `SSL_CERT_FILE` are also honored as fallbacks (in that order) when `NEMOCLAW_CORPORATE_CA_BUNDLE` is unset, so an environment that already exports one of those for the corporate proxy works without extra configuration. The bundle must be a readable, non-symlink, non-world-writable PEM file that contains at least one certificate. To disable the import entirely, set `NEMOCLAW_CORPORATE_CA_IMPORT=0`.
+`REQUESTS_CA_BUNDLE`, `CURL_CA_BUNDLE`, and `SSL_CERT_FILE` are also honored as fallbacks (in that order) when `NEMOCLAW_CORPORATE_CA_BUNDLE` is unset, so an environment that already exports one of those for the corporate proxy works without extra configuration.
+The bundle must be a readable, non-symlink, non-world-writable PEM file that contains at least one certificate.
+To disable the import entirely, set `NEMOCLAW_CORPORATE_CA_IMPORT=0`.

Based on learnings and coding guidelines: "Keep one sentence per line in Markdown and MDX source files."

🤖 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 `@docs/reference/troubleshooting.mdx` around lines 977 - 987, The
troubleshooting section in the MDX source has multiple sentences on the same
line, which violates the one-sentence-per-line convention. Reflow the prose in
the affected paragraph so each sentence is on its own source line, including the
sentences describing the proxy/TLS behavior and the fallback environment
variables, while keeping the existing wording and structure intact.

Source: Coding guidelines

src/lib/onboard/dockerfile-patch.test.ts (1)

160-165: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Restore env vars after test instead of leaving them deleted.

Test 1 correctly restores NEMOCLAW_CORPORATE_CA_BUNDLE in a finally, but this test permanently deletes REQUESTS_CA_BUNDLE, CURL_CA_BUNDLE, and SSL_CERT_FILE with no save/restore, unlike the careful pattern used elsewhere. If a prior test or the host environment set any of these, this silently strips them for the rest of the suite run.

🧪 Proposed fix to save/restore prior values
-  it("leaves NEMOCLAW_CORPORATE_CA_B64 empty when no corporate CA is configured", () => {
-    delete process.env.NEMOCLAW_CORPORATE_CA_BUNDLE;
-    delete process.env.REQUESTS_CA_BUNDLE;
-    delete process.env.CURL_CA_BUNDLE;
-    delete process.env.SSL_CERT_FILE;
+  it("leaves NEMOCLAW_CORPORATE_CA_B64 empty when no corporate CA is configured", () => {
+    const savedEnv = {
+      NEMOCLAW_CORPORATE_CA_BUNDLE: process.env.NEMOCLAW_CORPORATE_CA_BUNDLE,
+      REQUESTS_CA_BUNDLE: process.env.REQUESTS_CA_BUNDLE,
+      CURL_CA_BUNDLE: process.env.CURL_CA_BUNDLE,
+      SSL_CERT_FILE: process.env.SSL_CERT_FILE,
+    };
+    for (const key of Object.keys(savedEnv)) delete process.env[key];
+    try {

Wrap the remainder of the test body and restore savedEnv entries in a finally.

🤖 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 `@src/lib/onboard/dockerfile-patch.test.ts` around lines 160 - 165, The test
that exercises dockerfileWith for the “no corporate CA configured” case is
mutating REQUESTS_CA_BUNDLE, CURL_CA_BUNDLE, and SSL_CERT_FILE without restoring
their previous values. Save the original values at the start of the test, run
the existing assertions using dockerfileWith, and restore each env var in a
finally block the same way the neighboring NEMOCLAW_CORPORATE_CA_BUNDLE test
does so the suite does not leak environment changes.
🤖 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 `@agents/hermes/start.sh`:
- Around line 1406-1440: `merge_corporate_proxy_ca` in `agents/hermes/start.sh`
can silently build an incomplete trust bundle because `cat` failures are
swallowed and the merged CA is still exported. Update the function so any
write/read failure while assembling `$_merged` causes the merge to fail closed:
check each append step in `merge_corporate_proxy_ca` and only set
`SSL_CERT_FILE`, `NODE_EXTRA_CA_CERTS`, and `_NEMOCLAW_CORPORATE_CA_MERGED`
after a fully successful merge. Keep the existing behavior of appending the
corporate CA to the base bundle, but do not mark the merge successful if the
bundle could not be written completely.

In `@scripts/nemoclaw-start.sh`:
- Around line 2726-2751: The merge_corporate_proxy_ca helper is repointing
CA-related environment variables even when building the merged bundle fails
partway. In merge_corporate_proxy_ca, stop suppressing the append failures for
the base bundle and corporate CA, verify the merged file contains both inputs
successfully, and only then export SSL_CERT_FILE, CURL_CA_BUNDLE,
REQUESTS_CA_BUNDLE, GIT_SSL_CAINFO, NODE_EXTRA_CA_CERTS and set
_NEMOCLAW_CORPORATE_CA_MERGED. If either append fails, leave the original trust
settings untouched and fail closed.

In `@src/lib/onboard/corporate-ca.ts`:
- Around line 68-102: `validateCorporateCaFile` has a TOCTOU race because it
validates `filePath` with `fs.lstatSync` and then re-reads the same path with
`fs.readFileSync`. Update the function to validate and read through a single
file descriptor so the checked file is the one whose contents are loaded, while
keeping the existing `lstatSync` symlink rejection and the current
size/permission/content checks in `validateCorporateCaFile` and
`CorporateCaValidationError` handling.

In `@test/corporate-ca-runtime-merge.test.ts`:
- Around line 63-148: Add a Hermes negative-path test alongside
merge_corporate_proxy_ca that mirrors the OpenClaw no-op case: when the
corporate CA file is absent, the Hermes merge block should leave SSL_CERT_FILE
and NODE_EXTRA_CA_CERTS unchanged and should not set
_NEMOCLAW_CORPORATE_CA_MERGED. Reuse the existing test helpers and symbols in
corporate-ca-runtime-merge.test.ts such as HERMES_START, sliceBlock, mergeBlock,
and runShell to build the scenario and assert the merge marker stays unset while
the original CA path remains intact.
- Around line 1-201: The Codebase Growth Guardrails check is failing because
sliceBlock introduced a new explicit if guard. Refactor sliceBlock so the
start/end validation no longer uses an added if statement, or move that
validation into an existing helper already counted in this file; keep the same
error behavior while preserving the runtime extraction logic used by mergeBlock
and the Hermes/OpenClaw tests.

---

Outside diff comments:
In `@test/corporate-ca-tls-e2e.test.ts`:
- Around line 1-224: The test file now introduces several new if statements that
trigger the Codebase Growth Guardrails check. Refactor the environment/setup
flow in corporate-ca-tls-e2e.test.ts by moving the conditional logic out of the
test body into small helper functions or existing hooks around trySetup,
afterAll, and describe.skipIf so the file’s if count does not increase, or
explicitly request a guardrail waiver if the logic must remain as-is.

---

Nitpick comments:
In `@docs/reference/troubleshooting.mdx`:
- Around line 977-987: The troubleshooting section in the MDX source has
multiple sentences on the same line, which violates the one-sentence-per-line
convention. Reflow the prose in the affected paragraph so each sentence is on
its own source line, including the sentences describing the proxy/TLS behavior
and the fallback environment variables, while keeping the existing wording and
structure intact.

In `@src/lib/onboard/dockerfile-patch.test.ts`:
- Around line 160-165: The test that exercises dockerfileWith for the “no
corporate CA configured” case is mutating REQUESTS_CA_BUNDLE, CURL_CA_BUNDLE,
and SSL_CERT_FILE without restoring their previous values. Save the original
values at the start of the test, run the existing assertions using
dockerfileWith, and restore each env var in a finally block the same way the
neighboring NEMOCLAW_CORPORATE_CA_BUNDLE test does so the suite does not leak
environment changes.

In `@test/corporate-ca-runtime-merge.test.ts`:
- Around line 63-200: Update the affected `it()` titles in
`corporate-ca-runtime-merge.test.ts` so each behavior-oriented test name ends
with the required `(`#6210`)` suffix; keep the existing meaning but append the
local issue reference consistently to every top-level test case, including the
OpenClaw merge, no-op, Hermes merge, and connect-session merge tests.

In `@test/corporate-ca-tls-e2e.test.ts`:
- Around line 198-222: The root-level integration test title in this block is
missing the required local issue-ref suffix; update the unsuffixed it()
description in the corporate CA merge test so it ends with a behavior-oriented
“(`#1234`)” style reference, matching the existing convention already used by the
neighboring it() case in corporate-ca-tls-e2e.test.ts.
🪄 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: 6561ae0c-4297-4e8e-b95e-68be73060d9c

📥 Commits

Reviewing files that changed from the base of the PR and between 9ebc888 and 41c8fe7.

📒 Files selected for processing (11)
  • Dockerfile
  • agents/hermes/Dockerfile
  • agents/hermes/start.sh
  • docs/reference/troubleshooting.mdx
  • scripts/nemoclaw-start.sh
  • src/lib/onboard/corporate-ca.test.ts
  • src/lib/onboard/corporate-ca.ts
  • src/lib/onboard/dockerfile-patch.test.ts
  • src/lib/onboard/dockerfile-patch.ts
  • test/corporate-ca-runtime-merge.test.ts
  • test/corporate-ca-tls-e2e.test.ts

Comment thread agents/hermes/start.sh
Comment thread scripts/nemoclaw-start.sh
Comment thread src/lib/onboard/corporate-ca.ts Outdated
Comment thread test/corporate-ca-runtime-merge.test.ts
Comment thread test/corporate-ca-runtime-merge.test.ts Outdated

@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/helpers/corporate-ca-support.ts (1)

38-67: 🔒 Security & Privacy | 🔵 Trivial | 💤 Low value

Dynamic execSync calls flagged by static analysis.

All interpolated values (cn, keyOut, certOut, caCert, caKey) are hardcoded literals from call sites, so there's no current injection vector. Still, using execFileSync/array-args here would remove the pattern entirely and avoid relying on call-site discipline if these helpers are reused with less-trusted inputs later.

🤖 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/helpers/corporate-ca-support.ts` around lines 38 - 67, The openssl
helper commands in opensslReqX509 and signLeaf are built with interpolated
strings that static analysis flags as dynamic exec usage. Replace the execSync
shell-string calls with a non-shell process invocation pattern such as
execFileSync plus explicit argument arrays, and keep the same behavior for
generating the CA, CSR, and signed leaf certificate while preserving the
existing path.join-based file targets.

Source: Linters/SAST tools

🤖 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/helpers/corporate-ca-support.ts`:
- Around line 38-67: The openssl helper commands in opensslReqX509 and signLeaf
are built with interpolated strings that static analysis flags as dynamic exec
usage. Replace the execSync shell-string calls with a non-shell process
invocation pattern such as execFileSync plus explicit argument arrays, and keep
the same behavior for generating the CA, CSR, and signed leaf certificate while
preserving the existing path.join-based file targets.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: f7f8ff80-1825-49c8-8548-cf26c3c66bea

📥 Commits

Reviewing files that changed from the base of the PR and between 41c8fe7 and 379a3dc.

📒 Files selected for processing (3)
  • test/corporate-ca-runtime-merge.test.ts
  • test/corporate-ca-tls-e2e.test.ts
  • test/helpers/corporate-ca-support.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/corporate-ca-runtime-merge.test.ts

Address PR review advisor findings on the corporate-proxy CA import:

- corporate-ca.ts: open the candidate once with O_NOFOLLOW and validate
  the opened fd via fstat + read-from-fd (closes the validate-then-reopen
  TOCTOU); reduce the size cap to 128 KiB, add a certificate-count cap,
  and structurally validate the leading block as X.509 so a full OS trust
  store or corrupt PEM is rejected.
- dockerfile-patch.ts: fail loudly when NEMOCLAW_CORPORATE_CA_BUNDLE is
  set explicitly but the staged Dockerfile lacks the ARG; log which host
  source (env + path) is baked so a fallback import is never silent.
- nemoclaw-start.sh / hermes start.sh: build the merged bundle in a
  mktemp sibling with every write checked, then atomically rename into
  place; only export the CA env + marker after the complete bundle exists
  (no partial/predictable-path bundle, no success-on-append-failure).
- Dockerfile / hermes Dockerfile: decode with `base64 --decode`.
- Extract the dockerfile-patch corporate-CA tests into their own focused
  file; document explicit-first usage, host trust-store guidance, and the
  fallback silent-skip behavior in troubleshooting.

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

yimoj commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed the PR Review Advisor findings in 5f99716:

Fixed

  • TOCTOU (PRA-8): validateCorporateCaFile opens once with O_NOFOLLOW, validates via fstat, reads from the same fd.
  • Non-atomic / predictable-path / success-on-append-failure merged bundle (PRA-6, nemotron PRA-7): merge now builds in a mktemp sibling, checks every write, atomically renames, and exports the CA env + marker only on success.
  • Explicit CA silently no-ops when ARG missing (PRA-7): explicit NEMOCLAW_CORPORATE_CA_BUNDLE + managed Dockerfile lacking the ARG now throws; fallback + custom Dockerfile stays a no-op.
  • Silent fallback baking (PRA-9, nemotron PRA-4): onboarding logs the source env+path; docs document fallback silent-skip and recommend the explicit var.
  • Size/cert bounds + structure (nemotron PRA-5, PRA-8): size cap 512 KiB→128 KiB, added a certificate-count cap, and structural X509Certificate parse of the leading block.
  • base64 -d portability (nemotron PRA-10): now base64 --decode.
  • Monolith test growth (nemotron PRA-6, Required): dockerfile-patch corporate-CA tests extracted to dockerfile-patch-corporate-ca.test.ts.

Justified

  • Host /etc/ssl/certs auto-detection (PRA-5, Required): intentionally not auto-scanned — baking a full OS trust store imports broad unrelated roots (the trust-bloat the advisors themselves flag). Supported explicitly by pointing NEMOCLAW_CORPORATE_CA_BUNDLE at it (now documented); caps reject a full store. Scope narrowed via docs + tests.
  • Architecture "source-of-truth" placeholders (PRA-1..4): not an upstream workaround — NemoClaw owns sandbox image build + entrypoint (the correct source boundary). OpenShell CA is appended, never replaced, and the #1828-preservation is covered by a real TLS test.
  • Fallback silent-skip is intended (explicit stays fail-loud); failing onboarding on a stale ambient REQUESTS_CA_BUNDLE would break users who never asked for a corporate CA.

Test follow-ups (PRA-T)*: runtime is covered by executed tests — corporate-ca-runtime-merge.test.ts runs the shipped merge blocks for both entrypoints, and corporate-ca-tls-e2e.test.ts runs the real merge block against an openssl-signed corporate leaf (verifies only post-merge; OpenShell root stays trusted).

The earlier build-typecheck failure was an unrelated pre-existing flake in test/package-contract/cli/config-set-cli-dispatch.test.ts, not this change.

Second review round on the corporate-proxy CA import:

- hermes start.sh: the merge now exports CURL_CA_BUNDLE, REQUESTS_CA_BUNDLE,
  and GIT_SSL_CAINFO explicitly. Previously it set only SSL_CERT_FILE and
  relied on the downstream ${VAR:-…} defaulting, which kept an
  OpenShell-preset value pointing at the OpenShell-only bundle so Hermes
  curl/python/git would not trust the corporate CA after a merge.
- corporate-ca.ts: structurally validate every PEM block as X.509, not just
  the first, so a valid leading cert cannot smuggle in a corrupt later block.
- troubleshooting.mdx: stop suggesting NEMOCLAW_CORPORATE_CA_BUNDLE point at
  the full OS trust store (which the size/cert caps reject); tell users to
  export just their corporate root/intermediates into a PEM.
- Tests: assert the Hermes merge overrides preset CURL/REQUESTS/GIT, cover a
  later-block structural rejection, and add a merge-failure negative test
  proving the entrypoint bails without exporting when the bundle can't be
  written.

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

yimoj commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator Author

Second review round (advisors both now report 0 required fixes, merge_after_fixes). Addressed in 03c0960:

Fixed

  • Hermes kept stale CURL/REQUESTS/GIT CA env (nemotron PRA-5): the Hermes merge now exports CURL_CA_BUNDLE/REQUESTS_CA_BUNDLE/GIT_SSL_CAINFO explicitly instead of relying on ${VAR:-…}, which kept an OpenShell-preset value. Test now asserts the merge overrides preset values.
  • Doc pointed at a full OS trust store that validation rejects (nemotron PRA-6): troubleshooting now tells users to export just their corporate root/intermediates into a PEM; removed the /etc/ssl/certs/ca-certificates.crt example.
  • Only first PEM block structurally validated (advisor PRA-4 / nemotron PRA-7/PRA-9): now validate every block as X.509; added a later-block rejection test.
  • No negative coverage of merge failure (nemotron PRA-8): added a test proving the entrypoint bails without exporting (OpenShell-only trust intact) when the bundle can't be written.

Justified

  • Predictable final /tmp/nemoclaw-ca-bundle.pem (advisor PRA-5): the bundle is built via mktemp + atomic rename and is 0444; the stable final path is required so connect-session env can reference it, and the sandbox /tmp is container-private.
  • Fallback silent-skip / ambient-trust / architecture "source-of-truth" items: unchanged from the round-1 dispositions — explicit NEMOCLAW_CORPORATE_CA_BUNDLE stays fail-loud, fallbacks warn+skip (failing onboarding on a stale ambient var would break users who never asked for a corporate CA), NemoClaw owns the sandbox image + entrypoint (the correct source boundary), and OpenShell CA is appended, never replaced.
  • base64 --decode / test execFileSync argv / TLS skipIf openssl: --decode is supported on the Debian sandbox base; the openssl test helpers take only mkdtemp paths (no injection); the TLS e2e throws (not skips) in CI when openssl is absent.

The earlier build-typecheck failure was an unrelated pre-existing flake (config-set-cli-dispatch.test.ts); it passed on re-run.

@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 current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/corporate-ca-runtime-merge.test.ts`:
- Around line 144-164: The failure-mode coverage is incomplete because this test
only exercises mergeBlock(OPENCLAW_START, ...) and not the equivalent Hermes
path. Add a matching negative test for agents/hermes/start.sh that uses the
Hermes mergeBlock flow and asserts the same bail-out behavior when the merged
bundle cannot be written:
SSL_CERT_FILE/CURL_CA_BUNDLE/REQUESTS_CA_BUNDLE/GIT_SSL_CAINFO stay unchanged,
the merge marker is not set, and the merged file does not exist. Keep the test
structure parallel to the existing corporate-ca-runtime-merge.test case so both
OpenClaw and Hermes merge implementations are covered symmetrically.
- Line 144: The test title in the root-level integration suite needs a
behavior-oriented issue-ref suffix; update the `it(...)` description in
`corporate-ca-runtime-merge.test.ts` to keep the behavior focus and end with a
local issue reference in the required `(`#1234`)` format. Use the existing test
case name as the anchor and adjust only the title string so it matches the repo
convention for `test/**/*.test.ts` cases.
🪄 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: fcd87a35-1c60-4f33-9783-6ee6b8ce7ba4

📥 Commits

Reviewing files that changed from the base of the PR and between 5f99716 and 03c0960.

📒 Files selected for processing (5)
  • agents/hermes/start.sh
  • docs/reference/troubleshooting.mdx
  • src/lib/onboard/corporate-ca.test.ts
  • src/lib/onboard/corporate-ca.ts
  • test/corporate-ca-runtime-merge.test.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/lib/onboard/corporate-ca.test.ts
  • docs/reference/troubleshooting.mdx
  • agents/hermes/start.sh
  • src/lib/onboard/corporate-ca.ts

Comment thread test/corporate-ca-runtime-merge.test.ts Outdated
Comment thread test/corporate-ca-runtime-merge.test.ts Outdated
@yimoj

yimoj commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator Author

Round-3 advisor re-review (head 03c0960): both advisors are merge_after_fixes; all CI checks are green. One advisor flags a single "required" item, PRA-4 (host /etc/ssl/certs/ detection) — justified below; the rest are non-binding resolve/justify.

PRA-4 — host /etc/ssl/certs/ detection (scope decision): The advisor's own required-action allows justifying the scope ("Keep the full-OS-trust-store rejection if broad trust-store baking remains intentionally unsupported"). That is the chosen design:

  • The reporter's actual repro sets REQUESTS_CA_BUNDLE/CURL_CA_BUNDLE — both fully supported, so the reported Telegram NET:FAIL is resolved.
  • Auto-scanning /etc/ssl/certs/ca-certificates.crt cannot identify which of ~140 roots is "the corporate CA"; importing the whole store is exactly the broad-trust-bloat anti-pattern flagged by PRA-5/PRA-9. So auto-detection isn't a safe design.
  • The host trust-store case is supported explicitly: extract your corporate root/intermediates into a PEM and point NEMOCLAW_CORPORATE_CA_BUNDLE at it (now documented).

This is a complete fix for the reported failure, not a workaround, so Fixes #6210 stands.

Other resolve/justify items: ambient-fallback trust is validated + capped + opt-out (NEMOCLAW_CORPORATE_CA_IMPORT=0) + logged; the OpenClaw negative test covers the merge bail-without-export path and the Hermes merge uses byte-identical bail logic; the mirrored ~15-line merge helper is intentional (OpenClaw and Hermes are separate entrypoints with no shared sourced lib) and both are covered by executed tests.

Full disposition record retained locally. A maintainer makes the final merge decision.

- Add the `(#6210)` issue-ref suffix to the runtime-merge test titles per the
  root-level test convention.
- Add a Hermes bail-path negative test mirroring the OpenClaw one, proving the
  Hermes entrypoint also leaves OpenShell-only trust intact and sets no merge
  marker when the merged bundle cannot be written.

Signed-off-by: Yimo Jiang <yimoj@nvidia.com>
…se64

Address the required PR-advisor findings on #6210:

- PRA-3: the corporate-CA bundle merged into a predictable /tmp path
  (/tmp/nemoclaw-ca-bundle.pem). Before the rename, drop any pre-planted
  symlink at the target so we rename into a fresh regular file we own rather
  than through an attacker-controlled link. Applied to both
  scripts/nemoclaw-start.sh and agents/hermes/start.sh.
- PRA-4: the Dockerfile decoded NEMOCLAW_CORPORATE_CA_B64 with `base64
  --decode` without checking the tool exists; add a build-time
  `command -v base64` guard that fails the build with a clear message.
- PRA-5 (justify, no change): GIT_SSL_CAINFO is already propagated to connect
  sessions by the pre-existing #2270 block whenever set (the merge exports it
  to the same bundle), so the corporate-CA propagation loop intentionally
  omits it to avoid a duplicate export; documented inline.

corporate-ca-runtime-merge + corporate-ca-tls-e2e suites pass; shell syntax
verified.

SKIP=test-cli: shell/Dockerfile-only change; the full vitest hook can trip on
pre-existing macOS bash 3.2 noise if this branch predates #6140. CI runs
bash 5.x green.

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
prekshivyas and others added 2 commits July 6, 2026 11:15
rebuild-prepared-recovery.test.ts relied on NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE
being set ambiently by whatever ran earlier. The cli project runs
fileParallelism:false in a single fork, so when rebuild-usage-notice
(which exercises the not-accepted path) runs earlier in a shard it leaves
the flag unset, and the rebuild preflight bails with 'Third-party software
notice was not accepted' — an order-dependent flake that surfaced on
cli-test-shards (2). Set the flag in beforeEach and snapshot/restore it so
the test is deterministic regardless of file order.

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

E2E Target Results — ❌ Some jobs failed

Run: 28813628210
Workflow ref: fix/6210-corporate-proxy-ca
Requested targets: (selector rejected by workflow validation)
Requested jobs: (selector rejected by workflow validation)
Summary: 0 passed, 1 failed, 0 cancelled, 0 skipped

Job Result
generate-matrix ❌ failure

Failed jobs: generate-matrix. Check run artifacts for logs.

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

Re-review for exact head 51ed9da7989b026216dbcd979f0fa3c43680028f narrows the remaining source work:

  • Host-anchor candidate validation must distinguish an actual CA from a valid leaf certificate, or explicitly document and test a legacy leaf-certificate policy. On a supported Ubuntu 24.04 host with empty administrator anchor directories, the current literal /etc/ssl/certs/ diagnostic treats ssl-cert-snakeoil.pem as a possible corporate CA even though its X.509 Basic Constraints report ca=false, producing a noisy false warning on the normal host.
  • Fail closed if chmod 0444 "$_tmp" fails in both scripts/nemoclaw-start.sh and agents/hermes/start.sh. mktemp creates mode 0600; continuing after chmod failure can install/export a merged bundle the later non-root agent cannot read. Add negative coverage for both entrypoints.
  • The cohesion rationale accepted on the earlier, smaller module no longer covers the current 609-line src/lib/onboard/corporate-ca.ts, which now combines trust policy, X.509 validation, env resolution, recursive scanning, literal-directory diagnostics, orchestration, and encoding, alongside duplicated runtime merge implementations. Split shared responsibilities, or obtain an explicit architecture override with a recorded bounded follow-up before merge.

The exact-head live evidence is acknowledged as green: https://github.com/NVIDIA/NemoClaw/actions/runs/29045770653 passed cloud-onboard, onboard-repair, and onboard-resume. No additional live rerun is requested until the source changes above move the head. After those changes, sync current main, resume CodeRabbit, and refresh the advisors and required exact-head live evidence.

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

E2E Target Results — ✅ All selected jobs passed

Run: 29051939394
Workflow ref: fix/6210-corporate-proxy-ca
Requested targets: cloud-onboard,onboard-repair,onboard-resume
Requested jobs: (default — all default-enabled free-standing jobs; explicit-only jobs openshell-gateway-auth-contract, mcp-bridge-dev, hermes-gpu-startup, sandbox-rlimits-connect, and jetson-nvmap-gpu are skipped unless selected)
Summary: 3 passed, 0 failed, 0 cancelled, 0 skipped

Job Result
cloud-onboard ✅ success
onboard-repair ✅ success
onboard-resume ✅ success

Comment thread src/lib/onboard/corporate-ca-host-anchors.ts Fixed
Comment thread src/lib/onboard/corporate-ca-host-anchors.ts Fixed
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

E2E Target Results — ✅ All selected jobs passed

Run: 29052388718
Workflow ref: fix/6210-corporate-proxy-ca
Requested targets: cloud-onboard,onboard-repair,onboard-resume
Requested jobs: (default — all default-enabled free-standing jobs; explicit-only jobs openshell-gateway-auth-contract, mcp-bridge-dev, hermes-gpu-startup, sandbox-rlimits-connect, and jetson-nvmap-gpu are skipped unless selected)
Summary: 3 passed, 0 failed, 0 cancelled, 0 skipped

Job Result
cloud-onboard ✅ success
onboard-repair ✅ success
onboard-resume ✅ success

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

E2E Target Results — ✅ All selected jobs passed

Run: 29053237188
Workflow ref: fix/6210-corporate-proxy-ca
Requested targets: cloud-onboard,onboard-repair,onboard-resume
Requested jobs: (default — all default-enabled free-standing jobs; explicit-only jobs openshell-gateway-auth-contract, mcp-bridge-dev, hermes-gpu-startup, sandbox-rlimits-connect, and jetson-nvmap-gpu are skipped unless selected)
Summary: 3 passed, 0 failed, 0 cancelled, 0 skipped

Job Result
cloud-onboard ✅ success
onboard-repair ✅ success
onboard-resume ✅ success

@prekshivyas

Copy link
Copy Markdown
Collaborator

Update at head 9ac7e4dcfd807bbdf0f4130c6751d26fc773f150:

  • Addressed CV’s latest source asks: leaf certs with basicConstraints CA:FALSE are rejected/ignored, runtime chmod 0444 failure now fails closed for OpenClaw and Hermes with negative coverage, and the corporate-CA implementation/tests are split into focused modules/files.
  • Addressed the follow-up advisor item on host trust-store caps: anchor and literal /etc/ssl/certs candidate sets now warn and skip when scan caps are exceeded instead of silently truncating.
  • Added focused coverage for over-cap host/literal scans, leaf cert rejection, chmod fail-closed runtime behavior, literal /etc/ssl/certs standalone CA import, and the split validation/env/policy/host-anchor paths.

Verification is green:

  • Local focused suite: vitest run src/lib/onboard/corporate-ca-validation.test.ts src/lib/onboard/corporate-ca-env.test.ts src/lib/onboard/corporate-ca-host-anchors.test.ts src/lib/onboard/corporate-ca-policy.test.ts src/lib/onboard/dockerfile-patch-corporate-ca.test.ts test/corporate-ca-runtime-merge.test.ts test/corporate-ca-tls-e2e.test.ts
  • Local typecheck:cli, Biome lint on touched files, git diff --check, and prek pre-push
  • PR CI: https://github.com/NVIDIA/NemoClaw/actions/runs/29053237705
  • Exact-head E2E (cloud-onboard,onboard-repair,onboard-resume): https://github.com/NVIDIA/NemoClaw/actions/runs/29053237188
  • CodeRabbit, GPT advisor, and Nemotron advisor checks are green. Nemotron’s remaining items are warning/justify-only architectural follow-ups around duplicated OpenClaw/Hermes shell/Dockerfile blocks and broader dockerfile-patch cleanup; I’m intentionally deferring those refactors to avoid broad packaging churn in this security fix. Existing tests exercise both entrypoints/Dockerfiles, and the exact-head live evidence is green.

Could you please re-review when you get a chance?

@prekshivyas
prekshivyas requested a review from cv July 9, 2026 22:22
@cv

cv commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

@coderabbitai resume

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Reviews resumed.

@cv

cv commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

Exact-head maintainer/security disposition for 9ac7e4dcfd807bbdf0f4130c6751d26fc773f150:

  • All three remaining human blockers are addressed: X.509 ca=false leaves are rejected, both entrypoints fail closed when the merged bundle cannot be made readable, and the former 609-line corporate-CA module is split into focused policy/env/validation/host-anchor modules.
  • Focused verification is green (90/90 tests, CLI build, CLI typecheck), all ordinary checks are green, all three new commits are Verified, and exact-head cloud-onboard, onboard-repair, and onboard-resume passed in run 29053237188.
  • Nemotron PRA-4 and PRA-6 are stale: the fallback/custom-Dockerfile no-op has an exact test and the custom contract is explicitly documented. PRA-7 is therefore a documented compatibility choice, while the explicit NEMOCLAW_CORPORATE_CA_BUNDLE path remains fail-loud.
  • PRA-1/PRA-2 identify real duplication across separately assembled OpenClaw and Hermes images, but both paths are exercised against the same behavioral/TLS corpus; I accept that bounded maintainability risk here. PRA-10/PRA-11 byte-for-byte cross-path assertions would be useful hardening, not a security or correctness blocker. The corporate-CA source cohesion concern is resolved by the module split; a broader unrelated dockerfile-patch.ts refactor is not required in this feature PR.

I have asked CodeRabbit to resume. No new source change is requested from this review. Please merge current main (the branch is eight commits behind, including the TypeScript 6.0.3 alignment) and let the exact-head checks/advisors settle; if they stay clean, I will replace the existing change request with approval.

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

Exact-head re-review at 16a8e53954658c2a90934df083034dcf92bf9fa6: the main merge is mechanical (9ac7e4dc + ad518b39, empty remerge diff), so the prior production security review remains clean across all nine categories. CodeRabbit also reports no actionable exact-head findings.

Two small current-tree fixes remain:

  • cloud-onboard.test.ts and onboard-repair.test.ts create their corporate-CA fixtures before the skip-capable Docker prerequisite probe, but register cleanup only after that probe. Register fixture cleanup immediately after creation, as onboard-resume.test.ts already does, so a local prerequisite skip cannot leak the temporary CA directory.
  • Remove the unused CORPORATE_CA_EXPLICIT_ENV import identified by CodeQL.

The Nemotron job failure is a provider/tool-result failure, not a required source finding; its partial duplication warnings remain dispositioned as nonblocking. After the two fixes, sync current main (now one docs-only commit ahead), refresh both exact-head advisors/ordinary checks, and rerun the required cloud-onboard, onboard-repair, and onboard-resume targets. The last live run passed at 9ac7e4dc, not this head.

@github-actions

Copy link
Copy Markdown
Contributor

E2E Target Results — ✅ All selected jobs passed

Run: 29103158958
Workflow ref: fix/6210-corporate-proxy-ca
Requested targets: cloud-onboard,onboard-repair,onboard-resume
Requested jobs: (default — all default-enabled free-standing jobs; explicit-only jobs openshell-gateway-auth-contract, mcp-bridge-dev, hermes-gpu-startup, sandbox-rlimits-connect, and jetson-nvmap-gpu are skipped unless selected)
Summary: 3 passed, 0 failed, 0 cancelled, 0 skipped

Job Result
cloud-onboard ✅ success
onboard-repair ✅ success
onboard-resume ✅ success

@prekshivyas

Copy link
Copy Markdown
Collaborator

Exact-head follow-up at 7ddde827: addressed the two remaining CV items from 16a8e539 by registering corporate-CA fixture cleanup immediately after fixture creation in both live tests and removing the unused CORPORATE_CA_EXPLICIT_ENV import. Ordinary CI, CodeRabbit, GPT/Nemotron advisors, E2E recommendation, and exact-head live cloud-onboard,onboard-repair,onboard-resume dispatch are green. Requesting re-review/approval on the updated head.

@cv
cv merged commit 2b84a04 into main Jul 10, 2026
126 checks passed
@cv
cv deleted the fix/6210-corporate-proxy-ca branch July 10, 2026 16:03
cv pushed a commit that referenced this pull request Jul 11, 2026
## Summary

Release-prep documentation for **v0.0.80**. Adds the `## v0.0.80`
section to `docs/about/release-notes.mdx` summarizing user-facing
changes since v0.0.79, each bullet linking to the relevant deeper page.

Produced via `nemoclaw-contributor-update-docs` (pre-tag path): scanned
`v0.0.79..HEAD`, applied the docs skip list (no violations), and
confirmed the 8 commits that already shipped in-PR docs are complete. No
new pages needed.

## Source summary

- #6507 -> `docs/about/release-notes.mdx`: Hermes v0.18 + Slack Block
Kit (rich rendering, digest-pinned base image).
- #6584 / #6616 -> `docs/about/release-notes.mdx`: host-local OpenRouter
runtime attribution adapter (port `11437`,
`NEMOCLAW_OPENROUTER_RUNTIME_ADAPTER_PORT`) and native Deep Agents
`openrouter` provider.
- #6210 / #6292 -> `docs/about/release-notes.mdx`: host corporate proxy
CA import into sandbox trust (`NEMOCLAW_CORPORATE_CA_BUNDLE`,
`NEMOCLAW_CORPORATE_CA_IMPORT`).
- #6624 / #6623 / #6656 -> `docs/about/release-notes.mdx`:
release-matched base-image selection, surfaced cluster-image build
diagnostics, preserved Nemotron profile registration.
- #6629 / #6637 -> `docs/about/release-notes.mdx`: bare `connect`
default-sandbox behavior and route-probe hardening.
- #6634 / #6626 / #6596 / #5569 / #6610 / #6655 ->
`docs/about/release-notes.mdx`: onboarding/recovery preservation,
stale-gateway-PID fix, installer backup message, vLLM label on managed
platforms.
- #6578 / #5670 -> `docs/about/release-notes.mdx`: automatic Hermes
light terminal skin and non-interactive `npx` MCP server startup.

## Verification

`npm run docs`: 0 errors, all internal links resolve (2 pre-existing
hidden-page warnings). `_build/` variants for OpenClaw, Hermes, and Deep
Agents all regenerate with the v0.0.80 section.

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

🤖 Generated with [Claude Code](https://claude.com/claude-code)


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Documentation**
  * Added release notes for v0.0.80.
  * Documented Hermes upgrades, including Slack Block Kit rendering.
  * Added details on OpenRouter traffic routing and attribution headers.
* Documented improved proxy certificate handling and sandbox
reliability.
* Highlighted enhanced connection defaults, route-probing safeguards,
onboarding recovery, and terminal/MCP startup behavior.
  * Added references to relevant user-guide documentation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Hadar301 pushed a commit to Hadar301/NemoClaw-OpenShift that referenced this pull request Jul 12, 2026
…DIA#6210) (NVIDIA#6292)

## Summary

On networks where a corporate MITM proxy sits in front of the host and
re-signs external TLS with its own root CA, that corporate root is
absent from the sandbox trust path. OpenShell injects only its own
L7-proxy CA, so external channel endpoints (e.g. `api.telegram.org`)
fail certificate verification even when the network policy allows the
connection (logs show `NET:OPEN` then `NET:FAIL`). This imports an
operator-supplied corporate CA into the sandbox trust bundle without
replacing the OpenShell CA.

## Related Issue

Fixes NVIDIA#6210

## Changes

- Add `src/lib/onboard/corporate-ca.ts`: validate a host CA bundle
(regular file, non-symlink, non-world-writable, bounded size, contains a
PEM certificate) from `NEMOCLAW_CORPORATE_CA_BUNDLE` (explicit,
fail-loud) or the conventional `REQUESTS_CA_BUNDLE` / `CURL_CA_BUNDLE` /
`SSL_CERT_FILE` fallbacks (skip-on-invalid). Opt out with
`NEMOCLAW_CORPORATE_CA_IMPORT=0`.
- Bake the validated CA into the OpenClaw and Hermes images via a base64
`NEMOCLAW_CORPORATE_CA_B64` build arg decoded to a root-owned `0444`
file (`dockerfile-patch.ts` + both Dockerfiles).
- At entrypoint startup (`scripts/nemoclaw-start.sh`,
`agents/hermes/start.sh`), append the baked CA to the OpenShell bundle
into a merged `/tmp` bundle — **never replacing** the OpenShell CA
(preserves NVIDIA#1828) — and repoint `SSL_CERT_FILE`, `CURL_CA_BUNDLE`,
`REQUESTS_CA_BUNDLE`, `GIT_SSL_CAINFO`, and `NODE_EXTRA_CA_CERTS` at it,
including for `connect` sessions. No-corporate-CA path is byte-for-byte
unchanged.
- Document the flow in `docs/reference/troubleshooting.mdx`.

## Type of Change

- [x] Code change with doc updates

## Quality Gates

- [x] Tests added or updated for changed behavior
- [x] Docs updated for user-facing behavior changes
- [x] Sensitive paths changed (security, policy, credentials, preflight,
onboarding, inference, runner, sandbox, or messaging)
- [x] Sensitive-path review completed or maintainer-approved waiver
recorded — reviewer/approval link/justification: host CA input is
validated (no symlink/world-writable/oversize; PEM required); the CA is
a public certificate baked as a root-owned `0444` file; OpenShell CA is
appended, never replaced; no secrets involved.

## Verification

- [x] PR description includes the DCO sign-off declaration and every
commit appears as `Verified` in GitHub
- [x] Normal `pre-commit`, `commit-msg`, and `pre-push` hooks passed, or
`npm run check:diff` passed when hooks were skipped or unavailable — all
28 prek pre-commit/pre-push hooks passed (shellcheck, hadolint, biome,
source-shape budget, test-file-size budget, env-var docs,
test-title-style); commit-msg clean on this commit.
- [x] Targeted behavior tests pass for the current change set — `vitest
run` on `corporate-ca.test.ts`, `dockerfile-patch.test.ts`,
`corporate-ca-runtime-merge.test.ts`, `corporate-ca-tls-e2e.test.ts` →
52 passed. The TLS test spins a server whose leaf is signed only by a
simulated corporate CA and proves verification succeeds only after the
real entrypoint merge while the OpenShell root stays trusted.
- [x] No secrets, API keys, or credentials committed
- [x] `npm run docs` builds without warnings (doc changes only)

### E2E note

The reporter's DGX Station + real corporate MITM proxy is not available
in CI. The trust-store behavior is reproduced hermetically without that
hardware: `test/corporate-ca-tls-e2e.test.ts` generates a corporate root
CA, signs a leaf with it, runs the **actual** `merge_corporate_proxy_ca`
block extracted from `scripts/nemoclaw-start.sh`, and asserts a Node TLS
request verifies the corporate-signed endpoint only after the merge
(pre-fix: `unable to verify` / `UNABLE_TO_GET_ISSUER`; post-fix: `200`),
and that the OpenShell root remains trusted through the merged bundle.

---

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


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added operator-controlled corporate proxy CA import with validation
and optional image baking, plus host anchor scanning fallback.
* Merges corporate and OpenShell CA trust at startup and updates TLS
trust settings for curl/Python/Git/Node; connect-session shells inherit
the merged trust (including Hermes).
* **Bug Fixes**
* Improves reliability of external TLS connections behind corporate MITM
proxies while preserving OpenShell-root trust; includes safer
merge/guard behavior.
* **Documentation**
* Added troubleshooting guidance for corporate MITM TLS failures and how
to supply/override corporate CA trust.
* **Tests**
* Added runtime merge, Dockerfile patching, and end-to-end TLS coverage.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Yimo Jiang <yimoj@nvidia.com>
Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
Signed-off-by: Carlos Villela <cvillela@nvidia.com>
Co-authored-by: Prekshi Vyas <prekshiv@nvidia.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Prekshi Vyas <34834085+prekshivyas@users.noreply.github.com>
Co-authored-by: Carlos Villela <cvillela@nvidia.com>
Hadar301 pushed a commit to Hadar301/NemoClaw-OpenShift that referenced this pull request Jul 12, 2026
## Summary

Release-prep documentation for **v0.0.80**. Adds the `## v0.0.80`
section to `docs/about/release-notes.mdx` summarizing user-facing
changes since v0.0.79, each bullet linking to the relevant deeper page.

Produced via `nemoclaw-contributor-update-docs` (pre-tag path): scanned
`v0.0.79..HEAD`, applied the docs skip list (no violations), and
confirmed the 8 commits that already shipped in-PR docs are complete. No
new pages needed.

## Source summary

- NVIDIA#6507 -> `docs/about/release-notes.mdx`: Hermes v0.18 + Slack Block
Kit (rich rendering, digest-pinned base image).
- NVIDIA#6584 / NVIDIA#6616 -> `docs/about/release-notes.mdx`: host-local OpenRouter
runtime attribution adapter (port `11437`,
`NEMOCLAW_OPENROUTER_RUNTIME_ADAPTER_PORT`) and native Deep Agents
`openrouter` provider.
- NVIDIA#6210 / NVIDIA#6292 -> `docs/about/release-notes.mdx`: host corporate proxy
CA import into sandbox trust (`NEMOCLAW_CORPORATE_CA_BUNDLE`,
`NEMOCLAW_CORPORATE_CA_IMPORT`).
- NVIDIA#6624 / NVIDIA#6623 / NVIDIA#6656 -> `docs/about/release-notes.mdx`:
release-matched base-image selection, surfaced cluster-image build
diagnostics, preserved Nemotron profile registration.
- NVIDIA#6629 / NVIDIA#6637 -> `docs/about/release-notes.mdx`: bare `connect`
default-sandbox behavior and route-probe hardening.
- NVIDIA#6634 / NVIDIA#6626 / NVIDIA#6596 / NVIDIA#5569 / NVIDIA#6610 / NVIDIA#6655 ->
`docs/about/release-notes.mdx`: onboarding/recovery preservation,
stale-gateway-PID fix, installer backup message, vLLM label on managed
platforms.
- NVIDIA#6578 / NVIDIA#5670 -> `docs/about/release-notes.mdx`: automatic Hermes
light terminal skin and non-interactive `npx` MCP server startup.

## Verification

`npm run docs`: 0 errors, all internal links resolve (2 pre-existing
hidden-page warnings). `_build/` variants for OpenClaw, Hermes, and Deep
Agents all regenerate with the v0.0.80 section.

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>

🤖 Generated with [Claude Code](https://claude.com/claude-code)


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Documentation**
  * Added release notes for v0.0.80.
  * Documented Hermes upgrades, including Slack Block Kit rendering.
  * Added details on OpenRouter traffic routing and attribution headers.
* Documented improved proxy certificate handling and sandbox
reliability.
* Highlighted enhanced connection defaults, route-probing safeguards,
onboarding recovery, and terminal/MCP startup behavior.
  * Added references to relevant user-guide documentation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: networking DNS, proxy, TLS, ports, host aliases, or connectivity area: onboarding Onboarding FSM, provider setup, sandbox launch, or first-run flow area: sandbox OpenShell sandbox lifecycle, runtime, config, or recovery area: security Security controls, permissions, secrets, or hardening feature PR adds or expands user-visible functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[NemoClaw][DGX Station][Sandbox] Corporate proxy CA not injected into sandbox trust store — external channel TLS fails (Telegram NET:FAIL)

8 participants