Repository navigation
Hosted and vendored npm scans break npm 12 allowScripts pins (pkg@x.y.z), so approved install scripts are silently blocked (or every npm ci fails under strict-allow-scripts) #1595
Description
[agent] Found by the scheduled npm bug-hunt routine (ledger #302).
Summary
npm 12 blocks dependency install scripts by default. Projects approve them in package.json#allowScripts, and npm approve-scripts <pkg> writes a version-pinned key by default ("esbuild@0.19.12": true). npm only matches a registry-style key (pkg, pkg@1.2.3) against a lock entry whose resolved is a registry tarball URL (…/<name>/-/<leaf>-<ver>.tgz). It reads the trusted name and version from that URL (@npmcli/arborist/lib/script-allowed.js, isRegistryNode / getTrustedRegistryIdentity).
A hosted scan rewrites resolved to https://patch.socket.dev/patch/npm/t/<uuid>/<leaf>.tgz. A vendored scan rewrites it to file:.socket/vendor/npm/<uuid>/<leaf>.tgz. Neither URL has the registry shape, so the pinned approval stops matching:
- Default npm 12 config: the next
npm ciinstalls the patched package but skips its approvedpostinstall/installscript. npm prints "install scripts blocked because they are not covered by allowScripts". socket-patch exits 0 and prints no warning. For native-addon packages (sharp, bcrypt, esbuild's binary check and so on), the install ends up broken. strict-allow-scripts=true: everynpm cifails (npm error --strict-allow-scripts: 1 package(s) have install scripts not covered by allowScripts: esbuild@0.19.12) until the pin is rolled back.
A name-only key ("esbuild": true) keeps working. The only fixes npm offers are worse. Re-running npm approve-scripts esbuild over a hosted pin replaces the pinned approval with name-only esbuild: true, which weakens the policy. Over a vendored pin, it writes an absolute, machine-specific key ("file:/home/me/proj/.socket/vendor/npm/<uuid>/esbuild-0.19.12.tgz": true) that doesn't match on any other checkout or in CI.
Impact
In any npm 12 project that uses the default (pinned) allowScripts entries, a hosted or vendored scan of a package with install scripts disables those scripts, or breaks every strict install. The scan reports success. vex exits 0 and attests the patch (the files are patched), while the package's install step never ran.
Repro
Run against current main (657fce2), with a local mock of the public patch API (SOCKET_PROXY_URL / SOCKET_API_URL plus --patch-server-url) serving a patch for esbuild@0.19.12 (lib/main.js + one comment line):
mkdir at && cd at
cat > package.json <<'EOF'
{"name":"at","version":"1.0.0","dependencies":{"esbuild":"0.19.12"},
"allowScripts":{"esbuild@0.19.12":true}}
EOF
npm install # npm 12.2.0
rm -rf node_modules && npm ci --loglevel=info | grep 'run esbuild@0.19.12 postinstall' # runs
socket-patch scan --mode hosted # or --mode vendored; exit 0, no warning
rm -rf node_modules && npm ci --loglevel=info
# npm warn install-scripts 1 package had install scripts blocked because they are not covered by allowScripts:
# npm warn install-scripts esbuild@0.19.12 (postinstall: node install.js)
echo strict-allow-scripts=true >> .npmrc
rm -rf node_modules && npm ci # exit 1, --strict-allow-scripts error
socket-patch rollback && rm -rf node_modules && npm ci # exit 0 againExpected vs actual
- Expected: a hosted or vendored scan doesn't change which install scripts npm runs. If it can't keep an
allowScriptsapproval valid, it warns, the way it already warns for the other npm 12 install gates (redirect_npm_allow_remoteforallow-remote,vendor_npm_allow_fileforallow-file, see docs/testing/npm-compatibility.md). docs/testing/npm-compatibility.md says npm 12 is "patched with a plainnpm ci", and the scan's next steps say a reinstall picks up the patched artifacts. Neither mentions that approved install scripts stop running. - Actual: exit 0 and no warning. The scripts are blocked, or with
strict-allow-scriptsthe install fails.
Matrix (Linux, Node 22.22.0, run 2026-10-16)
| npm | mode | allowScripts key |
postinstall before scan | after scan + npm ci |
|---|---|---|---|---|
| 12.0.0 / 12.1.0 / 12.2.0 | hosted | esbuild@0.19.12 |
runs | blocked |
| 12.0.0 / 12.1.0 / 12.2.0 | vendored | esbuild@0.19.12 |
runs | blocked |
| 12.0.0 / 12.1.0 / 12.2.0 | hosted / vendored | esbuild (name-only) |
runs | runs |
| 12.2.0 | hosted | esbuild@0.19.12 + strict-allow-scripts=true |
runs | npm ci exit 1 |
| 11.21.1, 10.9.4, 7.24.2 | hosted / vendored | any | runs | runs (no default-deny) |
Each npm 12 cell reproduced at least twice. macOS and Windows weren't run (probe branches are blocked for this routine). npm's matcher is platform-independent.
First bad version
Not a socket-patch regression: no release reads allowScripts. It became reachable when npm 12.0.0 started blocking install scripts by default.
Suspect code
crates/socket-patch-core/src/hosted/engine.rs:2890is where the npm hosted run emits its other npm 12 install-gate warning (redirect_npm_allow_remote). Nothing there readspackage.json#allowScripts.crates/socket-patch-core/src/vendor/npm_lock.rs:1588(vendor_npm_allow_file) is the vendored counterpart.- Possible directions: warn (or refuse) when a rewritten entry has
hasInstallScript: trueand a pinnedallowScriptskey covers it. Or, for hosted, serve a tarball URL whose path keeps npm's registry shape (/<name>/-/<leaf>-<ver>.tgz), so npm'sversionFromTgzstill derives the trusted name and version.
Activity
mikolalysenko commentedon Oct 16, 2026
[agent] Triaged as p1 (npm). Confirmed on main
657fce2: nothing incrates/socket-patch-core/srcreadspackage.json#allowScripts, so neither the hosted nor the vendored npm rewrite warns. Related to #1597 (pnpm build approvals) but not the same root cause (different matcher and writer), so not clustered.Generated by Claude Code
mikolalysenko commentedon Oct 16, 2026
[agent] New data from the scheduled npm bug-hunt routine (ledger #302), main
657fce2, Linux, Node 22.22.0.npm 12.2.1 (published 2026-10-15,
next-12) still has this bug. Its arborist (10.0.3 → 10.0.4) changesscript-allowed.js, but only fornode_modules/.storelink targets (npm/cli#9939). Registry identity still needs a registry-shapedresolved.esbuild@0.19.12esbuild@0.19.12esbuild@0.19.12esbuild(name only)In every row, the
npm installbefore the scan runs the postinstall without a warning. After the scan, a freshnpm ciprintsinstall scripts blocked because they are not covered by allowScripts: esbuild@0.19.12 (postinstall: node install.js). The patched bytes are installed in every cell.Generated by Claude Code
mikolalysenko commentedon Oct 21, 2026
[agent] Claiming this issue (shared root cause: npm rewrites never check package.json#allowScripts, so a pinned approval silently stops matching the rewritten
resolved). Branch: agent/fix-npm-allow-scripts-pins. Claim-ID: 2026-10-21T12:23:52Z-fb4054Generated by Claude Code
mikolalysenko commentedon Oct 21, 2026
[agent] Draft PR: #1657
Generated by Claude Code
Warn when npm 12 script approvals stop matching
Match npm's allowScripts rules more exactly
Use the shared BOM helper for package.json
Read a BOM-prefixed package-lock.json in hosted
Compare single-version allowScripts pins as npm does