Skip to content

fix(install): repeat specs in global allow-scripts suggestion - #9840

Open
Ashish-CodeJourney wants to merge 2 commits into
npm:latestfrom
Ashish-CodeJourney:fix/9835-global-allow-scripts-suggestion
Open

fix(install): repeat specs in global allow-scripts suggestion#9840
Ashish-CodeJourney wants to merge 2 commits into
npm:latestfrom
Ashish-CodeJourney:fix/9835-global-allow-scripts-suggestion

Conversation

@Ashish-CodeJourney

@Ashish-CodeJourney Ashish-CodeJourney commented Aug 4, 2026

Copy link
Copy Markdown

The blocked-install-scripts warning suggested npm install -g --allow-scripts=<pkg>, which has no install targets, so the command falls back to installing the current directory and fails with ENOENT reading package.json for anyone not sitting in a project.

Stop suggesting a command at all: name the flag to add to the install that was already run. Reconstructing the command is not safe — npm.argv carries positional specs only, so flags are silently dropped. Also derive the suggested --allow-scripts values from the policy identity rather than the display name, so git, file and remote deps get a key the matcher accepts.

Fixes: #9835

What / Why

Global installs have no project package.json, so npm install-scripts approve cannot be used. The warning instead points at --allow-scripts, but the command it prints is not runnable:

$ npm install -g esbuild

added 2 packages in 3s
npm warn install-scripts 1 package had install scripts blocked because they are not covered by allowScripts:
npm warn install-scripts   esbuild@0.28.1 (postinstall: node install.js)
npm warn install-scripts
npm warn install-scripts Run `npm install -g --allow-scripts=esbuild` to allow these scripts once, or `npm config set allow-scripts=esbuild --location=user` to allow them for all global installs.

$ npm install -g --allow-scripts=esbuild
npm error code ENOENT
npm error syscall open
npm error path /tmp/tmp.j1pwe1SPOy/package.json
npm error errno -2
npm error enoent Could not read package.json: Error: ENOENT: no such file or directory, open '/tmp/tmp.j1pwe1SPOy/package.json'

npm install -g with no positional specs installs the current directory, which a global installer is generally not sitting in, so the suggested remediation dead-ends on ENOENT.

An earlier revision of this PR rebuilt the command from npm.command and npm.argv. Per review, that is not safe:

  • lib/npm.js:91 sets npm.argv from config.parsedArgv.remain, which is positional specs only. Flags are dropped, so an install from a private registry would be suggested back as a default-registry install that also allows that package's scripts to run.
  • The specs are unquoted, so npm i -g 'pkg@>=1.2.0' turns > into shell redirection.

Review also surfaced a second bug on the same line, present on latest: the suggested --allow-scripts / npm config set allow-scripts values are built from trustedDisplay(node).name, which is a display name. Per workspaces/arborist/lib/script-allowed.js, only registry deps are matched by name — git, file, remote and tarball deps are matched by their resolved source. Following the advice left those scripts blocked. lib/commands/rebuild.js and lib/utils/strict-allow-scripts-preflight.js build their suggestions the same way and have the same bug.

How

remediationLines() in lib/utils/reify-output.js no longer prints a command. It names the flag to add to the install the user already ran, via a new allowScriptsFlag() helper next to the existing configSetAllowScripts() in lib/utils/allow-scripts-remediation.js:

$ npm install -g esbuild
npm warn install-scripts Re-run your install with `--allow-scripts=esbuild` to allow these scripts once, or run `npm config set allow-scripts=esbuild --location=user` to allow them for all global installs.

$ npm install -g esbuild --allow-scripts=esbuild

changed 2 packages in 398ms

Nothing is reconstructed, so no flags can be lost and no spec needs re-quoting into a command.

The values themselves now come from a new shared policyKeyFor(node) in the same module. It builds candidate keys (trusted registry name → node.resolvedresolvedSourceSpecs) and verifies each against the node with the real matches() from script-allowed.js, so the key handed to the user is one the policy accepts rather than one that merely looks right. rebuild.js and strict-allow-scripts-preflight.js use it too. Resolved sources carry shell metacharacters — # in a git committish starts a comment — so the emitted value is quoted when needed.

The same non-working example was documented in npm install-scripts and npm approve-scripts; both are corrected to npm install -g canvas sharp --allow-scripts=canvas,sharp, which is runnable as written.

Tests

Written failing first.

New test/lib/utils/allow-scripts-remediation.js:

  • registry deps are keyed by their trusted name
  • aliased registry deps are keyed by the registered name
  • tarball / file / git deps are keyed by their resolved source
  • falls back to the display name when nothing matches (bundled deps)
  • plain keys are left unquoted; shell-unsafe keys are quoted; single quotes are escaped

test/lib/utils/reify-output.js:

  • global remediation never suggests a spec-less install command
  • global remediation uses resolved sources as policy keys
  • global remediation quotes shell-unsafe policy keys

test/lib/utils/strict-allow-scripts-preflight.js:

  • global error suggests the resolved source for a tarball dep

The two command-replay tests from the earlier revision are removed along with the helper they covered.

Known gap

npm update -g --allow-scripts=<pkg> still will not rerun a blocked script when there is nothing to update — the remediation is only reached on an install that actually reifies the package. Closing that means making approve-scripts work for global installs, which currently throws EGLOBAL at lib/utils/allow-scripts-cmd.js:71. Left out of this PR as a separate change; happy to fold it in if preferred.

References

Fixes #9835

The blocked-install-scripts warning suggested `npm install -g
--allow-scripts=<pkg>`, which has no install targets, so the command
falls back to installing the current directory and fails with ENOENT
reading package.json for anyone not sitting in a project.

Build the suggestion from the command that was actually run and its
positional specs, so `npm install -g esbuild` now suggests `npm install
-g esbuild --allow-scripts=esbuild`. Commands invoked without specs
(`npm update -g`) keep the bare form, which works.

Fixes: npm#9835
@Ashish-CodeJourney
Ashish-CodeJourney requested review from a team as code owners August 4, 2026 11:14
Comment thread lib/utils/allow-scripts-remediation.js Outdated
Comment thread test/lib/utils/reify-output.js Outdated
Comment thread lib/utils/allow-scripts-remediation.js Outdated
`npm install -g esbuild` warned "Run `npm install -g
--allow-scripts=esbuild`", which has no specs and so installs the current
directory, failing with ENOENT reading package.json.

The command cannot be reconstructed either: `npm.argv` carries positionals
only, so flags like `--registry` would be dropped from a suggestion that
also allows that package's scripts to run, and unquoted specs such as
`pkg@>=1.2.0` turn `>` into shell redirection. Suggest the flag to add to
the install the user already ran instead of replaying it.

Also derive the suggested policy keys with the real matcher rather than the
display name. Only registry deps are matched by name; git, file, remote and
tarball deps are matched by their resolved source, so the previous
suggestions left those scripts blocked. Resolved sources contain shell
metacharacters, so the value is quoted when needed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Incomplete remediate prompt npm install -g --allow-scripts=<package>

2 participants