Skip to content

fix: redact json output values one at a time so urls cannot corrupt it - #10041

Open
Sanjays2402 wants to merge 1 commit into
npm:latestfrom
Sanjays2402:fix/issue-9873
Open

Sanjays2402 wants to merge 1 commit into
npm:latestfrom
Sanjays2402:fix/issue-9873

Conversation

@Sanjays2402

Copy link
Copy Markdown

Fixes #9873.

I ran into this while generating an SBOM: any project whose tree contains a package with a URL in its deprecation message (e.g. @esbuild-kit/esm-loader, deprecated as "Merged into tsx: https://tsx.hirok.io") makes npm ls --json --long print nothing to stdout and exit 1. No error on the console, just the debug-log path on stderr, so it silently breaks downstream tooling that consumes the JSON.

The culprit is redactValue in lib/utils/display.js. It serialized the whole document to compact JSON and ran the result through redactLog as one big string. @npmcli/redact's URL matcher then matched from the https:// inside one value across the JSON punctuation around it (up to the next @, e.g. a scoped _id like @esbuild-kit/esm-loader@2.6.5), treated the span as embedded credentials, and redacted it, producing JSON that no longer parses. The parse error was swallowed by the output handler, hence the empty stdout and exit 1.

The fix redacts each string value on its own instead: the object is normalized with a JSON.stringify/JSON.parse round trip (so toJSON values like Dates keep working), then a small recursive walk applies redactLog per string. A URL in one value can no longer swallow its neighbors, while passwords in URLs are still redacted as before.

This also addresses #9112, which has the same root cause (npm search --json output corrupted the same way).

Repro (before the fix, npm ls --json --long exits 1 with 0 bytes on stdout; after, exit 0 with valid JSON):

mkdir repro && cd repro
npm init -y
# package @scope/dep-a with "deprecated": "Merged into tsx: https://tsx.hirok.io"
npm ls --json --long

Tests: added a regression test in test/lib/utils/display.js covering a URL-bearing deprecation message next to a scoped _id, arrays, Date round-tripping, and that URL passwords still get redacted. The new test fails on the old code (Unexpected token '*' ... is not valid JSON) and passes with the fix. Full test/lib/utils/display.js suite (71 asserts), plus test/lib/commands/ls.js and test/lib/commands/search.js, all green; eslint clean on both touched files.

Previously the whole compact JSON document was passed through redactLog, so a url inside one string value (e.g. a deprecation message) could match across the surrounding JSON up to the next @ and corrupt the output, making commands like npm ls --json --long print nothing and exit 1.
@Sanjays2402
Sanjays2402 requested a review from a team as a code owner September 25, 2026 04:19
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.

[BUG] npm ls --json --long outputs nothing and exits 1 when a deprecation message contains a URL (redaction corrupts compact JSON)

1 participant