You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
After npm install <pkg>@<other version> moves a vendored npm package off its patched version, scan --prune, vendor --revert, remove and rollback all drift-keep it, so vendor --check stays red and every remedy it names loops #1155
[agent] Found by the scheduled npm bug-hunt routine (ledger #302).
Summary
Vendor a package, then upgrade or downgrade it with npm (npm install ms@2.1.3 over a vendored ms@2.1.2). The lock entry node_modules/ms now resolves the new version from the registry, and nothing references .socket/vendor/npm/<uuid>/ any more. After that:
vendor --check exits 1: dependency removed: no lockfile resolves pkg:npm/ms@2.1.2 any more (it was upgraded or uninstalled) … run socket-patch scan --mode vendored --prune to revert the vendored entry.
vendor --revert exits 0: lock entry node_modules/ms was re-resolved since vendoring (now resolved to https://registry.npmjs.org/ms/-/ms-2.1.3.tgz); left alone and Kept 1 drifted package … undo the drift and re-run vendor --revert to finish.
remove pkg:npm/ms@2.1.2 exits 1: 1 matching entry was drift-kept …; re-run scan --mode vendored to normalize, then remove again. Re-running the scan changes nothing.
"Undo the drift" would mean downgrading back to the vulnerable version. The only way out is deleting .socket/vendor/npm/<uuid>/ and editing .socket/vendor/state.json by hand.
The Bun routine found this first and handed it to the npm ledger. Bun's text and binary locks behave the same. #1132 (Bun bun.lockbremoval), #1140 (uv) and #1142 (Pipenv) are the per-backend removal variants. On npm, a plain npm uninstall is reverted correctly (the vendor_lock_entry_removed arm from #665/#689). Only the re-resolved case loops.
Impact
Any project that upgrades a vendored dependency (the normal way to drop a patch once upstream ships a fix, and something Dependabot or Renovate does by itself) is left with a committed orphan tarball and a ledger entry. vendor --check then fails CI permanently, and none of the remedies socket-patch suggests can clear it. Nothing is installed unpatched (the new version installs from the registry), so this is a stuck-state / CI-gate bug, not a silent unpatching.
Repro (Linux, real npm, local mock of the public patch proxy serving a free patch for ms@2.1.2)
A downgrade (left-pad 1.3.0 → npm install left-pad@1.2.0) behaves the same.
Expected vs actual
Expected: CLI_CONTRACT.md:137 says scan --prune leg (b) reverts "EVERY ledger entry whose dependency is no longer in the lockfile graph". pkg:npm/ms@2.1.2 is no longer in the graph. vendor --check (and the doc comment on unwired_check_failure, crates/socket-patch-cli/src/commands/vendor.rs:396) names "upgraded or uninstalled" and scan --prune as the fix. So prune, and also vendor --revert / remove / rollback, should drop the ledger entry and the artifact, leaving the user's upgraded lock entry alone.
Actual: the npm revert classifies the re-resolved entry as vendor_lock_entry_drifted, so outcome.drift_skipped() keeps the artifact and ledger entry. Every command reports "kept", and vendor --check keeps sending you to the same prune.
(On npm 8 the pre-upgrade vendor --check is already red because of #879. That's unrelated.) macOS and Windows weren't probed: the code path is platform-independent lock JSON handling.
Not bisected: main's history is grafted, so commit bisects aren't possible. The "upgraded or uninstalled … --prune" check text came with #970/#1050.
Suspect code
crates/socket-patch-core/src/vendor/npm_lock.rs:1209-1231 (revert_one_record): when the live entry exists but its resolved no longer points into our uuid dir, it always warns vendor_lock_entry_drifted and returns. It doesn't check whether the live entry still names the vendored version. An entry whose version differs from rec.original/rec.new (or, more generally, one that Discovery::vendor_entry_in_use already calls Some(false)) is the same "dependency left the graph" case as the removed arm above it (lines 1189-1197), and should be handled like LOCK_ENTRY_REMOVED_CODE: nothing to restore, artifact releasable.
crates/socket-patch-cli/src/commands/vendor.rsrun_vendor_gc (~4202) then lists the entry under kept, disagreeing with the in-use verdict that vendor --check uses.
[agent] Found by the scheduled npm bug-hunt routine (ledger #302).
Summary
Vendor a package, then upgrade or downgrade it with npm (
npm install ms@2.1.3over a vendoredms@2.1.2). The lock entrynode_modules/msnow resolves the new version from the registry, and nothing references.socket/vendor/npm/<uuid>/any more. After that:vendor --checkexits 1:dependency removed: no lockfile resolves pkg:npm/ms@2.1.2 any more (it was upgraded or uninstalled) … run socket-patch scan --mode vendored --prune to revert the vendored entry.scan --mode vendored --pruneexits 0 and reverts nothing.--jsonshowsgc.keptVendoredEntries: ["pkg:npm/ms@2.1.2"]. The human output just says "No patches available" (that's the Humanscan --mode vendored --prunesilently skips the vendored GC when no remaining package has a patch, so annpm uninstalled vendored entry is never reverted (exit 0), while--jsonreverts it andvendor --checkkeeps pointing at that same command #1127 gap; with another patched package still installed it prints the "kept 1 drifted vendored entry … undo the drift" line instead).vendor --revertexits 0:lock entry node_modules/ms was re-resolved since vendoring (now resolved to https://registry.npmjs.org/ms/-/ms-2.1.3.tgz); left aloneandKept 1 drifted package … undo the drift and re-run vendor --revert to finish.remove pkg:npm/ms@2.1.2exits 1:1 matching entry was drift-kept …; re-run scan --mode vendored to normalize, then remove again. Re-running the scan changes nothing.rollback pkg:npm/ms@2.1.2exits 1:Kept vendored state … lockfile wiring drifted.vendor --checkis still 1."Undo the drift" would mean downgrading back to the vulnerable version. The only way out is deleting
.socket/vendor/npm/<uuid>/and editing.socket/vendor/state.jsonby hand.The Bun routine found this first and handed it to the npm ledger. Bun's text and binary locks behave the same. #1132 (Bun
bun.lockbremoval), #1140 (uv) and #1142 (Pipenv) are the per-backend removal variants. On npm, a plainnpm uninstallis reverted correctly (thevendor_lock_entry_removedarm from #665/#689). Only the re-resolved case loops.Impact
Any project that upgrades a vendored dependency (the normal way to drop a patch once upstream ships a fix, and something Dependabot or Renovate does by itself) is left with a committed orphan tarball and a ledger entry.
vendor --checkthen fails CI permanently, and none of the remedies socket-patch suggests can clear it. Nothing is installed unpatched (the new version installs from the registry), so this is a stuck-state / CI-gate bug, not a silent unpatching.Repro (Linux, real npm, local mock of the public patch proxy serving a free patch for ms@2.1.2)
A downgrade (
left-pad1.3.0 →npm install left-pad@1.2.0) behaves the same.Expected vs actual
scan --pruneleg (b) reverts "EVERY ledger entry whose dependency is no longer in the lockfile graph".pkg:npm/ms@2.1.2is no longer in the graph.vendor --check(and the doc comment onunwired_check_failure,crates/socket-patch-cli/src/commands/vendor.rs:396) names "upgraded or uninstalled" andscan --pruneas the fix. So prune, and alsovendor --revert/remove/rollback, should drop the ledger entry and the artifact, leaving the user's upgraded lock entry alone.vendor_lock_entry_drifted, sooutcome.drift_skipped()keeps the artifact and ledger entry. Every command reports "kept", andvendor --checkkeeps sending you to the same prune.Matrix (main
c4235a2, Linux, Node 22.22 / Node 24.21 for npm 12)npm uninstall(control)--jsonprune, this run)(On npm 8 the pre-upgrade
vendor --checkis already red because of #879. That's unrelated.) macOS and Windows weren't probed: the code path is platform-independent lock JSON handling.Not bisected: main's history is grafted, so commit bisects aren't possible. The "upgraded or uninstalled …
--prune" check text came with #970/#1050.Suspect code
crates/socket-patch-core/src/vendor/npm_lock.rs:1209-1231(revert_one_record): when the live entry exists but itsresolvedno longer points into our uuid dir, it always warnsvendor_lock_entry_driftedand returns. It doesn't check whether the live entry still names the vendored version. An entry whoseversiondiffers fromrec.original/rec.new(or, more generally, one thatDiscovery::vendor_entry_in_usealready callsSome(false)) is the same "dependency left the graph" case as the removed arm above it (lines 1189-1197), and should be handled likeLOCK_ENTRY_REMOVED_CODE: nothing to restore, artifact releasable.crates/socket-patch-cli/src/commands/vendor.rsrun_vendor_gc(~4202) then lists the entry underkept, disagreeing with the in-use verdict thatvendor --checkuses.