Skip to content

fix(security): redact secret patterns from CLI log and error output#1246

Merged
cv merged 9 commits intoNVIDIA:mainfrom
BenediktSchackenberg:fix/redact-secrets-672
Apr 2, 2026
Merged

fix(security): redact secret patterns from CLI log and error output#1246
cv merged 9 commits intoNVIDIA:mainfrom
BenediktSchackenberg:fix/redact-secrets-672

Conversation

@BenediktSchackenberg
Copy link
Copy Markdown
Contributor

@BenediktSchackenberg BenediktSchackenberg commented Apr 1, 2026

Finalizes the approach from #672 on top of current main.

Summary:

  • add runner-level secret redaction for command/error output
  • cover run(), runInteractive(), and runCapture() paths
  • include runner tests for keyed secrets, provider tokens, bearer tokens, stack traces, and multi-secret strings

Tested:

  • npm test -- --run test/runner.test.js

Summary by CodeRabbit

  • New Features

    • Automatic secret redaction for command outputs and a new public redaction utility.
  • Bug Fixes

    • Non-interactive command runs now suppress stdin and pipe outputs; interactive runs preserve stdin while captured stdout/stderr are redacted before logging or rethrowing.
    • Error messages and thrown errors now have secrets redacted.
  • Tests

    • Added unit and regression tests covering many token formats and redaction in error fields and output arrays.

Copilot AI review requested due to automatic review settings April 1, 2026 10:31
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 1, 2026

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

Adds secret-redaction utilities (exported as redact) and integrates them into the runner: run/runInteractive now default stdio to ["ignore","pipe","pipe"], captured stdout/stderr are redacted before logging, and runCapture rethrows redacted errors.

Changes

Cohort / File(s) Summary
Runner: redaction & behavior changes
bin/lib/runner.js
Introduces SECRET_PATTERNS, redactMatch, redactUrl, redact, redactError, and writeRedactedResult. run/runInteractive default stdio to ["ignore","pipe","pipe"], stream redacted captured output, and runCapture throws redacted errors. Exports updated to include redact.
Tests: redact and regression guards
test/runner.test.js
Adjusts child_process.spawnSync test doubles and stdio assertions to new defaults; adds describe("redact") tests for multiple secret patterns, URL auth/query redaction, non-string passthrough; adds regression tests ensuring errors and captured stdout/stderr are redacted before logging or rethrowing.

Sequence Diagram(s)

sequenceDiagram
    participant Caller
    participant Runner as "bin/lib/runner.js"
    participant Child as "child_process.spawnSync"
    participant Console as "process.stdout/stderr"

    Caller->>Runner: invoke run/runCapture/runInteractive(cmd, ...)
    Runner->>Child: spawnSync(cmd, { stdio: ["ignore","pipe","pipe"], ... })
    Child-->>Runner: result { status, stdout, stderr, error? }
    Runner->>Runner: redact(stdout), redact(stderr), redactError(error?)
    alt error present
        Runner->>Caller: throw redacted Error
    else
        Runner->>Console: writeRedactedResult(stdout/stderr)
        Runner-->>Caller: return result
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hopped through logs at break of dawn,
Chewed out secrets till they were gone.
With a twitch and a tidy little thump,
I tucked them deep beneath the clump.
Now outputs shine — no trace upon.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(security): redact secret patterns from CLI log and error output' directly and specifically describes the main change—implementing secret redaction for command output and error logging across the runner module.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

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

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

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds runner-level secret redaction so sensitive tokens/keys are masked before appearing in CLI log output or bubbled-up command execution errors.

Changes:

  • Introduces redact() / redactError() in bin/lib/runner.js and applies redaction to run(), runInteractive(), and runCapture() error paths.
  • Adds unit/regression tests covering multiple secret formats (NVIDIA keys, bearer tokens, GitHub tokens, key assignments) and multi-secret strings.

Reviewed changes

Copilot reviewed 1 out of 2 changed files in this pull request and generated no comments.

File Description
bin/lib/runner.js Adds secret redaction helpers and applies them to runner error/log output paths.
test/runner.test.js Adds test coverage validating redaction behavior and runCapture error-field redaction.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
bin/lib/runner.js (1)

16-27: ⚠️ Potential issue | 🔴 Critical

Child output with inherited stdio bypasses redaction in run() and runInteractive().

Both functions default to inherited stdio (["ignore", "inherit", "inherit"] and "inherit" respectively), which means the child process writes directly to the parent terminal. When a command fails, the sanitized command message is redacted (line 25, 40), but the actual child process output—which may contain raw secrets—reaches the terminal first. This creates a security gap: the redaction only covers the echoed command, not the failure output that precedes it.

In contrast, runCapture() uses piped stdio (["pipe", "pipe", "pipe"]) and properly redacts captured output via redactError() before re-throwing. If sensitive commands are run through run() or runInteractive(), their unredacted output is not protected by the current redaction strategy.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bin/lib/runner.js` around lines 16 - 27, The current run() (and similarly
runInteractive()) uses inherited stdio so child output can print raw secrets
before we log a redacted error; change them to capture child stdout/stderr (use
stdio: ["pipe","pipe","pipe"] or equivalent) and, on non-zero exit, run the
captured output through redactError() (or redact()) before writing any console
output or rethrowing; update run() to read result.stdout/result.stderr, redact
them, then print the redacted message and exit, and apply the same approach in
runInteractive() (or delegate to runCapture()) so no child output bypasses
redaction.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@bin/lib/runner.js`:
- Around line 66-80: The current redact function and SECRET_PATTERNS miss
authenticated URLs and auth-style query params; update redact (and
SECRET_PATTERNS) to also handle URL userinfo and auth query params by either
reusing the URL-aware redaction used in onboard-session.js or by adding
patterns/logic that (1) detect URL userinfo (e.g., https://user:pass@host/ ) and
replace the password portion after the colon with asterisks, and (2) detect auth
query parameters like auth, sig, signature (case-insensitive) and redact their
values (e.g., ?auth=VALUE -> ?auth=****); make these changes in the redact
function and/or SECRET_PATTERNS so calls to redact(...) will consistently mask
authenticated URLs and those query-param credentials.

---

Outside diff comments:
In `@bin/lib/runner.js`:
- Around line 16-27: The current run() (and similarly runInteractive()) uses
inherited stdio so child output can print raw secrets before we log a redacted
error; change them to capture child stdout/stderr (use stdio:
["pipe","pipe","pipe"] or equivalent) and, on non-zero exit, run the captured
output through redactError() (or redact()) before writing any console output or
rethrowing; update run() to read result.stdout/result.stderr, redact them, then
print the redacted message and exit, and apply the same approach in
runInteractive() (or delegate to runCapture()) so no child output bypasses
redaction.
🪄 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: Pro

Run ID: 4f4cb343-9a4b-4b89-8ab5-3fe1b28db155

📥 Commits

Reviewing files that changed from the base of the PR and between e06c147 and 396510d.

📒 Files selected for processing (2)
  • bin/lib/runner.js
  • test/runner.test.js

@BenediktSchackenberg

This comment was marked as abuse.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
bin/lib/runner.js (1)

111-125: Minor: redactError mutation may cause double-redaction if error is re-caught.

Since redactError mutates the error in place, if an already-redacted error is somehow caught and passed through redactError again, the redactMatch output (e.g., nvap****) could theoretically be re-processed. This is unlikely in current flow since runCapture only calls it once before throwing, but worth noting for defensive coding.

The existing behavior is acceptable given the current call sites.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bin/lib/runner.js` around lines 111 - 125, redactError currently mutates the
passed-in Error which can lead to double-redaction if the same error is
reprocessed; change redactError to operate on a shallow copy of the error object
instead of mutating the original: create a new object (preserving non-enumerable
Error semantics as needed) copy relevant properties from the input err, run
redact(...) on message/cmd/stdout/stderr/output on the copy, compute the stack
replacement using the originalMessage and the copy.message, and return the new
redacted error while leaving the input err unchanged; keep references to the
existing functions/variables redact and replaceAll and the function name
redactError for locating the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@bin/lib/runner.js`:
- Around line 111-125: redactError currently mutates the passed-in Error which
can lead to double-redaction if the same error is reprocessed; change
redactError to operate on a shallow copy of the error object instead of mutating
the original: create a new object (preserving non-enumerable Error semantics as
needed) copy relevant properties from the input err, run redact(...) on
message/cmd/stdout/stderr/output on the copy, compute the stack replacement
using the originalMessage and the copy.message, and return the new redacted
error while leaving the input err unchanged; keep references to the existing
functions/variables redact and replaceAll and the function name redactError for
locating the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 30c362b7-7ddd-4fb8-b8cb-f781396df716

📥 Commits

Reviewing files that changed from the base of the PR and between 128b46a and 0f3febb.

📒 Files selected for processing (2)
  • bin/lib/runner.js
  • test/runner.test.js

@BenediktSchackenberg
Copy link
Copy Markdown
Contributor Author

Review follow-up: the originally actionable runner issues on this PR are already addressed on fix/redact-secrets-672 in 0f3febb.

  • The URL/userinfo and auth-query-param leak is resolved in redact().
  • The outside-diff stdio concern is also already addressed in the current branch state: run() captures stdout/stderr, runInteractive() keeps stdin inherited while capturing stdout/stderr, and both paths emit redacted output before the failure message.

The remaining CodeRabbit note about redactError() mutating the Error instance is a non-blocking nit in the latest review body, and CodeRabbit explicitly marked the current behavior acceptable for the existing call sites. No additional code change is needed for that comment on this PR.

Adds JSDoc comments to run(), runInteractive(), runCapture(),
redactMatch(), redactUrl(), redact(), and writeRedactedResult()
to bring docstring coverage above the 80% threshold.
@BenediktSchackenberg
Copy link
Copy Markdown
Contributor Author

Added JSDoc comments to all functions in runner.js (run, runInteractive, runCapture, redactMatch, redactUrl, redact, writeRedactedResult) — docstring coverage should now be well above the 80% threshold.

@kjw3
Copy link
Copy Markdown
Contributor

kjw3 commented Apr 1, 2026

@BenediktSchackenberg can you address CI failures

…er tests

TypeScript check was failing with TS2339 because Error type does not
include .cmd and .output properties. Using @type {any} JSDoc casts to
tell the type checker these are intentionally extended error objects
(as produced by Node's execSync on failure).

Signed-off-by: Benedikt Schackenberg <6381261+BenediktSchackenberg@users.noreply.github.com>
@BenediktSchackenberg
Copy link
Copy Markdown
Contributor Author

@kjw3 — fixed the TypeScript check failure: added @type {any} JSDoc casts for the extended Error properties (.cmd, .output) in the runner test. Latest commit: 950cac3.

@wscurran wscurran added security Something isn't secure priority: high Important issue that should be resolved in the next release NemoClaw CLI Use this label to identify issues with the NemoClaw command-line interface (CLI). labels Apr 1, 2026
Copy link
Copy Markdown
Contributor

@cv cv left a comment

Choose a reason for hiding this comment

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

Thorough work. The stdio change from inherit to pipe is the right call — it is the only way to intercept child output before it reaches the terminal. URL credential redaction, the error field coverage, and the test suite are all solid.

One minor note for future: redactError mutates the error in place, so a re-caught error would get double-redacted. Acceptable for current call sites since runCapture only calls it once before throwing.

Consolidates #672. #1247 closed in favor of this.

@cv cv merged commit cb668d7 into NVIDIA:main Apr 2, 2026
7 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

NemoClaw CLI Use this label to identify issues with the NemoClaw command-line interface (CLI). priority: high Important issue that should be resolved in the next release security Something isn't secure

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants