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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions