Skip to content

Vendored npm rollback / remove / vendor --revert on a lockfileVersion 2 lock fail after any npm install, yet half-revert the lock: packages goes back to the registry while the legacy mirror keeps the patched integrity, and npm 6 then fails EINTEGRITY #1937

Closed

Description

[agent] Found by the scheduled npm bug-hunt routine (ledger #302).

Summary

This is the revert-side sibling of #879. After a vendored scan of an npm lockfileVersion 2 project, an ordinary npm install (npm 7, 8, 10 or 12 on an existing v2 lock) re-saves the lock. npm never writes resolved for a file: resolution in the legacy dependencies mirror, so the mirror node keeps only version and the patched integrity. #879 taught vex and vendor --check that this is still wired, and both pass on main. The revert engine wasn't updated:

  • rollback, remove <purl> and vendor --revert warn vendor_lock_entry_drifted "lock entry /dependencies/left-pad was re-resolved since vendoring (no resolved field); left alone" and keep the vendored state. rollback and remove exit 1. rollback --json reports rollback_failed: "nothing was rolled back: 1 patch failed", with the event "lockfile wiring drifted; vendored state left untouched".
  • That claim is false: the same run did rewrite the packages["node_modules/left-pad"] entry back to the registry resolved and integrity. The mirror node still carries the patched integrity. The lock is left half-reverted, and the ledger plus .socket/vendor/ artifact are kept.

Impact

  • Every npm 7/8 team, and every npm 9–12 team with an existing v2 lock, that vendors a patch and then runs npm install once can no longer roll it back or remove it with one command. That's the normal workflow: the scan's own "Next steps" tells them to run npm install.
  • The failed run leaves a lock that no longer matches itself. npm 7+ npm ci installs the upstream bytes from packages. npm 6 reads the mirror and fails npm ci with EINTEGRITY (wanted sha512-<patched> but got sha512-<registry>). After that, vendor --check exits 1 ("vendored wiring drifted … node_modules/left-pad still resolve left-pad@1.3.0 outside the vendored artifact"), even though the user asked for the patch to be removed.
  • The remedy it prints ("undo the drift … or re-vendor, and re-run vendor --revert") does work: scan --mode vendored re-adds the mirror resolved, and a revert then succeeds. But it means re-vendoring a patch you're trying to remove, and the next npm install before the revert brings the problem back.

Repro (Linux, Node 22.23.4, npm 8.19.4; a local mock patch API serving one free patch for the real npmjs left-pad@1.3.0)

mkdir p && cd p && git init -q
echo '{"name":"p","version":"1.0.0","private":true,"dependencies":{"left-pad":"1.3.0"}}' > package.json
npm install                                   # npm 8 → lockfileVersion 2
socket-patch scan --mode vendored --yes $API  # exit 0; both packages entry and mirror get resolved file:.socket/vendor/...
git add -A && git commit -qm vendored
rm -rf node_modules && npm ci                 # patched
npm install                                   # re-saves the lock: drops `resolved` from dependencies.left-pad (git diff: 1 deletion)
socket-patch vendor --check                   # exit 0 (fixed by #879)
cp package-lock.json before.json
socket-patch rollback; echo $?                # exit 1: "Kept vendored state …: lockfile wiring drifted; vendored state left untouched"
diff <(jq . before.json) <(jq . package-lock.json)
#  packages["node_modules/left-pad"].resolved:  file:.socket/vendor/npm/<uuid>/left-pad-1.3.0.tgz → https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz
#  packages["node_modules/left-pad"].integrity: sha512-<patched> → sha512-<registry>
#  dependencies.left-pad: unchanged (version + PATCHED integrity, no resolved)
ls .socket/vendor/state.json                  # still there
npx npm@6 ci --cache "$(mktemp -d)"           # EINTEGRITY wanted sha512-<patched> but got sha512-<registry>
socket-patch vendor --check; echo $?          # 1: vendored wiring drifted

remove pkg:npm/left-pad@1.3.0 (exit 1, "1 matching entry was drift-kept … re-run scan --mode vendored to normalize, then remove again") and vendor --revert (exit 0, "Kept 1 drifted package") leave exactly the same half-reverted lock.

Expected vs actual

Matrix (Linux only: probe branches are still paused for this routine)

npm lock after npm install rollback vendor --revert remove
7.24.2 v2 mirror resolved dropped fail (exit 1, half-revert) not run not run
8.19.4 v2 dropped fail (3×) fail (exit 0, half-revert) fail (exit 1, half-revert)
8.19.4 v2 + overrides: {"left-pad": "$left-pad"} dropped fail not run not run
10.9.9 existing v2 (--lockfile-version 2) dropped fail not run not run
12.2.1 existing v2 dropped fail not run not run
8.19.4 v2, npm ci only (no npm install) resolved kept pass not run not run
10.9.9 / 12.2.1 v3 (no mirror) n/a pass pass pass

Same result on main cbbbdb9 and the v5.0.1 release, so it isn't a recent regression. It has probably been there since the npm mirror revert records were added; I didn't bisect further.

Suspect code

Related: #879 (the same npm serialization on the check/vex side, fixed), #1155 / #1187 (version-moved-off drift-keep for npm), #989 (the revert-engine tracking issue).

Activity

  1. mikolalysenko commented on Nov 1, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Triage: priority:p1 (npm). Not a duplicate: #879 fixed the check/vex side of the v2 legacy-mirror shape; this is the revert side, which still treats the mirror node's missing resolved as drift after already rewriting packages. No open PR covers it.


    Generated by Claude Code

  2. mikolalysenko commented on Nov 1, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Claiming this issue (shared root cause: npm revert treats a resolved-less legacy-mirror node holding the vendored integrity as drift, after already reverting the packages entry). Branch: agent/fix-npm-v2-mirror-revert. Claim-ID: 2026-11-01T14:21:38Z-090425


    Generated by Claude Code

  3. mikolalysenko commented on Nov 1, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Draft PR: #1938


    Generated by Claude Code

  4. added 2 commits that reference this issue on Nov 1, 2026
    b512d3a
    f6f0ee6
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions