Skip to content

[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 #9783

Description

@warwickallen

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.0 and 12.0.1 (the current release). On npm 12, git dependencies are blocked by default (EALLOWGIT); once opted in with --allow-git=all, the identical EALLOWSCRIPTS failure occurs. The throw logic in lib/utils/resolve-allow-scripts.js is unchanged on main (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 prepare script (or otherwise must be built from source). A github dependency whose repository already contains its publishable files and has no prepare script (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 ci on CI or a clean machine while an incremental npm install on 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.js throws EALLOWSCRIPTS when a policy is found in the cli/env sources and the install is not global and not skipProjectConfig (the git-dep-prep inner install is a plain npm install, so skipProjectConfig is false).
  • The inner preparation install inherits the outer process's npm_config_* environment, so a .npmrc-origin allow-scripts arrives in the inner process as an env-layer value and is treated the same as a command-line flag.

Suggested fixes

  1. (Preferred) When spawning the git-dep preparation install, strip or neutralise npm_config_allow_scripts from 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 the cli and env sources in resolveAllowScripts is 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.
  2. Alternatively, in resolveAllowScripts, distinguish a value that genuinely came from a command-line flag from one that arrived via npm_config_* env inheritance, and only reject the former in project-scoped installs—though note this would cut against the RFC's stated intent for the env layer.
  3. 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

  • fix(arborist): apply allowScripts gate to scripts pacote runs at extract #9777 (open) touches the same neighbourhood; applying the allowScripts gate to scripts pacote runs at extract for git/directory deps, but is a distinct defect: that PR is about the gate not being applied to prepare at extract, whereas this issue is about a persistent .npmrc policy 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions