Repository navigation
Description
[agent] Found by the scheduled npm bug-hunt routine (ledger #302).
Summary
When the project .npmrc sets omit-lockfile-registry-resolved=true (npm ≥ 8), a hosted scan pins the lock entry (resolved = hosted URL, integrity = patched sha512), writes allow-remote=all and exits 0. The only warning is redirect_npm_allow_remote. The pin works for the first npm ci. But the next lock-saving npm command, even a no-op npm install with nothing to change, drops resolved for every registry-spec dependency. npm's arborist does this whatever the URL's host (shrinkwrap.js: node.isRegistryDependency → overrideResolves() returns undefined). The lock is then left half-patched:
integrityis still the patched sha512, butresolvedis gone, so npm fetches the registry tarball.- Every cold-cache
npm ci/npm installfailsEINTEGRITY(wanted the patched sha512, got the registry one). A warm cache or an existingnode_moduleshides it locally, so it first shows up in CI. vexno longer sees a hosted reference: "no hosted or vendored patch references were found", exit 2.
This is the same class as #812 (replace-registry-host=always rewrote the hosted pins). #812 was fixed by reading the setting from every npm config layer and warning redirect_npm_replace_registry_host. omit-lockfile-registry-resolved has no such check. A re-scan does re-pin the entry, so the project can recover, but nothing tells the user to do that or why the build broke.
Impact
A project that commits .npmrc with omit-lockfile-registry-resolved=true (a common setting for registry-mirror portability) gets a scan that reports success. The first routine npm install after it (adding an unrelated dep, or a no-op) leaves a lock that can't install anywhere with a cold cache. That breaks CI, and vex silently stops attesting the patch.
Repro (Linux, npm 10.9.4; local mock of the patch API)
mkdir r && cd r
echo '{"name":"r","version":"1.0.0","dependencies":{"left-pad":"1.3.0"}}' > package.json
echo 'omit-lockfile-registry-resolved=true' > .npmrc
npm install
socket-patch scan --mode hosted --yes --json $API # exit 0, warnings: [redirect_npm_allow_remote] only
npm ci # ok, patched bytes installed
socket-patch vex $API -O /dev/null # ok, 1 statement
npm install # no-op install
node -e "e=require('./package-lock.json').packages['node_modules/left-pad'];console.log(e.resolved, e.integrity)"
# -> undefined sha512-fkOX6TIS... (resolved gone, patched integrity kept)
socket-patch vex $API -O /dev/null # exit 2: no hosted or vendored patch references
rm -rf node_modules && npm ci --cache "$(mktemp -d)"
# -> npm error code EINTEGRITY ... wanted sha512-fkOX6TIS... but got sha512-XI5MPzVN...
socket-patch scan --mode hosted --yes $API # re-pins (recovery works)$API = --api-url http://127.0.0.1:18080 --api-token fake --org test-org --patch-server-url http://127.0.0.1:18080 (a local mock serving /patches/batch, /by-package, /view, /package and the hosted tarball). The npm behaviour depends only on the dependency's spec type (a registry range/version), not on the URL host, so real patch.socket.dev pins are affected the same way.
Expected vs actual
- Expected: as with
replace-registry-host(docs/ecosystems.md, npm section: "the hosted run reads it from the same env / project / user / global / builtin layers and warnsredirect_npm_replace_registry_host"), a hosted run should detect an npm config that will undo its pins and warn with a remedy: remove the setting or setomit-lockfile-registry-resolved=falsein the project.npmrc, or use vendored mode. It could also refuse. The scan's "Commit .npmrc and package-lock.json to keep the hosted patches" next step implies the pin survives ordinary npm use. - Actual: no warning, exit 0. The first
npm installsilently turns the pin into an uninstallable lock.
Vendored mode isn't affected: file: specs keep resolved. npm update re-resolving a pin is a separate, known non-bug, and it leaves a consistent registry lock. This case leaves an inconsistent one.
OS × version
| OS | npm | socket-patch | Reproduces |
|---|---|---|---|
| Linux | 8.19.4 | main 5ca5623 |
yes |
| Linux | 10.9.4 | main 5ca5623 |
yes (twice, two projects) |
| Linux | 10.9.4 | v5.0.1 | yes |
| Linux | 12.2.1 | main 5ca5623 |
yes |
| macOS / Windows | — | — | untested (probe branches unavailable this run). The npm logic is platform-independent. |
npm 6 and 7 don't have the setting. Not bisected: the behaviour goes back to at least v5.0.1, and the hosted run has never checked this key.
Suspect code
crates/socket-patch-core/src/patch/redirect/npmrc.rs:284(OuterSettingforreplace_registry_host) and:664(effective_replace_registry_host): the layered npm-config reader that the Hosted npm scan pins a hosted tarball URL that npm rewrites to the registry under replace-registry-host=always, so every npm ci / npm install then fails E404 while the scan reports success #812 warning uses.omit-lockfile-registry-resolvedneeds the same reader and warning.- npm side:
@npmcli/arborist/lib/shrinkwrap.js(meta.resolved = overrideResolves(resolved, options)whennode.isRegistryDependency) andlib/override-resolves.js.
Activity
mikolalysenko commentedon Oct 30, 2026
[agent] Triage:
priority:p1(npm). Not a duplicate: #812 coveredreplace-registry-hostonly, and no code onmainreadsomit-lockfile-registry-resolved(grep -rn omit-lockfile-registry-resolved crates docsfinds nothing). The fix belongs beside the layered npm-config reader inpatch/redirect/npmrc.rsthat #812 added. Eligible for a fix run.Generated by Claude Code
mikolalysenko commentedon Oct 30, 2026
[agent] Claiming this issue (shared root cause: hosted npm run never reads omit-lockfile-registry-resolved from the npm config layers, so it cannot warn that npm will strip the hosted pin). Branch: agent/fix-npm-omit-lockfile-resolved. Claim-ID: 2026-10-30T01:21:05Z-a2acea
Generated by Claude Code
mikolalysenko commentedon Oct 30, 2026
[agent] Draft PR: #1872
Generated by Claude Code
Warn when npm will strip hosted lock pins