Skip to content

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

Open
WuKongAI-CMU wants to merge 1 commit intoNVIDIA:mainfrom
WuKongAI-CMU:security/redact-cli-secrets
Open

fix(security): redact secret patterns from CLI log and error output#672
WuKongAI-CMU wants to merge 1 commit intoNVIDIA:mainfrom
WuKongAI-CMU:security/redact-cli-secrets

Conversation

@WuKongAI-CMU
Copy link

@WuKongAI-CMU WuKongAI-CMU commented Mar 22, 2026

Summary

Adds a redact() helper to bin/lib/runner.js that masks known secret patterns before they reach CLI error messages.

Patterns covered:

  • NVIDIA API keys (nvapi-*)
  • NVCF keys (nvcf-*)
  • Bearer tokens
  • Generic key/token/password assignments in command strings

Applied to all run() and runInteractive() error output where the failing command string is logged. Non-secret strings pass through unchanged.

Test plan

  • 6 new tests in test/runner.test.js covering all pattern types + non-secret passthrough + non-string input
  • All 21 runner tests pass
  • All 81 pre-commit tests pass

Fixes #664

Summary by CodeRabbit

  • Bug Fixes

    • Sensitive tokens and API keys are now masked in error messages.
  • Tests

    • Added test coverage for secret masking functionality.

Add a redact() helper to bin/lib/runner.js that masks known secret
patterns before they reach CLI error messages. Covers:
- NVIDIA API keys (nvapi-*)
- NVCF keys (nvcf-*)
- Bearer tokens
- Generic key/token/password assignments

Applied to all run() and runInteractive() error output where the
failing command string is logged. Non-secret strings pass through
unchanged.

Fixes NVIDIA#664

Signed-off-by: peteryuqin <peter.yuqin@gmail.com>
@coderabbitai
Copy link

coderabbitai bot commented Mar 22, 2026

📝 Walkthrough

Walkthrough

This PR adds a secret redaction utility to prevent API keys and tokens from leaking in CLI error output. A redact() function scans strings for known secret patterns (NVIDIA keys, Bearer tokens, etc.) and masks matched values, with application to runner error logging.

Changes

Cohort / File(s) Summary
Core Redaction Implementation
bin/lib/runner.js
Added redact(str) utility function with SECRET_PATTERNS regex matcher covering NVIDIA API keys (nvapi-/nvcf-), Bearer tokens, and key=value assignments. Applied to error logging in run() and runInteractive() to mask truncated command strings. Exported function for public use.
Test Coverage
test/runner.test.js
Added describe("redact") test suite verifying masking of NVIDIA keys, NVCF tokens, Bearer authorization headers, shell variable assignments, and non-secret string preservation. Includes tests for non-string inputs (null, undefined, numbers).

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰✨ Secrets safe, no leaks today!
With redacted keys, we're here to stay,
No tokens bare, no keys exposed,
Security wrapped and safely closed! 🔐🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main security-focused change: adding secret pattern redaction to CLI output.
Linked Issues check ✅ Passed All coding requirements from issue #664 are met: redact() helper implemented, applied to error output, covers required patterns (nvapi-, nvcf-, Bearer tokens, key assignments), and comprehensive tests added.
Out of Scope Changes check ✅ Passed All changes are directly scoped to redaction implementation in runner.js and corresponding test coverage; no unrelated modifications detected.

✏️ 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

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

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)

46-58: ⚠️ Potential issue | 🟡 Minor

Apply redact() to runCapture error paths for consistency with other run functions.

runCapture throws unredacted exceptions (line 57), while run() and runInteractive() both apply redact() to command failures. This inconsistency creates a defensive programming gap: although current callers catch errors without logging, future modifications could expose the unredacted command string containing secrets. Align with the existing pattern by wrapping the thrown error message with redact().

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

In `@bin/lib/runner.js` around lines 46 - 58, The catch block in runCapture
currently rethrows the raw error; update runCapture so that in the catch, after
handling opts.ignoreError, it rethrows the error wrapped with redact() (i.e.,
throw redact(err)) to match the behavior of run() and runInteractive(); keep the
existing ignoreError return "" behavior and ensure you reference the runCapture
function and redact() wrapper when making the change.
🧹 Nitpick comments (2)
test/runner.test.js (1)

144-182: Consider adding test cases for edge patterns and potential bypasses.

The test suite covers the happy paths well. Consider adding tests for:

  1. Case sensitivity: Does BEARER (uppercase) or bearer (lowercase) get masked? (The regex uses gi flag, so it should)
  2. Quoted values: API_KEY="secret123abc" — does the quote handling work?
  3. Multiple secrets in one string: nvapi-key1 nvapi-key2
Optional: Additional test cases
it("masks bearer tokens case-insensitively", () => {
  const { redact } = require(runnerPath);
  expect(redact("authorization: bearer someToken1234")).toContain("****");
});

it("masks multiple secrets in one string", () => {
  const { redact } = require(runnerPath);
  const input = "nvapi-firstkey12345 and nvapi-secondkey6789";
  const output = redact(input);
  expect(output).not.toContain("firstkey");
  expect(output).not.toContain("secondkey");
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/runner.test.js` around lines 144 - 182, Add unit tests to cover edge
patterns and potential bypasses for the redact function: include
case-insensitive bearer variations (e.g., "BEARER"/"bearer") to ensure redact
(from runnerPath) handles regex flags correctly, add quoted assignment cases
(e.g., API_KEY="secret123abc") to verify quotes are masked, and add a test with
multiple secrets in one string (e.g., "nvapi-first nvapi-second") to confirm
redact masks all occurrences; place these new it() cases alongside the existing
describe("redact") block referencing redact from runnerPath.
bin/lib/runner.js (1)

66-71: Add coverage for _KEY= and ghp_ patterns to match shell script.

The JS patterns miss two variants that are covered in scripts/debug.sh (lines 107-113):

  • ghp_ prefix (GitHub personal access tokens)
  • _KEY= suffix (catches NVIDIA_API_KEY, OTHER_API_KEY, etc.)

The shell script redacts both, but the JS version only covers API_KEY, SECRET, TOKEN, PASSWORD, CREDENTIAL.

Suggested update
 const SECRET_PATTERNS = [
   /nvapi-[A-Za-z0-9_-]{10,}/g,
   /nvcf-[A-Za-z0-9_-]{10,}/g,
+  /ghp_[A-Za-z0-9]{30,}/g,
   /(?<=Bearer\s)[A-Za-z0-9_.+/=-]{10,}/gi,
-  /(?<=(?:API_KEY|SECRET|TOKEN|PASSWORD|CREDENTIAL)[=: ]['"]?)[A-Za-z0-9_.+/=-]{10,}/gi,
+  /(?<=(?:_KEY|API_KEY|SECRET|TOKEN|PASSWORD|CREDENTIAL)[=: ]['"]?)[A-Za-z0-9_.+/=-]{10,}/gi,
 ];
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bin/lib/runner.js` around lines 66 - 71, SECRET_PATTERNS in runner.js is
missing coverage for GitHub PATs and env vars that end with _KEY; update the
SECRET_PATTERNS array (the constant named SECRET_PATTERNS) to add a regex for
the ghp_ prefix (e.g. match ghp_[A-Za-z0-9_-]{10,}) and extend the existing
lookbehind group that matches API_KEY/SECRET/TOKEN/PASSWORD/CREDENTIAL to also
accept variable names that end with _KEY (so include _KEY in that lookbehind),
keeping the same overall length threshold and flags as the other patterns.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@bin/lib/runner.js`:
- Around line 46-58: The catch block in runCapture currently rethrows the raw
error; update runCapture so that in the catch, after handling opts.ignoreError,
it rethrows the error wrapped with redact() (i.e., throw redact(err)) to match
the behavior of run() and runInteractive(); keep the existing ignoreError return
"" behavior and ensure you reference the runCapture function and redact()
wrapper when making the change.

---

Nitpick comments:
In `@bin/lib/runner.js`:
- Around line 66-71: SECRET_PATTERNS in runner.js is missing coverage for GitHub
PATs and env vars that end with _KEY; update the SECRET_PATTERNS array (the
constant named SECRET_PATTERNS) to add a regex for the ghp_ prefix (e.g. match
ghp_[A-Za-z0-9_-]{10,}) and extend the existing lookbehind group that matches
API_KEY/SECRET/TOKEN/PASSWORD/CREDENTIAL to also accept variable names that end
with _KEY (so include _KEY in that lookbehind), keeping the same overall length
threshold and flags as the other patterns.

In `@test/runner.test.js`:
- Around line 144-182: Add unit tests to cover edge patterns and potential
bypasses for the redact function: include case-insensitive bearer variations
(e.g., "BEARER"/"bearer") to ensure redact (from runnerPath) handles regex flags
correctly, add quoted assignment cases (e.g., API_KEY="secret123abc") to verify
quotes are masked, and add a test with multiple secrets in one string (e.g.,
"nvapi-first nvapi-second") to confirm redact masks all occurrences; place these
new it() cases alongside the existing describe("redact") block referencing
redact from runnerPath.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 29d3d3d1-7a52-4a7e-a4ec-c0af1bf967dd

📥 Commits

Reviewing files that changed from the base of the PR and between d37a09f and b899518.

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

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

security: redact secret patterns from CLI log and error output

1 participant