Skip to content

fix: redact cause, stdout and stderr in default error details - #10072

Open
ManoharPaturi wants to merge 1 commit into
npm:latestfrom
ManoharPaturi:error-cause-redaction
Open

ManoharPaturi wants to merge 1 commit into
npm:latestfrom
ManoharPaturi:error-cause-redaction

Conversation

@ManoharPaturi

Copy link
Copy Markdown

Problem

errorMessage starts by redacting the error message and stack, and the default case already redacts command args:

er.message &&= replaceInfo(er.message)
er.stack &&= replaceInfo(er.stack)
...
detail.push(['command', ...[er.cmd, ...er.args.map(replaceInfo)]])

But three sibling fields were pushed to the detail lines untouched:

if (er.cause) {
  detail.push(['cause', er.cause.message])
}
if (er.stdout) {
  detail.push(['', er.stdout.trim()])
}
if (er.stderr) {
  detail.push(['', er.stderr.trim()])
}

These fields commonly carry secrets: child process failures attach captured stdout/stderr (git and fetch errors echo urls with embedded credentials), and chained causes keep the underlying message. Before this change a cause message of fetch failed for https://user:sekrit@registry.example.com/foo reached the output verbatim.

The gap is wider than the terminal: lib/cli/exit-handler.js prints these same detail lines through console.error on the early exit paths (before the npm instance is set), which bypasses the display layer and its string level redaction entirely, so nothing downstream catches these fields either.

Solution

Apply replaceInfo (the same redactLog used for message, stack and args) to er.cause.message, er.stdout and er.stderr before appending them.

Test Evidence

Added a test in test/lib/utils/error-message.js that builds a default case error whose cause message and captured stdout/stderr contain a url password and a granular token, and asserts neither secret appears in the returned detail while the non secret parts survive. The test fails on the previous code, matching on sekrit in the detail, and passes with this change. Existing snapshot tests are unchanged because redaction is the identity for their plain values. Full file green: npx tap test/lib/utils/error-message.js.

References

  • Redaction already applied to message, stack and args in the same function.
  • The early exit path that prints detail without the display layer: lib/cli/exit-handler.js #logConsoleError.

The top of errorMessage already redacts the error message, stack and
command args, but the default case appended er.cause.message, er.stdout
and er.stderr to the detail lines untouched. Child process output and
chained causes can carry registry or git urls with embedded credentials
and published tokens, and these lines also reach stderr verbatim on the
early exit path in the exit handler, which prints through console.error
and bypasses the display layer redaction entirely.

Run the same redaction over those three fields.
@ManoharPaturi
ManoharPaturi requested a review from a team as a code owner October 4, 2026 02:37
Copilot AI balanced review requested due to automatic review settings October 4, 2026 02:37

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

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.

2 participants