Skip to content

Vendored npm scan wires file: tarballs that npm ≥ 11.14 refuses under allow-file=root (transitive deps) or allow-file=none, so every npm ci fails EALLOWFILE while scan, vendor --check and vex report success with no warning #969

Description

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

Summary

npm 11.14.0 added allow-file (all / root / none; default all). It gates every dependency that resolves to a local tarball file. Vendored mode rewires package-lock.json entries to file:.socket/vendor/npm/<uuid>/<pkg>.tgz, which are exactly those specs. When the project's npm config sets allow-file=root and the vendored package is a transitive dependency, or sets allow-file=none for any vendored package, npm ci / npm install refuse the lock:

npm error code EALLOWFILE
npm error Fetching non-root packages of type "file" have been disabled     # root
npm error Fetching packages of type "file" have been disabled              # none

socket-patch doesn't look at allow-file at all. scan --mode vendored exits 0 with success and no warning, vendor --check passes, and lockfile-only vex attests not_affected. The first sign of trouble is the project's next install, in CI.

(allow-file=root does admit a vendored direct dependency. In the repro, left-pad installs patched under root and is-number, pulled in by to-regex-range, is refused.)

Impact

  • A project that hardens its npm config, the same family of settings as npm 12's allow-git=none / allow-remote=none defaults, gets a broken lock from a "successful" vendored scan, and every install fails until someone traces EALLOWFILE back to the vendored file: entries.
  • It fails closed (nothing unpatched is installed). But the run promises an installable commit, and vendor --check, the verification gate, passes it.
  • The docs say the opposite: docs/testing/npm-compatibility.md ("allow-file defaults to all: vendored file: tarballs install unchanged"), docs/ecosystems.md and CLI_CONTRACT ("Vendored mode is unaffected … npm gates [file: specs] by allow-file (default all), never allow-remote") cover only the default. Hosted mode has a full contract for the matching allow-remote case: it reads every npm config layer, and an explicit non-all value is respected and warned about (redirect_npm_allow_remote) with the install-time remedy.

Repro (Linux, main 9c43dfc, local mock patch API serving is-number@7.0.0 and left-pad@1.3.0)

mkdir t && cd t
echo '{"name":"t","version":"1.0.0","private":true,"dependencies":{"to-regex-range":"5.0.1","left-pad":"1.3.0"}}' > package.json
npm i
printf 'allow-file=root\n' > .npmrc          # or allow-file=none
socket-patch scan --mode vendored --yes --json --api-url $MOCK --org test-org --api-token x
#   exit 0, status success, vendor.summary.applied 2, warnings []
socket-patch vendor --check --json            # exit 0, success
socket-patch vex -O vex.json --product pkg:npm/t@1.0.0   # exit 0, not_affected (from the lock, before any install)
rm -rf node_modules && npm ci --cache "$(mktemp -d)"
#   npm error code EALLOWFILE / Fetching non-root packages of type "file" have been disabled; exit 1

npm_config_allow_file=root npm ci (env layer, no .npmrc) fails the same way.

Expected vs actual

  • Expected: treat allow-file the way hosted mode treats allow-remote. If any npm config layer (project / user / global .npmrc, npm_config_allow_file) has an effective allow-file of none, or root while a vendored entry isn't a root dependency, and the installed npm is ≥ 11.14, the vendored run should warn (or refuse) and name the setting and remedy (allow-file=all in .npmrc, or npm ci --allow-file=all). vendor --check should flag the same thing, and the docs' "vendored is unaffected" claim should be scoped to the default.
  • Actual: silent success from scan, vendor --check and vex, then EALLOWFILE on every install.

OS × version

OS npm (Node) allow-file scan / vendor --check npm ci
Linux 12.2.0 (24.21) root, transitive success, no warning (x2) EALLOWFILE, exit 1 (x2)
Linux 12.2.0 (24.21) none success, no warning (x2) EALLOWFILE, exit 1 (x2)
Linux 12.2.0 (24.21) root, direct dep only success installs patched (pass)
Linux 12.2.0 (24.21) all (default) success installs patched (pass)
Linux 11.21.0 (22.22) root / none success, no warning EALLOWFILE, exit 1
Linux 10.9.4 / ≤ 11.13 n/a (no such setting; checked npm 11.8 / 11.9 / 11.10 / 11.12 / 11.13 config definitions) — not affected
macOS / Windows not probed npm's own config gate, no OS-specific path

First affected npm: 11.14.0, where allow-file first appears in @npmcli/config definitions. Not a socket-patch regression: no release has ever checked allow-file.

Suspect code

  • crates/socket-patch-core/src/vendor/npm_flavor.rs:343 (vendor_npm_any): rewires lock entries to file: specs without reading npm's allow-file.
  • crates/socket-patch-core/src/hosted/engine.rs:1405-1417 (npm_allow_remote): the precedent (config-layer reader plus redirect_npm_allow_remote warning). Its doc comment, and CLI_CONTRACT.md:117, state that vendored is unaffected because allow-file defaults to all.
  • crates/socket-patch-cli/src/commands/vendor.rs:1020-1046 (vendor --check wiring probe): passes the entry.

Related: #812 (hosted pin broken by replace-registry-host=always; the same "npm config makes the rewrite uninstallable, scan reports success" class).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions