fix(hosted): never redirect bundled npm entries; warn on misses - #189
Open
Mikola Lysenko (mikolalysenko) wants to merge 1 commit into
Open
fix(hosted): never redirect bundled npm entries; warn on misses#189Mikola Lysenko (mikolalysenko) wants to merge 1 commit into
Mikola Lysenko (mikolalysenko) wants to merge 1 commit into
Conversation
Bundled (`inBundle`, legacy `bundled`) package-lock entries are extracted from their parent's tarball at install time — npm ignores their resolved/integrity — yet `scan --mode hosted` rewrote them anyway. The hosted URL then appeared in the lockfile text, so the run counted the dep as redirected, persisted a ledger record, and `--vex` attested not_affected over bytes that never install. The npm rewriter now skips bundled entries with the vendored backend's loud stays-UNPATCHED warning (`redirect_npm_bundled_instance_skipped`), so they are never rewritten, never counted redirected, and never attested. The rewriter also now matches lock entries by the package they stand for — the entry's `name` field when present, mirroring the vendored `entry_name` — so an alias install (`npm i alias@npm:real`) of the patched package is redirected instead of silently dropped, and an entry that merely shares the key name (`npm i <name>@npm:other`, the fork-substitution pattern) is never hijacked to the upstream patched artifact. Link (workspace) entries are skipped with a warning, and a granted dep matching no lock entry finally warns `redirect_npm_entry_not_found` — parity with the pnpm/berry/uv rewriters — instead of vanishing from the redirected count with an empty warnings array. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Wenxin Jiang (Wenxin-Jiang)
approved these changes
Aug 14, 2026
| tmp.path().join("package.json"), | ||
| r#"{ "name": "consumer", "version": "0.0.0", "dependencies": { "parent": "2.0.0" } }"#, | ||
| ) | ||
| .unwrap(); |
There was a problem hiding this comment.
You might want to replace usages of unwrap with expect in the future. expect lets you have a message specific to the error and helps in debugging.
ex. https://github.com/darakian/ddh/blob/aac9046fbfe302c64e180a8a88de3c262cd1a1a0/src/main.rs#L262
I assume this is claude'd code so probably not relevant for this PR but maybe something to encode in a skill.
Jon (darakian)
approved these changes
Aug 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Two audit findings (2026-08-13 hosted-mode audit) in the npm hosted rewriter (
rewrite_npm_lock/rewrite_npm_v2_deps):Bundled entries were rewritten, confirmed, and VEX-attested though npm never installs them from the rewritten URL (high).
inBundle: trueentries in the v2/v3packagesmap (and legacybundled: truein the v1/v2dependenciestree) are extracted from their parent's tarball at install time — npm ignores theirresolved/integrity. The rewriter matched them anyway, so the hosted URL landed in the lockfile text,run_redirect's content-based confirmation gate counted the dep as redirected, persisted a ledger record, and--vexattestednot_affectedover bytes that never install. Concrete failure: a lock with the patched dep only as"node_modules/parent/node_modules/foo": { "inBundle": true, ... }reportedredirected: 1and produced a false VEX attestation whilenpm cikept installing the unpatched bundled copy.Alias installs were silently dropped and the npm rewriter had no entry-not-found warning (medium).
npm i my-alias@npm:foo@1.0.0keys the lock entry by the alias with the real package inname. Discovery is alias-aware, but the rewriter matched only the key suffix, so the granted patch never landed — withredirected: 0and an empty warnings array, unreadable in CI. The same key-only matching also meant a fork substitution (npm i foo@npm:other) could be hijacked back to the upstream patched artifact whenever the versions coincided.Fix
The rewriter now mirrors the vendored backend's
entry_namematching and refusal semantics, so both backends agree on what a lock entry stands for:namefield when present, else the path after the lastnode_modules/; keys outsidenode_modules/(project root, workspace source dirs) are never candidates. Alias installs of the patched package now redirect; fork substitutions are never hijacked.inBundle: true/ legacybundled: trueentries are skipped with the loud stays-UNPATCHED warningredirect_npm_bundled_instance_skipped(same wording asvendor_bundled_instance_skipped). Because the confirmation gate is content-based, a skipped entry is consequently never counted redirected, never ledger-recorded, and never VEX-attested.link: trueentries are skipped withredirect_npm_link_entry_skipped.redirect_npm_entry_not_found— parity with the pnpm/berry/uv rewriters — covering the version-drift and not-installed cases.Skips set
matched_any, so the specific loud warning fires instead of a misleading not-found. For name-less entries the matching is behaviorally identical to the old key-suffix match (redirect goldens are byte-identical). Reruns stay idempotent:matched_anyis set before the already-redirected short-circuit, so a second run neither warns nor re-edits.Known accepted noise (pre-existing tradeoff, same as
redirect_pnpm_entry_not_found): in a rare multi-lock project where the dep lives only in another PM's lockfile,redirect_npm_entry_not_foundcan fire spuriously; cross-lock warning hygiene is owned by the hosted-warnings lane. In v2 hybrid locks a bundled dep may warn twice (packages+dependenciestrees), symmetric with the existing double-edit behavior for rewrites.No findings skipped.
Testing
patch::redirect::tests(bundled skip + loud warning, partial coverage with a rewritable sibling, legacybundled: true, alias redirect, fork non-hijack, entry-not-found on version drift) — all RED-verified against the pre-fix rewriter, and independently mutation-checked in review (disabling the inBundle guard fails both bundled tests; removing name-aware matching fails both alias tests).redirect_inbundle_only_dep_is_skipped_not_confirmedpinning the end-to-end invariant:--jsonenveloperedirected == 0, bundled warning surfaced,package-lock.jsonbyte-untouched, hosted URL absent.cargo test -p socket-patch-core --lib(2081),--test redirect_golden(byte-identical goldens),cargo test -p socket-patch-cli --test in_process_redirect(24),--test e2e_redirect_npm_builddefault legs and both--ignoredreal-npm legs (freshnpm ciinstalls patched bytes + VEX verifies; tampered tarball fails closed). Clippy-D warningsand rustfmt clean on touched targets/files.🤖 Generated with Claude Code
Note
High Risk
Changes hosted redirect confirmation, ledger, and VEX semantics for npm lockfiles (bundled/alias/link cases); incorrect matching could still mis-redirect or miss patches in CI.
Overview
The npm hosted lock rewriter no longer treats every
packageskey suffix as the patched package. It matches entries bynamewhen present (alias installs) or the path after the lastnode_modules/, and only considers keys undernode_modules/so workspace/root entries are not edited.Bundled copies (
inBundle: true/ legacybundled: true) andlink: trueentries are skipped with explicit warnings instead of rewritingresolved/integrity. That avoids counting a dep as redirected, ledger-recording it, or VEX-attesting when npm still installs unpatched bytes from a parent tarball.When a granted override matches nothing in the lockfile, the rewriter now emits
redirect_npm_entry_not_found, aligned with other PM rewriters. Unit tests and a CLI subprocess test cover bundled-only skips (redirected: 0, untouched lockfile).Reviewed by Cursor Bugbot for commit cd5895a. Configure here.