Skip to content

fix(security): fail closed while scrubbing migration backups - #7847

Merged
senthilr-nv merged 29 commits into
mainfrom
codex/pr7765-credential-scrub
Aug 2, 2026
Merged

fix(security): fail closed while scrubbing migration backups#7847
senthilr-nv merged 29 commits into
mainfrom
codex/pr7765-credential-scrub

Conversation

@apurvvkumaria

@apurvvkumaria apurvvkumaria commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

Summary

This is a clean, compliant replacement for #7765 that preserves @Ayush7614 as the author of the original three commits. Host-to-sandbox migration and rebuild backups now scrub the same credential shapes, and backup creation fails closed instead of retaining a raw configuration when sanitization or cleanup fails.

Changes

  • Share credential-field, token-shape, auth-header, CLI-argument, and safe-placeholder filtering across migration and rebuild snapshots.
  • Recursively sanitize every copied migration JSON, YAML, and .env artifact, including external roots; omit authentication-state files and malformed artifacts.
  • Reject required malformed OpenClaw configuration copies and clean up incomplete staging directories.
  • Delete and reject incomplete rebuild backups if an unsafe artifact cannot be removed.
  • Bind migration and rebuild snapshot traversal and mutations to opened directory descriptors, fail closed on parent or root identity changes, and require POSIX python3 support.
  • Scrub common OAuth credential fields, including access, refresh, and identity tokens.
  • Add cross-implementation parity, failure-injection, malformed-artifact, value-shape, and real backup permission/content coverage.
  • Document that migration and rebuild snapshots are filtered restore points rather than byte-for-byte copies.

Type of Change

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

Quality Gates

  • Tests added or updated for changed behavior
  • Existing tests cover changed behavior — justification:
  • Tests not applicable — justification:
  • Docs updated for user-facing behavior changes
  • Docs not applicable — justification:
  • Sensitive paths changed (security, policy, credentials, preflight, onboarding, inference, runner, sandbox, or messaging)
  • Sensitive-path review completed or maintainer-approved waiver recorded — reviewer/approval link/justification: Codex Desktop completed the nine-category security review at 5b8dd525f6297ca6fe1f3b669801c464824e5704; PASS for secrets/credentials, input validation/sanitization, authorization boundaries, dependencies, error handling/logging, data protection, secure configuration/defaults, security regression testing, and holistic abuse/availability review. Exact diff fingerprint: cbe679f02c0b986b6e02aa4a9aeb7778acb4539f6aca760c5ff6edae8745064a.
  • Non-success, skipped, or missing CI check accepted by maintainer — check name, approval link, and follow-up issue:

Documentation Writer Review

  • Documentation writer subagent reviewed the completed changes
  • Result: docs-updated
  • Evidence: Independent Codex Desktop documentation writer review passed at exact head 5a27ad655cc1329e15ff37e28440762e24cb4dd6; all three changed pages and generated OpenClaw, Hermes, and Deep Agents variants were reviewed; npm run docs completed with 0 errors and 2 existing Fern warnings.
  • Agent: Codex Desktop

DGX Station Hardware Evidence

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

Verification

  • PR description includes a Signed-off-by: line and every commit appears as Verified in GitHub
  • Normal pre-commit, commit-msg, and pre-push hooks passed, or npm run validate:pr passed after refreshing origin/main when hooks were skipped or unavailable
  • Targeted behavior tests pass for the current change set, or tests are marked not applicable above — At exact head 5b8dd525f, the 27/27 interpreter-boundary and sanitizer tests, pre-commit guardrails, and plugin/CLI pre-push typechecks passed. The documentation build passed at immediately preceding head eb10756df; the follow-up changes tests only. At signed feature commit d59a0e3b4 before the docs-only main sync, the full 767/767 plugin suite and coverage ratchet (95.31% statements / 95.96% lines) passed; the feature diff is unchanged by the sync.
  • Applicable broad gate passed — npm run validate:pr; PASS on current main.
  • Quality Gates section completed with required justifications or waivers
  • No secrets, API keys, or credentials committed
  • npm run docs builds without errors (2 pre-existing warnings)
  • Doc pages follow the style guide (doc changes only)
  • New doc pages include SPDX header and frontmatter (new pages only)

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

Summary by CodeRabbit

  • Security Enhancements

    • Migration and snapshot bundles now sanitize credentials across JSON, YAML, and .env files while preserving approved placeholders and null values.
    • Sensitive or unsafe artifacts are omitted or removed, with stricter fail-closed behavior and clearer error reporting.
    • Incomplete backups are cleaned up when sanitization fails.
  • Documentation

    • Expanded snapshot contents, retention, and sanitization guidance.
    • Documented the python3 POSIX requirement and WSL support for Windows environments.
  • Tests

    • Added broader credential detection, redaction, parity, filesystem-safety, and failure-path coverage.

Ayush7614 and others added 6 commits July 29, 2026 13:03
Align host-to-sandbox migration and rebuild backups with the shared
credential filter so bot tokens, env secrets, Authorization headers,
Hermes YAML, and .env PASS fields cannot survive snapshot sanitization.

Signed-off-by: Ayush7614 <ayushknj3@gmail.com>
Shell-sourced env files often use `export KEY=value`, which bypassed
key detection. Strip the prefix before credential-field matching.

Signed-off-by: Ayush7614 <ayushknj3@gmail.com>
Preserve unset credential fields, omit unsanitizable Hermes YAML from
backups, normalize backup file extensions, and align migration secret
shape detection with the canonical token patterns.

Signed-off-by: Ayush7614 <ayushknj3@gmail.com>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
@apurvvkumaria apurvvkumaria added area: sandbox OpenShell sandbox lifecycle, runtime, config, or recovery area: security Security controls, permissions, secrets, or hardening bug-fix PR fixes a bug or regression v0.0.98 labels Jul 29, 2026
@apurvvkumaria apurvvkumaria self-assigned this Jul 29, 2026
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

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

Use the following commands to manage reviews:

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

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Snapshot creation now uses shared credential filtering for migration state, external roots, and sandbox backups. JSON, YAML, and .env files are sanitized. Unsafe files are removed, and incomplete backups are cleaned up or reported.

Changes

Snapshot credential sanitization

Layer / File(s) Summary
Shared credential filtering
nemoclaw/src/security/credential-filter.ts, src/lib/security/credential-filter.ts, test/credential-filter-parity.test.ts
Adds credential detection, placeholder preservation, recursive scrubbing, environment filtering, YAML support, sensitive-file classification, and no-follow file access.
Migration artifact sanitization
nemoclaw/src/security/snapshot-sanitizer.ts, nemoclaw/src/security/snapshot-sanitizer-python.ts, nemoclaw/src/security/snapshot-sanitizer*.test.ts
Scans migration trees with a restricted Python helper, validates file identities, applies atomic replacements, removes unsafe artifacts, and rejects unsafe required configuration.
Backup cleanup and migration integration
src/lib/state/*, nemoclaw/src/commands/migration-state.*, ci/test-file-size-budget.json
Routes copied state and external roots through sanitization, applies restrictive .env permissions, removes incomplete backups, reports cleanup failures, and updates filesystem test fixtures.
Platform and retention documentation
docs/get-started/prerequisites.mdx, docs/manage-sandboxes/backup-restore.mdx, docs/reference/host-files-and-state.mdx
Documents credential sanitization, identity checks, cleanup behavior, Python requirements, and WSL requirements for snapshot creation and deletion.

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

Sequence Diagram(s)

sequenceDiagram
  participant SnapshotCreation
  participant MigrationState
  participant SnapshotSanitizer
  participant CredentialFilter
  participant BackupFilesystem

  SnapshotCreation->>MigrationState: create snapshot bundle
  MigrationState->>SnapshotSanitizer: sanitize copied state and external roots
  SnapshotSanitizer->>CredentialFilter: sanitize JSON, YAML, and .env files
  CredentialFilter-->>SnapshotSanitizer: sanitized content or failure
  SnapshotSanitizer->>BackupFilesystem: remove unsafe artifacts
  MigrationState->>BackupFilesystem: remove incomplete staging directory on failure
  MigrationState-->>SnapshotCreation: completed snapshot or sanitization error
Loading

Possibly related PRs

Suggested reviewers: cv, ericksoa

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 34.48% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the security fix and fail-closed behavior for migration backup scrubbing.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Fix failing CI checks
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/pr7765-credential-scrub

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

@github-code-quality

github-code-quality Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: TypeScript

TypeScript / code-coverage/plugin

The overall coverage in commit 3634a20 in the codex/pr7765-credent... branch remains at 96%, unchanged from commit 4367d71 in the main branch.

Show a code coverage summary of the most impacted files.
File main 4367d71 codex/pr7765-credent... 3634a20 +/-
nemoclaw/src/co...ration-state.ts 94% 93% -1%
nemoclaw/src/co...test-fixture.ts 0% 81% +81%
nemoclaw/src/sh...er-boundary.cts 0% 95% +95%
nemoclaw/src/se...ot-sanitizer.ts 0% 95% +95%
nemoclaw/src/se...ntial-filter.ts 0% 100% +100%

TypeScript / code-coverage/cli

The overall coverage in commit 3634a20 in the codex/pr7765-credent... branch remains at 81%, unchanged from commit 4367d71 in the main branch.

Show a code coverage summary of the most impacted files.
File main 4367d71 codex/pr7765-credent... 3634a20 +/-
src/lib/platform.ts 89% 84% -5%
src/lib/shields/index.ts 69% 68% -1%
src/lib/messagi...nnels/policy.ts 100% 100% 0%
src/lib/sandbox...rce-identity.ts 88% 88% 0%
src/lib/state/config-io.ts 93% 93% 0%
src/lib/state/sandbox.ts 79% 81% +2%
src/lib/securit...ntial-filter.ts 92% 94% +2%
nemoclaw/src/se...ntial-filter.ts 0% 90% +90%
src/lib/securit...ot-sanitizer.ts 0% 93% +93%

Updated August 02, 2026 07:26 UTC

@github-actions

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

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

⚠️ Outside diff range comments (1)
nemoclaw/src/commands/migration-state.ts (1)

515-522: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Migration snapshot sanitization still fails open on an unparsable config.

if (!config) return; leaves the copied file exactly as-is, so an openclaw.json that loadConfigDocument cannot parse (malformed, oversized, unexpected shape) is retained in the snapshot with raw tokens. The backup path in src/lib/state/sandbox.ts now deletes such artifacts; this path should also omit the file or surface a failure rather than silently keeping it.

🤖 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 `@nemoclaw/src/commands/migration-state.ts` around lines 515 - 522, Update
sanitizeConfigFile so a falsy result from loadConfigDocument is not treated as
success: omit the snapshot file or propagate an explicit failure instead of
returning while retaining the unsanitized copy. Preserve the existing credential
stripping and permission handling for successfully parsed configurations.
🧹 Nitpick comments (1)
src/lib/state/sandbox-backup-sanitization.test.ts (1)

27-46: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a success-path case; the injected sanitizeEnvFile: () => false hides real .env behavior.

Both tests only exercise failure branches, and the stub bypasses the sanitizer under test. A case with a real credential-bearing .env asserting the placeholder content and the 0o600 mode (plus the backupExists → "incomplete backup remains" branch) would cover the behavior this PR is adding.

As per path instructions, flag "broad mocks that bypass the behavior under test".

🤖 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/state/sandbox-backup-sanitization.test.ts` around lines 27 - 46, Add
success-path coverage alongside the existing failure test for
sanitizeBackupDirectory: use the real sanitizeEnvFile implementation with a
credential-bearing .env, then assert the sanitized placeholder content and 0o600
file mode. Also cover the backupExists path and verify an incomplete backup
remains, avoiding mocks that bypass the sanitizer behavior under test.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@nemoclaw/src/security/credential-filter.ts`:
- Around line 4-6: Add a parity test covering the credential-filter
implementations in nemoclaw and src/lib/security, using shared fixtures that
exercise channel tokens, environment secrets, authorization headers, and
non-credential values. Assert both filters produce identical classifications and
placeholder behavior for every fixture, so duplicated field lists and rules
cannot drift.

In `@src/lib/security/credential-filter.ts`:
- Around line 370-387: Update sanitizeEnvFileContent to also redact values that
valueLooksLikeSecret identifies, even when the parsed key is not recognized by
isCredentialField. Preserve existing handling for comments, malformed lines,
credential placeholders, export-prefixed keys, and credential-field redaction,
while keeping the fail-closed deny-by-default behavior.
- Around line 441-448: Update the YAML handling in sanitizeBackupDirectory so
parseYaml results representing an empty or comment-only document (null) are
treated as successfully sanitized with no changes. Return the existing success
outcome before toConfigValue or isConfigObject rejects the null value, while
preserving current behavior for non-null unsupported or non-object
configurations.

---

Outside diff comments:
In `@nemoclaw/src/commands/migration-state.ts`:
- Around line 515-522: Update sanitizeConfigFile so a falsy result from
loadConfigDocument is not treated as success: omit the snapshot file or
propagate an explicit failure instead of returning while retaining the
unsanitized copy. Preserve the existing credential stripping and permission
handling for successfully parsed configurations.

---

Nitpick comments:
In `@src/lib/state/sandbox-backup-sanitization.test.ts`:
- Around line 27-46: Add success-path coverage alongside the existing failure
test for sanitizeBackupDirectory: use the real sanitizeEnvFile implementation
with a credential-bearing .env, then assert the sanitized placeholder content
and 0o600 file mode. Also cover the backupExists path and verify an incomplete
backup remains, avoiding mocks that bypass the sanitizer behavior under test.
🪄 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: dfd366e3-3e15-4ba3-91d1-ce965eec9d4f

📥 Commits

Reviewing files that changed from the base of the PR and between cd49b2f and 6e45761.

📒 Files selected for processing (9)
  • docs/manage-sandboxes/backup-restore.mdx
  • docs/reference/host-files-and-state.mdx
  • nemoclaw/src/commands/migration-state.ts
  • nemoclaw/src/security/credential-filter.test.ts
  • nemoclaw/src/security/credential-filter.ts
  • src/lib/security/credential-filter.test.ts
  • src/lib/security/credential-filter.ts
  • src/lib/state/sandbox-backup-sanitization.test.ts
  • src/lib/state/sandbox.ts

Comment thread nemoclaw/src/security/credential-filter.ts
Comment thread src/lib/security/credential-filter.ts
Comment thread src/lib/security/credential-filter.ts Outdated
@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

PR Review Advisor — No blocking findings reported

Advisor assessment: No blocking advisor findings reported
Next action: Review the warnings below.
Findings: 0 blockers · 1 warning · 0 suggestions

Model lanes

  • GPT-5.6 Terra (primary): Completed · high confidence · 0 blockers · 1 warning · 0 suggestions
  • Nemotron 3 Ultra (second opinion): Failed after a partial review · low confidence · 0 blockers · 0 warnings · 0 suggestions

Second-opinion terminology and E2E selections are advisory. They do not change the primary assessment or E2E / PR Gate.

3 semantic terminology decisions

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

  • established — descriptor-relative at docs/get-started/prerequisites.mdx:44: Keep `descriptor-relative`; existing repository security text uses the same meaning.
  • established — credential-bearing at docs/get-started/prerequisites.mdx:45: Keep `credential-bearing`; it matches the controlled-word definition.
  • established — migration snapshot at docs/reference/host-files-and-state.mdx:56: Keep `migration snapshot`; architecture, command, and reference text use it consistently.

E2E guidance

Advisory only. E2E / PR Gate selects and runs jobs independently.

Recommended E2E: cloud-inference, cloud-onboard, security-posture, onboard-repair, onboard-resume, rebuild-openclaw, state-backup-restore

1 optional E2E recommendation
  • snapshot-commands
1 warning · 0 suggestions

Warnings

Warnings do not block.

PRA-1 Warning — Limit the omission claim to artifacts that the sanitizer can inspect

  • Location: docs/manage-sandboxes/backup-restore.mdx:38
  • Category: docs
  • Problem: The documentation says that NemoClaw omits a copied configuration or environment file when it cannot sanitize it. The descriptor scanner instead aborts the complete snapshot before it creates file actions when a supported artifact exceeds its scan size limit or another scan-level inspection fails.
  • Impact: An operator can expect a usable snapshot with one omitted large artifact, but snapshot creation instead fails and removes the snapshot.
  • Recommendation: Revise the text to say that NemoClaw omits malformed or individually invalid supported artifacts, while scan-level safety or size-limit failures abort snapshot creation and trigger incomplete-snapshot cleanup.
  • Verification: Compare the revised statement with the scan-limit failure path in `nemoclaw/src/shared/snapshot-sanitizer-boundary.cts` and `sanitizeMigrationDirectory`.
  • Test coverage: Documentation build coverage is sufficient for this wording-only correction; existing sanitizer failure tests should continue to cover scan-level failure behavior.
  • Evidence: docs/manage-sandboxes/backup-restore.mdx:36-40 states that NemoClaw omits a copied configuration or environment file when it cannot sanitize it. nemoclaw/src/shared/snapshot-sanitizer-boundary.cts:168-170 defines scan size limits; lines 281-286 fail the scan when a file exceeds the file limit. nemoclaw/src/security/snapshot-sanitizer.ts:116-120 throws when descriptor scanning fails. nemoclaw/src/commands/migration-state.ts:604-626 catches sanitization errors and deletes the incomplete snapshot.

Workflow run details

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

Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Comment thread nemoclaw/src/security/snapshot-sanitizer.ts Fixed
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>

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

🤖 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 `@nemoclaw/src/security/snapshot-sanitizer-failure.test.ts`:
- Around line 20-27: Resolve the guardrail failure caused by the added
conditionals in the filesystem mock callbacks around the openSync and rmSync
overrides. Refactor the test setup to avoid increasing the counted if-statement
total, or update the accepted guardrail budget according to repository policy;
preserve the simulated open-failure and auth.json removal-prevention behaviors.

In `@nemoclaw/src/security/snapshot-sanitizer.test.ts`:
- Around line 67-87: The test currently cannot detect whether
sanitizeMigrationDirectory follows linked.json because target.json is also
inside root and sanitized independently. Update the test setup so targetPath is
outside the root directory, keep linkPath inside root, and assert after
sanitizing root that the external target content remains unchanged while
auth.json is removed.
- Around line 135-144: Make the “fails closed when a required sanitized file
cannot be written” test deterministic by mocking writeFileSync or renameSync to
inject the required failure instead of changing directory permissions. Add
afterEach cleanup that restores the mock and any injected failure state, while
preserving the assertion that sanitizeOpenClawConfigFile returns false.

In `@nemoclaw/src/security/snapshot-sanitizer.ts`:
- Around line 61-66: Make the open flow in
nemoclaw/src/security/snapshot-sanitizer.ts around the O_NOFOLLOW check fail
closed: return null when constants.O_NOFOLLOW is unavailable, removing the
lstatSync-to-openSync path fallback while preserving atomic no-follow opening.
Update nemoclaw/src/security/snapshot-sanitizer-failure.test.ts around the
fallback test to expect sanitizeOpenClawConfigFile to return false.
🪄 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: c876d4b8-ba32-495c-a188-1128dca7d9d2

📥 Commits

Reviewing files that changed from the base of the PR and between a596539 and e366086.

📒 Files selected for processing (5)
  • ci/test-file-size-budget.json
  • nemoclaw/src/commands/migration-state.test.ts
  • nemoclaw/src/security/snapshot-sanitizer-failure.test.ts
  • nemoclaw/src/security/snapshot-sanitizer.test.ts
  • nemoclaw/src/security/snapshot-sanitizer.ts

Comment thread nemoclaw/src/security/snapshot-sanitizer-failure.test.ts Outdated
Comment thread nemoclaw/src/security/snapshot-sanitizer.test.ts Outdated
Comment thread nemoclaw/src/security/snapshot-sanitizer.test.ts Outdated
Comment thread nemoclaw/src/security/snapshot-sanitizer.ts Outdated
@cjagwani

Copy link
Copy Markdown
Collaborator

Exact-head babysitting status for e3660863a36b56a2b212a626871a1761c3499174 (comment only; I am not submitting a changes-requested review):

  • protected E2E passed, but codebase-growth-guardrails is red: changed tests add 6 if statements (migration-state.test.ts +1, snapshot-sanitizer-failure.test.ts +3, snapshot-sanitizer.test.ts +2);
  • the current primary advisor’s filesystem-locality warning is valid: sanitizeMigrationDirectory validates entries by pathname and later recurses/opens them by pathname, so a checked nested entry can be replaced with a symlink between validation and use. O_NOFOLLOW protects only the final component, not replaced parent components. This security-sensitive snapshot scrubber should bind traversal/operations to already-opened parent directories, or fail closed where that guarantee is unavailable, with a deterministic swap-race test proving an external target is never accessed or changed;
  • the stable-symlink test should place its target outside the snapshot root, and the write-failure test should inject the failure deterministically rather than relying on directory permissions.

Maintainer edits are disabled, so I am leaving the branch untouched. Once the author pushes a quiet fix, I’ll re-review the exact head and babysit its CI/E2E to a terminal state.

Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>

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

Caution

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

⚠️ Outside diff range comments (1)
nemoclaw/src/security/snapshot-sanitizer.ts (1)

58-74: 🔒 Security & Privacy | 🔴 Critical | 🏗️ Heavy lift

Bind directory traversal to opened parent handles to close the symlink-swap TOCTOU.

At lines 137-146, sanitizeMigrationDirectory validates fullPath with lstatSync and then reuses that pathname. O_NOFOLLOW protects only the final component. Replacing a validated directory with a symlink can redirect reads and writes outside the snapshot root.

Use descriptor-relative operations, or fail closed when unavailable. Add a deterministic parent-directory swap test.

🤖 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 `@nemoclaw/src/security/snapshot-sanitizer.ts` around lines 58 - 74, Update
sanitizeMigrationDirectory and its fullPath access flow to bind directory
validation and subsequent reads/writes to opened parent directory handles,
preventing symlink swaps between lstatSync and pathname use; use
descriptor-relative operations where supported and otherwise fail closed. Add a
deterministic test that swaps the validated parent directory and verifies no
access escapes the snapshot root.
🧹 Nitpick comments (1)
nemoclaw/src/security/snapshot-sanitizer.ts (1)

58-74: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated secure file-read helper across two security modules.

readRegularSnapshotFile here is structurally identical to readRegularFileNoFollow in src/lib/security/credential-filter.ts. Both implement the same O_NOFOLLOW-based fail-closed open logic independently. Extract a single shared helper so a future fix to one copy (as just happened for the fail-closed behavior) cannot silently miss the other.

🤖 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 `@nemoclaw/src/security/snapshot-sanitizer.ts` around lines 58 - 74, Extract
the shared O_NOFOLLOW fail-closed file-reading logic from
readRegularSnapshotFile and readRegularFileNoFollow into one reusable security
helper, then update both callers to use it. Preserve the existing regular-file
validation, UTF-8 reading, descriptor cleanup, and null-on-failure behavior.
🤖 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.

Outside diff comments:
In `@nemoclaw/src/security/snapshot-sanitizer.ts`:
- Around line 58-74: Update sanitizeMigrationDirectory and its fullPath access
flow to bind directory validation and subsequent reads/writes to opened parent
directory handles, preventing symlink swaps between lstatSync and pathname use;
use descriptor-relative operations where supported and otherwise fail closed.
Add a deterministic test that swaps the validated parent directory and verifies
no access escapes the snapshot root.

---

Nitpick comments:
In `@nemoclaw/src/security/snapshot-sanitizer.ts`:
- Around line 58-74: Extract the shared O_NOFOLLOW fail-closed file-reading
logic from readRegularSnapshotFile and readRegularFileNoFollow into one reusable
security helper, then update both callers to use it. Preserve the existing
regular-file validation, UTF-8 reading, descriptor cleanup, and null-on-failure
behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 5900ea7d-dff2-45f3-bc73-7f68d69afd12

📥 Commits

Reviewing files that changed from the base of the PR and between e366086 and 72fe613.

📒 Files selected for processing (12)
  • docs/manage-sandboxes/backup-restore.mdx
  • nemoclaw/src/commands/migration-state.test.ts
  • nemoclaw/src/security/credential-filter.test.ts
  • nemoclaw/src/security/credential-filter.ts
  • nemoclaw/src/security/snapshot-sanitizer-failure.test.ts
  • nemoclaw/src/security/snapshot-sanitizer.test.ts
  • nemoclaw/src/security/snapshot-sanitizer.ts
  • src/lib/security/credential-filter-failure.test.ts
  • src/lib/security/credential-filter-secret-patterns.test.ts
  • src/lib/security/credential-filter.ts
  • src/lib/state/sandbox.ts
  • test/credential-filter-parity.test.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • docs/manage-sandboxes/backup-restore.mdx
  • test/credential-filter-parity.test.ts
  • nemoclaw/src/security/credential-filter.test.ts
  • src/lib/state/sandbox.ts
  • nemoclaw/src/commands/migration-state.test.ts

apurvvkumaria and others added 9 commits July 31, 2026 06:13
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>

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

Reviewed exact head 5b8dd52.

No blocking code or security findings. Product scope: in scope; this hardens the existing migration/rebuild snapshot surface and does not create a new supported integration or solution surface.

Validated independently: 154 focused CLI/plugin/integration tests passed; CLI and plugin typechecks passed; repository checks passed; npm run docs completed with 0 errors (2 existing warnings); and a real-filesystem sanitizer-failure check confirmed createSnapshotBundle returns null and removes the incomplete staging directory. The latest primary PR Review Advisor reported 0 blockers, and all CodeRabbit threads are resolved.

Approval is based on the PR implementation and focused validation, independently of GitHub merge/check blocked status.

@senthilr-nv
senthilr-nv enabled auto-merge (squash) August 1, 2026 17:51
@cjagwani

cjagwani commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator

Exact-head babysitting update for 5a27ad655cc1329e15ff37e28440762e24cb4dd6 (comment only; not a Changes Requested review): the current CI failure is an unrelated timeout in test/package-contract/openshell-policy-boundary.test.ts at line 182 (run 30711188168, job 91398854315). The E2E coordinator correctly emitted the prerequisite-CI retry marker and did not dispatch E2E. Its recorded base 5cd29cce9568fa6a3c790c967daa41cd87eec418 is no longer current, so I did not rerun stale head/base evidence. A fresh head/base revision is required before exact-head CI/E2E can be accepted. This coordination update does not supersede the security review of the new head.

@senthilr-nv
senthilr-nv merged commit d5b64a7 into main Aug 2, 2026
52 of 53 checks passed
@senthilr-nv
senthilr-nv deleted the codex/pr7765-credential-scrub branch August 2, 2026 07:49
cv added a commit that referenced this pull request Aug 3, 2026
<!-- markdownlint-disable MD041 -->
## Summary

Follow-up review of merged #7995 and #7847 found two independent
state-handling gaps. Shields recovery now remains bound to its original
configuration target. Migration preparation now installs only verified,
credential-filtered configuration bytes through a pinned directory
descriptor.

## Changes

- Follow-up to #7995:
- Persist the agent name, config path, and config directory in each
Shields timer marker.
- Use that persisted target for detached-timer and expired-marker
recovery when registry state is unavailable or resolves to a different
target.
- Give Deep Agents a canonical protected-file set and a
descriptor-sealed config-lock transaction.
- Fail closed when the transaction cannot restore or confirm a protected
posture.
  - Document recovery from a critical Deep Agents config-lock failure.
- Follow-up to #7847:
  - Exclude the copied `openclaw.json` from the general recursive copy.
- Read the copied config through the descriptor-bound snapshot scanner.
  - Strip credential fields and contextual secret assignments in memory.
- Install the sanitized config with exclusive, no-follow creation at
mode `0600`.
- Verify the installed inode, link count, size, and digest before
accepting it.
- Reject unsafe config path segments that could modify object
prototypes.
- Pin the plugin contextual secret patterns to the canonical CLI
patterns with a parity test.

## Type of Change

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

## Quality Gates

- [x] Tests added or updated for changed behavior
- [ ] Existing tests cover changed behavior — justification:
- [ ] Tests not applicable — justification:
- [x] Docs updated for user-facing behavior changes
- [ ] Docs not applicable — justification:
- [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: Independent
nine-category security review passed for commit SHA
`662d4ad020369dfcad3a21da225090cce507b111` against base SHA
`d6ac4027b75b15b8acd1456984acbbfb623cb231`. No findings in secrets and
credentials; input validation and data sanitization; authentication and
authorization; dependencies and third-party libraries; error handling
and logging; cryptography and data protection; configuration and
security headers; security testing; or system security. The three
addressed review threads are resolved.
- [ ] Non-success, skipped, or missing CI check accepted by maintainer —
check name, approval link, and follow-up issue:

## Documentation Writer Review

- [x] Documentation writer subagent reviewed the completed changes
- Result: `docs-updated`
- Evidence: Independent review passed for commit SHA
`662d4ad020369dfcad3a21da225090cce507b111` against base SHA
`d6ac4027b75b15b8acd1456984acbbfb623cb231`. Reviewed
`docs/reference/commands.mdx`, `docs/reference/troubleshooting.mdx`, and
changed explanatory text against the NemoClaw Writing Guide, Controlled
Word List, documentation guidance, implementation, tests, and Deep
Agents variant routing. Required checks `docs-review-receipt`,
`cli-parity`, and `preview` pass for this commit. No blocking finding
remains.
- Agent: Codex Desktop
<!-- docs-review-head-sha: 662d4ad -->
<!-- docs-review-agents-blob-sha: 3dd7c24 -->

## DGX Station Hardware Evidence

- [ ] Tested on DGX Station
- Tested commit:
- Station profile/scenario:
- Result:
- Supporting evidence:

## Verification

- [x] PR description includes a `Signed-off-by:` line and every commit
appears as `Verified` in GitHub
- [x] Normal `pre-commit`, `commit-msg`, and `pre-push` hooks passed, or
`npm run validate:pr` passed after refreshing `origin/main` when hooks
were skipped or unavailable
- [x] Targeted behavior tests pass for the current change set — Required
check `checks` concluded `SUCCESS` for commit SHA
`662d4ad020369dfcad3a21da225090cce507b111`.
- [ ] Applicable broad gate passed — Required check `E2E / PR Gate` is
running for commit SHA `662d4ad020369dfcad3a21da225090cce507b111`.
- [x] Quality Gates section completed with required justifications or
waivers
- [x] No secrets, API keys, or credentials committed
- [x] `npm run docs` builds without warnings (documentation changes
only) — Required documentation checks pass for commit SHA
`662d4ad020369dfcad3a21da225090cce507b111`.
- [x] Doc pages follow the [style
guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md)
(doc changes only)
- [ ] New doc pages include SPDX header and frontmatter (new pages only)

---
Signed-off-by: Carlos Villela <cvillela@nvidia.com>

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

* **New Features**
* Added fail-closed protection for Deep Agents configuration-lock
failures.
* Improved automatic recovery by preserving agent and configuration
details.
  * Added secure snapshot-based configuration recovery and verification.
  * Expanded detection of embedded credential patterns.

* **Bug Fixes**
* Prevented unsafe repairs, symlink swaps, hard-link mutations, and
incomplete rollbacks.
  * Improved handling of invalid or empty configuration files.

* **Documentation**
* Added guidance for diagnosing, recovering from, and verifying critical
configuration-lock failures.

* **Tests**
* Expanded coverage for security, recovery, locking, rollback, and timer
behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Carlos Villela <cvillela@nvidia.com>
senthilr-nv added a commit that referenced this pull request Aug 4, 2026
<!-- markdownlint-disable MD041 -->
## Summary

Adds the canonical dated `v0.0.101` changelog entry that was missing
when the release tag was cut. This post-release recovery records the
shipped behavior on current `main` without changing or replacing the
existing tag.

## Changes

- Add `docs/changelog/2026-08-03.mdx` with the exact `## v0.0.101`
heading, release summary, detailed behavior changes, support boundaries,
and links to durable documentation.
- [#7317](#7317) ->
`docs/changelog/2026-08-03.mdx`: Records experimental OpenClaw Google
Chat support and its restricted credential and webhook boundary.
- [#7715](#7715) ->
`docs/changelog/2026-08-03.mdx`: Records strict onboarding recovery
state and authoritative resume identity.
- [#7749](#7749) ->
`docs/changelog/2026-08-03.mdx`: Records the provider-neutral policy
seam and unchanged runtime support boundary.
- [#7817](#7817) ->
`docs/changelog/2026-08-03.mdx`: Records preserved Hermes home-channel
assignments across rebuilds.
- [#7820](#7820) ->
`docs/changelog/2026-08-03.mdx`: Records the SSH-session status field
correction.
- [#7847](#7847) ->
`docs/changelog/2026-08-03.mdx`: Records fail-closed credential
filtering for migration and rebuild backups.
- [#7870](#7870) ->
`docs/changelog/2026-08-03.mdx`: Records sandbox-qualified in-sandbox
host command hints.
- [#7875](#7875) ->
`docs/changelog/2026-08-03.mdx`: Records Microsoft Teams stop and start
E2E coverage.
- [#7885](#7885) ->
`docs/changelog/2026-08-03.mdx`: Records Hermes managed gateway
detection in status.
- [#7889](#7889) ->
`docs/changelog/2026-08-03.mdx`: Records policy-authenticated HTTPS Pin
Runtime route revocation.
- [#7891](#7891) ->
`docs/changelog/2026-08-03.mdx`: Records default fallback for negative
timeout and polling overrides.
- [#7993](#7993) ->
`docs/changelog/2026-08-03.mdx`: Records correct sibling detection
during uninstall.
- [#7995](#7995) ->
`docs/changelog/2026-08-03.mdx`: Records absent configuration-hash
handling before shields lock.
- [#8001](#8001) ->
`docs/changelog/2026-08-03.mdx`: Records the dormant atomic managed
workload replacement foundation.
- [#8029](#8029) ->
`docs/changelog/2026-08-03.mdx`: Records repository terminology review
in PR Review Advisor.
- [#8031](#8031) ->
`docs/changelog/2026-08-03.mdx`: Records provider-neutral managed
snapshot authority.
- [#8032](#8032) ->
`docs/changelog/2026-08-03.mdx`: Records immutable managed clone handoff
contracts.
- [#8034](#8034) ->
`docs/changelog/2026-08-03.mdx`: Records the dormant provider-owned
clone transaction surface.
- [#8035](#8035) ->
`docs/changelog/2026-08-03.mdx`: Records the dormant Hermes managed
clone broker boundary.
- [#8036](#8036) ->
`docs/changelog/2026-08-03.mdx`: Records the dormant transactional
managed bootstrap boundary.
- [#8037](#8037) ->
`docs/changelog/2026-08-03.mdx`: Records dormant Docker bootstrap
primitives and the unchanged provider support boundary.
- [#8070](#8070) ->
`docs/changelog/2026-08-03.mdx`: Records consolidated sandbox
resource-limit E2E coverage.
- [#8071](#8071) ->
`docs/changelog/2026-08-03.mdx`: Records escaped and bounded CLI
validation diagnostics.
- [#8081](#8081) ->
`docs/changelog/2026-08-03.mdx`: Records bounded linear snapshot Base64
validation.
- [#8085](#8085) ->
`docs/changelog/2026-08-03.mdx`: Records commit-bound workflow approval
for eligible same-repository maintainers.
- [#8088](#8088) ->
`docs/changelog/2026-08-03.mdx`: Records Hermes managed-policy E2E
selection.
- [#8090](#8090) ->
`docs/changelog/2026-08-03.mdx`: Records pinned CI search-tool
provisioning.
- [#8106](#8106) ->
`docs/changelog/2026-08-03.mdx`: Records fallback from failed managed
OpenShell gateway startup.
- [#8107](#8107) ->
`docs/changelog/2026-08-03.mdx`: Records Hermes adapter lifecycle E2E
selection.
- [#8128](#8128) ->
`docs/changelog/2026-08-03.mdx`: Records the dormant transactional
Docker bootstrap adapter and rollback authority.
- [#8140](#8140) ->
`docs/changelog/2026-08-03.mdx`: Records Slack conflict scope across
independent OpenShell gateways.
- [#8147](#8147) ->
`docs/changelog/2026-08-03.mdx`: Records completion of durable v0.0.100
documentation audit follow-ups.

## Type of Change

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

## Quality Gates

- [ ] Tests added or updated for changed behavior
- [ ] Existing tests cover changed behavior — justification:
- [x] Tests not applicable — justification: This documentation-only
recovery does not change executable behavior.
- [x] Docs updated for user-facing behavior changes
- [ ] Docs not applicable — justification:
- [ ] Sensitive paths changed (security, policy, credentials, preflight,
onboarding, inference, runner, sandbox, or messaging)
- [ ] Sensitive-path review completed or maintainer-approved waiver
recorded — reviewer/approval link/justification:
- [ ] Non-success, skipped, or missing CI check accepted by maintainer —
check name, approval link, and follow-up issue:

## Documentation Writer Review

- [x] Documentation writer subagent reviewed the completed changes
- Result: `docs-updated`
- Evidence: Independently reviewed `docs/changelog/2026-08-03.mdx` at
commit `0bebe1f568e3dc85cf410aac1dfb8f8830070b85`. Its blob is
`82887920f9720eafd75db6b2271c35f7477edb9b`. The entry follows the
writing guide, controlled terminology, changelog structure, MDX SPDX
format, literal CLI-name rule, and root-absolute route requirements. It
accurately records the `v0.0.100...v0.0.101` release range, Announcement
#8162, accepted scope boundaries, and shipped security behavior. There
are no code samples. Focused changelog tests and the documentation build
pass for this commit.
- Agent: Codex Desktop independent documentation writer
<!-- docs-review-head-sha: 0bebe1f -->
<!-- docs-review-agents-blob-sha:
3dd7c24 -->

## Security Review

- Result: `PASS`
- Reviewed commit: `0bebe1f568e3dc85cf410aac1dfb8f8830070b85`
- Base commit: `643a4ab8b5f583d8555192a37927268b26022c51`
- Findings: None.
- Secrets and credentials: `PASS`. No credential values or secret files
are present.
- Input validation and data sanitization: `PASS`. No executable input
path changes.
- Authentication and authorization: `PASS`. No identity or permission
logic changes.
- Dependencies and third-party libraries: `PASS`. No dependency changes.
- Error handling and logging: `PASS`. No runtime path changes;
diagnostic-security claims are precise.
- Cryptography and data protection: `PASS`. No implementation changes.
- Configuration and security controls: `PASS`. No configuration,
container, port, or HTTP changes.
- Security testing: `PASS`. No coverage is removed; the entry records
shipped test and security behavior.
- System security: `PASS`. No runtime control changes; dormant and
non-activation boundaries are explicit.
- Agent: Codex Desktop independent security reviewer

## Verification

- [ ] PR description includes a `Signed-off-by:` line and every commit
appears as `Verified` in GitHub — verification is pending after commit
`0bebe1f568e3dc85cf410aac1dfb8f8830070b85` is pushed.
- [ ] Normal `pre-commit`, `commit-msg`, and `pre-push` hooks passed, or
`npm run validate:pr` passed after refreshing `origin/main` when hooks
were skipped or unavailable — commit hooks passed; pre-push is pending.
- [x] Targeted behavior tests pass for the current change set, or tests
are marked not applicable above — tests are not applicable to this
documentation-only recovery.
- [x] Applicable broad gate passed — not applicable to this
documentation-only recovery.
- [x] Quality Gates section completed with required justifications or
waivers
- [x] No secrets, API keys, credentials, or private keys are added by
this diff.
- [ ] `npm run docs` builds without warnings (doc changes only) — GitHub
documentation checks are pending.
- [x] Doc pages follow the [style
guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md)
(doc changes only) — independent documentation review passed.
- [x] New doc pages include SPDX header and frontmatter (new pages only)
— the native changelog entry uses the required parser-safe MDX SPDX
comment and intentionally has no frontmatter.

GitHub CI is authoritative.
Focused changelog tests and `npm run docs` passed after the merge
refresh.

---
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>


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

* **New Features**
  * Added experimental Google Chat support.
  * Improved runtime and session status visibility.
  * Added onboarding recovery and persistence safeguards.
  * Added snapshot validation and dormant managed-workload support.

* **Bug Fixes**
* Improved backup sanitization, route handling, and gateway reliability.

* **Documentation**
  * Added the v0.0.101 changelog and related updates.

* **Tests**
  * Expanded end-to-end coverage and strengthened trusted CI validation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Signed-off-by: Carlos Villela <cvillela@nvidia.com>
Co-authored-by: Carlos Villela <cvillela@nvidia.com>
Co-authored-by: Senthil Ravichandran <senthilr@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: sandbox OpenShell sandbox lifecycle, runtime, config, or recovery area: security Security controls, permissions, secrets, or hardening bug-fix PR fixes a bug or regression

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants