[BUG] A user/global `.npmrc` `allow-scripts` setting is forwarded to git-dependency preparation as an env-layer policy and fails the install with `EALLOWSCRIPTS`
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 10.1k
- Forks
- 4.7k
- Avg merge
- 2d 2h
- Merged PRs (30d)
- 19
Description
Summary
Setting allow-scripts in a user or global .npmrc (which npm's own warning explicitly recommends for global installs) makes any later
npm install/npm ci of a project that has a git dependency requiring preparation fail with:
npm error git dep preparation failed
npm error npm error code EALLOWSCRIPTS
npm error npm error --allow-scripts is not allowed in project-scoped installs.
Add the entries to the "allowScripts" field in package.json, or to .npmrc, instead.
The error is self-contradictory from the user's point of view: it says to put the entry in .npmrc, and it is in .npmrc. The root cause is that npm prepares a git dependency by spawning an inner npm install, forwarding the outer config to it as npm_config_* environment variables. The inner install reads allow-scripts from its env layer, which resolveAllowScripts treats as a command-line/env policy and rejects in a project-scoped install, even though the user never passed --allow-scripts and the value originated in a persistent .npmrc.
Environment
- npm: reproduced on
11.17.0and12.0.1(the current release). On npm 12, git dependencies are blocked by default (EALLOWGIT); once opted in with--allow-git=all, the identicalEALLOWSCRIPTSfailure occurs. The throw logic inlib/utils/resolve-allow-scripts.jsis unchanged onmain(same line numbers). - node:
v26.5.0 - OS: Linux (WSL2, Ubuntu)
How the offending setting gets there
This isn't an exotic hand-edit. Installing a global package with an install script prints (npm 11):
npm warn allow-scripts <pkg>@x.y.z (postinstall: node install.cjs)
npm warn allow-scripts Run `npm install -g --allow-scripts=<pkg>` to allow
these scripts once, or `npm config set allow-scripts=<pkg> --location=user`
to allow them for all global installs.
Following the second suggestion writes allow-scripts=<pkg> to the user .npmrc. From that point on, every git-dependency preparation in every project on the machine fails.
Minimal, self-contained reproduction (no network / external repo required)
R=$(mktemp -d); mkdir -p "$R/dep" "$R/app"
# A git dependency that has a `prepare` script, so npm must run
# "git dep preparation" (an inner `npm install`) to build it.
cd "$R/dep"
cat > package.json <<'EOF'
{ "name": "gitdep", "version": "1.0.0",
"scripts": { "prepare": "node -e \"process.exit(0)\"" } }
EOF
git init -q && git add -A && git -c user.email=t@t -c user.name=t commit -qm init
# A consumer project that depends on it via git.
cd "$R/app"
cat > package.json <<EOF
{ "name": "app", "version": "1.0.0",
"dependencies": { "gitdep": "git+file://$R/dep" } }
EOF
# A USER-level .npmrc containing the entry exactly as
# `npm config set allow-scripts=... --location=user` would write it.
# (--userconfig isolates the repro from the real user .npmrc; writing the
# same line to ~/.npmrc behaves identically.)
echo "allow-scripts=whatever" > "$R/userrc"
npm install --userconfig "$R/userrc" --cache "$R/cache" # cold cache => real git-dep prep
# npm 12: add --allow-git=all (git deps are otherwise blocked with EALLOWGIT)
Actual result
npm error code 1
npm error git dep preparation failed
npm error command <NODE>/bin/node <NODE>/lib/node_modules/npm/bin/npm-cli.js \
install --force --cache=<...> --prefer-offline=false --prefer-online=false \
--offline=false --no-progress --no-save --no-audit --include=dev \
--include=peer --include=optional --no-package-lock-only --no-dry-run
npm error npm warn using --force Recommended protections disabled.
npm error npm error code EALLOWSCRIPTS
npm error npm error --allow-scripts is not allowed in project-scoped installs.
Add the entries to the "allowScripts" field in package.json, or to .npmrc, instead.
Expected result
The install completes. An allow-scripts value that lives in a persistent .npmrc is, by definition, not a command-line flag, and the docs describe the user/global .npmrc as a valid home for it in global contexts. It should not be re-interpreted as a forbidden CLI/env policy simply because npm chose to shell out to an inner npm install to prepare a git dependency.
Notes on the trigger
- Requires the git dependency to actually undergo preparation; i.e. it has a
preparescript (or otherwise must be built from source). A github dependency whose repository already contains its publishable files and has nopreparescript (e.g.is-odd) is packed directly, runs no inner install, and does not trip the bug. - Requires a cold git-dep cache, so the preparation actually runs. A warm cache serves the prepared dep and hides the failure, which makes this present intermittently and confusingly (it breaks
npm cion CI or a clean machine while an incrementalnpm installon a warm cache "works"). - Independent of where the setting lives: reproduced identically with the entry in the user
.npmrc, the global (etc/npmrc) file, and even the consumer project's own.npmrc. There is therefore no persistent-config location that both silences the global-install script warning and leaves git-dep installs working.
Root cause (source pointers)
lib/utils/resolve-allow-scripts.jsthrowsEALLOWSCRIPTSwhen a policy is found in thecli/envsources and the install is not global and notskipProjectConfig(the git-dep-prep inner install is a plainnpm install, soskipProjectConfigis false).- The inner preparation install inherits the outer process's
npm_config_*environment, so a.npmrc-originallow-scriptsarrives in the inner process as an env-layer value and is treated the same as a command-line flag.
Suggested fixes
- (Preferred) When spawning the git-dep preparation install, strip or neutralise
npm_config_allow_scriptsfrom the child environment. The preparation install already runs with a deliberately curated flag set (--force --no-save --no-audit ...), so script policy for the inner build could be passed explicitly rather than inherited ambiently. This seems the cleanest fix: the grouping of thecliandenvsources inresolveAllowScriptsis deliberate per RFC 868 (env vars are meant to be rejected alongside CLI flags in project-scoped installs), so the defect is the ambient env inheritance into the inner install, not the source classification itself. - Alternatively, in
resolveAllowScripts, distinguish a value that genuinely came from a command-line flag from one that arrived vianpm_config_*env inheritance, and only reject the former in project-scoped installs—though note this would cut against the RFC's stated intent for theenvlayer. - At minimum, fix the error message: when the value came from
.npmrc(directly or via env inheritance), do not tell the user to "add the entries to.npmrc"; that is where it already is, and the advice sends them in a circle.
Related
- npm/cli#9777 (open) touches the same neighbourhood; applying the
allowScriptsgate to scripts pacote runs at extract for git/directory deps, but is a distinct defect: that PR is about the gate not being applied toprepareat extract, whereas this issue is about a persistent.npmrcpolicy being misclassified as a CLI/env policy in the inner preparation install and aborting it.
Workaround (for anyone hitting this)
Do not persist allow-scripts in a user/global .npmrc. Instead pass it only at the moment of the global install that needs it:
npm install -g <pkg> --allow-scripts=<pkg>
This approves the global install's script without leaving a persistent setting that later breaks git-dependency preparation in unrelated projects.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with lib/utils/resolve-allow-scripts.js and trace the inner npm install used for git-dependency preparation, especially how npm_config_allow_scripts is inherited. Reproduce with the cold-cache script and a persistent user or global .npmrc; done means that installation completes without EALLOWSCRIPTS while preserving the intended project-scoped policy checks.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, nodejs
- Domain
- cli, tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100