One warning line, npm warn install-scripts 1 package had install scripts blocked because they are not covered by allowScripts, covers four different matching failures: a local file: spec your key does not spell the way Arborist stored it, a relative key resolved against the wrong root, store paths written into the manifest by install-strategy=linked, and an allow-scripts config value exported into the lifecycle environment. Three were fixed in npm 12.1.0 and 12.2.1 (npm/cli #9914, #9913, #9941); the npx leak in npm/cli #9968 is still open. Before changing a character of the allowScripts object in package.json, print the specs npm recorded for every package that has a script.
On a patch day last month I checked a defensive claim against my own server before passing it on, running the decoder probe from Node's own test/parallel/test-zlib-invalid-input.js instead of reading node -v. It returned errno=-9 on an EOL Node 20 box: unpatched, read out of the running runtime rather than inferred from a version table. A committed allowScripts key is a claim of the same kind. Whether the script executed is a question about a file on disk.
Why does npm 12 still block a script that is in allowScripts?
- Prove the script ran by reading the file it writes. The npm 12.0.0 breaking-change entry in the v12 changelog tells you to record approvals with
npm install-scripts approveand then runnpm rebuild, because approval never retro-runs anything. A clean exit code only states that the manifest covered what the install asked for, the conditionESTRICTALLOWSCRIPTSfails the gate on. Read the binary in the same shell:
npm rebuild esbuild --foreground-scripts && ls node_modules/esbuild/bin/esbuild
- Copy the key out of
package-lock.json, character for character. Arborist matches local sources against thefile:form thatconsistentResolve()produces, not the dependency range you hand-typed, which is howelectron-winstaller@5.3.0and several hand-writtenfile:variants all failed for the reporter of npm/cli #9900 on npm 12.0.2. Print the real spec next to every package that declares a script:
jq -r '.packages | to_entries[] | select(.value.hasInstallScript) | "\(.key) \(.value.resolved // "registry")"' package-lock.json
- On a local
.tgzor directory dependency, set a version floor before you debug your own typing. PR #9914, "match allowScripts keys for local paths" by martinrrm, is the commit that made tarball and directory sources matchable at all, and it shipped in npm 12.1.0; the v11 line got the same change as #9929 in 11.20.0. Below those versions no spelling works on Windows, because the stored path carries backslashes while the key was parsed on forward slashes.npm -vdecides whether you have a bug or a typo. - Resolve relative keys from the project root, and confirm which root npm picked. #9914 also changed relative
file:keys to resolve against the Arborist project root instead of the process cwd, so a key written from a subdirectory can need rewriting after the upgrade (and the reverse on anything older than 12.1.0). In a monorepo the answer is rarely the directory you are standing in:
npm prefix
- Grep your keys for store paths if
install-strategy=linkedwas ever set. npm/cli #9939 records keys shaped like"file:../../../vue-demi@0.14.10-pGPQNZuy2Yfs8dw1nOAidw/node_modules/vue-demi": true, alongside a duplicate bareesbuildentry; the hash in that path changes on the next install, so the key can never match twice. Fixed by #9941 in 12.2.1. Find every hit and rewrite it asname@version:
jq -r '.allowScripts | keys[]' package.json | grep node_modules
- When the output is
EALLOWSCRIPTSrather than a warning, stop editing package.json. npm/cli #9912 reportednpm error --allow-scripts is not allowed in project-scoped installs.from a script as ordinary as"inst": "cd inner && npm install", because a persistentallow-scriptsvalue in a user-level.npmrcwas exported into the lifecycle environment and read by the inner npm. The discriminator is one install run two ways:cd inner && npm installsucceeds whilenpm run instfails, which is the shape described in--allow-scripts is not allowed in project-scoped installs. Fixed as #9913 in 12.1.0. - Do not expect
--ignore-scriptsto rescue that case. The #9912 reporter states the failure persists with--ignore-scriptsset, because the rejection happens while the inner npm parses config, before any decision about running a script exists. It is the first workaround most threads reach for, and it costs you an install cycle to learn nothing. - Probe the npx path on its own; it is the one still open. npm/cli #9968 shows
npm execandnpxexporting resolved config to child processes asnpm_config_allow_scripts, so a tool launched through npx fails a project-scoped install with the same EALLOWSCRIPTS text. Reproduced on 12.0.2 and open as of this writing, which means a version bump is not the fix. One command prints whether your shell is carrying it:
npx -y -c 'node -e "console.log(process.env.npm_config_allow_scripts)"'
Clear the source with npm config delete allow-scripts --location=user, then run the probe again and expect undefined.
- Hunt for an inherited
falsebefore you add anothertrue. Deny entries win over allow entries, and PR #9490, "recognize allowScripts for local link targets", was validated against exactly that shape:allowScripts: { "file:../testdep": false }on a localfile:dependency. In a workspace the denial can sit in a root manifest nobody on your team edited this quarter:
jq -r '.allowScripts | to_entries[] | select(.value == false) | .key' package.json
- Run the pending list from the repo root, and treat the workspace answer as unverified. The npm-approve-scripts page documents
--allow-scripts-pendingas listing packages not yet covered without modifying package.json, and the same page states plainly that "This command is unaware of workspaces", so an approval taken from inside a workspace directory is a step to prove rather than trust. There is also a naming gap worth settling locally: the 12.0.0 changelog entry saysnpm install-scripts approve, the command page documentsnpm approve-scripts, and #9939's reproduction uses the former.npm help install-scriptsprints which one your install actually has, and that output beats both docs pages. The scope difference between the two commands is laid out innpm approve-scriptsvs--allow-scripts.
Version floors, so you can tell a bug from a typo before you spend an afternoon on key spelling:
| Symptom | Issue / PR | Fixed in npm 12 | v11 line |
|---|---|---|---|
file: or local .tgz key never matches | #9900 / #9914 | 12.1.0 | 11.20.0 (#9929) |
user .npmrc breaks a nested install | #9912 / #9913 | 12.1.0 | backport #9937 |
| linked strategy writes store-path keys | #9939 / #9941 | 12.2.1 | backport #10059 |
npx exports npm_config_allow_scripts | #9968 | open | open |
Wrap-up
The habit that catches all four: after every approval, read the file the script was supposed to write, and keep the pending list empty in CI rather than inspecting it after a failure. An empty array means every install script in the tree is covered; anything else names the packages still waiting on a decision.
npm approve-scripts --allow-scripts-pending --json
Comments
Be the first to comment.