Repository navigation
Vendored npm vex and vendor --check fail after any npm 7–10 npm install on a lockfileVersion 2 lock, because npm drops resolved from the legacy mirror and #813 treats that as an unpatched npm 6 install (regression) #879
Description
[agent] Found by the scheduled npm bug-hunt routine (ledger #302).
Summary
After a vendored scan of an npm lockfileVersion 2 project, an ordinary npm install with npm 7, 8 or 10 re-saves the lock. npm's serializer never writes resolved for a file: resolution in the legacy dependencies mirror, so the mirror node keeps only version and the patched integrity. The packages half is still wired to .socket/vendor/…, and the installed tree is patched.
Since #813 (the #432 fix, f023506), drop_mirror_unwired reads a mirror node with no resolved as "resolves from a non-Socket source". So:
vexrefuses the patch:vendor_unwired/patched_ref_unattributable("… still resolves it to "noresolvedurl", so npm 6 installs the unpatched bytes"), exit 1.vendor --checkexits 1:vendor_check_failed"wiring missing: no lockfile or config references .socket/vendor/npm/ any more, so a fresh install gets the unpatched package".
Both claims are false. The packages entry still references the artifact, a fresh npm 7+ npm ci installs the patched bytes, and npm 6 doesn't install unpatched bytes from that mirror node either: the node keeps the patched integrity, so a cold-cache npm 6 npm ci fails closed with EINTEGRITY (ledger #302 already records this as npm behaviour). That's the same fail-closed outcome #813 accepts for a hosted alias mirror node (redirect_npm_legacy_alias_client), which vex still attests.
Impact
Every npm 7/8 team, and every npm 9–11 team with an existing v2 lock, that vendors a patch and then runs npm install for any reason gets a red vendor --check in CI and loses its VEX statement. The suggested remedy loops: re-running scan --mode vendored re-adds resolved (check passes), and the next npm install strips it again. Hosted mode isn't affected, because npm keeps an http(s) resolved in the mirror.
Repro (Linux, npm 8.19.4, Node 22; a local mock patch API serving one free left-pad patch)
mkdir p && cd p
echo '{"name":"app","version":"1.0.0","dependencies":{"left-pad":"1.3.0"}}' > package.json
npm install # npm 8 → lockfileVersion 2
socket-patch scan --mode vendored --yes $API # exit 0
socket-patch vendor --check # exit 0
socket-patch vex --product pkg:npm/app@1.0.0 -O v.json # exit 0, 1 statement
npm install # no changes; re-saves the lock
# "dependencies": { "left-pad": { "version": "1.3.0", "integrity": "sha512-<patched>" } } ← resolved dropped
head -c 22 node_modules/left-pad/index.js # still patched
socket-patch vendor --check # exit 1: vendor_check_failed "wiring missing"
socket-patch vex --product pkg:npm/app@1.0.0 -O v.json # exit 1: vendor_unwired / patched_ref_unattributable
socket-patch scan --mode vendored --yes $API # re-adds resolved; check passes
npm install # strips it again; check fails againThe npm 6 claim, checked on the re-saved lock: npm6 ci --cache <empty> gives exit 1 with EINTEGRITY wanted sha512-<patched> but got sha512-<registry>.
Expected vs actual
- Expected: a
packagesentry wired to the vendored artifact, plus a mirror node with the matching patchedintegrityand noresolved, counts as wired (or at worst gets an npm 6 caveat warning). That's whatvendor --checkandvexdid before Fix npm 6 installing unpatched aliases (#432) #813. CLI_CONTRACT /vendor --checkpromise a failure only when "a fresh install gets the unpatched package", and none does here. - Actual: exit 1 from both, with a diagnosis saying no lockfile references the artifact.
Matrix (Linux)
| npm | lock | npm install re-save |
vendor --check |
vex |
|---|---|---|---|---|
| 7.24.2 | v2 | strips mirror resolved |
fail | fail |
| 8.19.4 | v2 (plain dep and an lp@npm:left-pad alias) |
strips | fail (3×) | fail |
| 10.9.4 | existing v2 (kept as v2) | strips | fail | fail |
| any | v3 (no mirror) | n/a | pass | pass |
| hosted, 8.19.4 | v2 | keeps http resolved |
n/a | pass |
macOS and Windows weren't probed (probe branches are paused), but the logic is OS-independent.
Bisect
1714299(parent of f023506):vendor --checkexit 0,vexexit 0 on the same re-saved locks.f023506(Fix npm 6 installing unpatched aliases (#432) #813) throughc644ab0(current main): both exit 1. First bad commit: f023506.
Suspect code
crates/socket-patch-core/src/vex/discover/npm.rs:289-301 (drop_mirror_unwired): node.resolved.map_or_else(Located::default, …) turns a missing resolved into "neither vendored nor hosted", then into resolved_elsewhere plus unwired. A mirror node with no resolved whose integrity equals the wired packages entry's (patched) integrity should contest nothing. The vendor --check wiring verdict seems to come from the same discovery, given the identical exit and timing.
Backlog review — 2026-10-08
Priority: P1 → P2. VEX/check incorrectly reject npm v2 locks after npm normalizes them; false negative on a correctly patched install.
Activity
mikolalysenko commentedon Oct 6, 2026
[agent] npm bug-hunt routine (ledger #302): there are two more shapes of this on main
9c43dfc(Linux). It's the same root cause, so this is evidence for the fix rather than a new issue.dependencies.left-pad.resolvedvendor --checkvexpackage-lock.jsonv2 (npm 8.19.4)npm install(Node 24.21)patched_ref_unattributable"noresolvedurl",vendor_unwired) — 2/2 runsnpm-shrinkwrap.jsonv2 (npm 8.19.4npm shrinkwrap)npm installpackage-lock.jsonv2 (npm 8.19.4), controlnpm installSo npm 12 doesn't upgrade an existing v2 lock to v3 on
npm install. It keeps v2 and stripsresolvedfrom thefile:mirror node just as npm 7–10 do, so the affected range is npm 7–12. The shrinkwrap flavour behaves the same. Any fix todrop_mirror_unwiredshould covernpm-shrinkwrap.jsontoo.Separately, npm 12
npm installon a vendored v2 shrinkwrap-only project installs unpatched bytes, because npm 12 ignoresnpm-shrinkwrap.jsonaltogether. That's tracked in #899, not here.Generated by Claude Code
mikolalysenko commentedon Oct 7, 2026
Being fixed in draft PR #1008 (batch fix for open
pm:npmissues).Fix vendored npm v2 mirror without resolved failing vex and vendor --…
mikolalysenko commentedon Oct 7, 2026
[agent] npm bug-hunt re-check on main
05ecc6e(after #970 rewordedvendor --checkfor #900): still reproduces, and the new wording makes this state worse to recover from.After
socket-patch scan --mode vendoredfollowed bynpm installon npm 7.24.2 / 8.19.4 (lockfileVersion 2; npm dropsresolvedfrom the legacydependenciesmirror):vexexits 1 (patched_ref_unattributable→vendor_unwired), as originally reported.vendor --checkexits 1 withwiring missing: no lockfile or config references .socket/vendor/npm/<uuid> any more … re-run \socket-patch vendor` to rewire it. That cause is false:package-lock.jsonstill references the artifact inpackages["node_modules/left-pad"].resolved, andnode_modules` holds the patched bytes.socket-patch vendorprintsNo manifest to vendor from; 1 vendored entry is tracked in the ledger — \socket-patch repair` verifies it.and exits 0, andvendor --checkstill exits 1.repair` doesn't clear it either.socket-patch scan --mode vendored, which rewires the mirror. After that,vendor --checkexits 0.Reproduced twice (npm 7.24.2 and npm 8.19.4, Linux). So the fix here probably also needs
vendor --checkto describe this mirror-only state, or to namescan --mode vendoredas the remedy, until the #813 rule stops firing on it.Generated by Claude Code
Keep a package-lock twin in the #879 shrinkwrap mirror test (#899)