Skip to content

Hosted npm scan doesn't warn about omit-lockfile-registry-resolved=true: the next plain npm install strips the hosted resolved URL but keeps the patched integrity, so every fresh npm ci then fails EINTEGRITY #1871

Closed

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:

  • integrity is still the patched sha512, but resolved is gone, so npm fetches the registry tarball.
  • Every cold-cache npm ci / npm install fails EINTEGRITY (wanted the patched sha512, got the registry one). A warm cache or an existing node_modules hides it locally, so it first shows up in CI.
  • vex no 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 warns redirect_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 set omit-lockfile-registry-resolved=false in 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 install silently 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

Activity

  1. mikolalysenko commented on Oct 30, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Triage: priority:p1 (npm). Not a duplicate: #812 covered replace-registry-host only, and no code on main reads omit-lockfile-registry-resolved (grep -rn omit-lockfile-registry-resolved crates docs finds nothing). The fix belongs beside the layered npm-config reader in patch/redirect/npmrc.rs that #812 added. Eligible for a fix run.


    Generated by Claude Code

  2. mikolalysenko commented on Oct 30, 2026

    @mikolalysenko
    CollaboratorAuthor

    [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

  3. mikolalysenko commented on Oct 30, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Draft PR: #1872


    Generated by Claude Code

  4. added a commit that references this issue on Oct 30, 2026
    025fc7c
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