ITADN
npm/cli/Issues

[BUG] strict-allow-scripts false-positives on extraneous (edgeless) registry nodes

#9680Openmanzoorwanijk 创建于 2026-06-27
BugNeeds TriageallowScripts
M
manzoorwanijkcommented
### Is there an existing issue for this? - [x] I have searched the existing issues ### This issue exists in the latest npm version - [x] I am using the latest npm ### This is not just a request to bump a dependency for a CVE - [x] This is not solely a request to bump a dependency for a CVE ### Current Behavior With `strict-allow-scripts=true` and a name-only deny entry such as `"allowScripts": { "esbuild": false }`, `npm install` aborts with `ESTRICTALLOWSCRIPTS`, claiming a package has an install script "not covered by allowScripts" — even though that package is explicitly denied by name and is `extraneous` (no incoming edges, slated for pruning, its install script will never run). The extraneous node exists only as a nested `package-lock.json` entry, not on disk. ``` npm error code ESTRICTALLOWSCRIPTS npm error --strict-allow-scripts: 1 package(s) have install scripts not covered by allowScripts: npm error esbuild@0.25.12 (install: (install scripts present)) ``` This reproduces under the default (hoisted) install strategy. The precondition is a registry node nested under a **workspace's** `node_modules` that nothing depends on: `buildIdealTree` prunes such an orphan when it is top-level, but retains it when it is nested inside a workspace, so the strict preflight sees it. The strict gate and the advisory listing disagree on the identical node: `npm install-scripts ls` reports the tree as fully covered ("No packages with unreviewed install scripts" — it walks the loaded actual tree), while the strict preflight (which walks `buildIdealTree`) surfaces the orphan as unreviewed. The bug was first observed with two `esbuild` nodes in a workspace monorepo, where one copy is depended on and another is an orphan leftover: | node | edgesIn | isRegistryDependency | extraneous | isScriptAllowed verdict | |---|---|---|---|---| | esbuild@0.27.2 (depended on) | 10 | `true` | `false` | `false` (deny matches — OK) | | esbuild@0.25.12 (orphan, nested under a workspace) | **0** | **`false`** | **`true`** | **`null` (unreviewed → error)** | The orphan's lockfile entry has a valid registry tarball URL (`https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz`) and `"extraneous": true`. ### Expected Behavior A package that is explicitly denied by a name-only `allowScripts` entry, or that is extraneous and will be pruned before any script runs, must not gate the install under `strict-allow-scripts`. The strict gate and `npm install-scripts ls` should agree on the same node. ### Steps To Reproduce Self-contained CLI reproduction. `core-js` stands in for `esbuild` (zero dependencies, declares a `postinstall`). The nested orphan lockfile entry is injected to model the stale entry that real workspace installs can leave behind; the bug is that `npm install` then false-positives on it. ```bash mkdir -p /tmp/sas-repro/packages/app && cd /tmp/sas-repro # 1. a workspace project on the default (hoisted) install strategy cat > package.json <<'JSON' { "name": "root", "version": "1.0.0", "private": true, "workspaces": ["packages/app"] } JSON cat > packages/app/package.json <<'JSON' { "name": "app", "version": "1.0.0", "dependencies": { "left-pad": "1.3.0" } } JSON npm install --ignore-scripts --no-audit --no-fund # 2. inject an orphan registry node nested under the workspace (nothing depends on it) npm install core-js@3.38.0 --ignore-scripts --no-audit --no-fund --prefix ./.tmp node -e ' const fs = require("fs"); const cj = JSON.parse(fs.readFileSync(".tmp/package-lock.json")).packages["node_modules/core-js"]; const l = JSON.parse(fs.readFileSync("package-lock.json")); l.packages["packages/app/node_modules/left-pad/node_modules/core-js"] = cj; fs.writeFileSync("package-lock.json", JSON.stringify(l, null, 2)); ' rm -rf .tmp node_modules # 3. deny core-js install scripts + enable the strict gate cat > package.json <<'JSON' { "name": "root", "version": "1.0.0", "private": true, "workspaces": ["packages/app"], "allowScripts": { "core-js": false } } JSON echo 'strict-allow-scripts=true' > .npmrc # 4. install fails, even though core-js is denied and is an extraneous orphan npm install --no-audit --no-fund ``` Observed: ``` npm error code ESTRICTALLOWSCRIPTS npm error --strict-allow-scripts: 1 package(s) have install scripts not covered by allowScripts: npm error core-js@3.38.0 (install: (install scripts present)) ``` while the advisory listing reports the same tree as clean: ``` $ npm install-scripts ls No packages with unreviewed install scripts. ``` For contrast, when the same `core-js` is reached through a real dependency edge (so `isRegistryDependency=true`), the name-only deny matches and `npm install` succeeds under the strict gate. ### Environment - npm: 12.0.0-pre.1 (`latest` branch), happens in v11 as well - Node.js: v24.17.0 - OS Name: macOS (Darwin 25.5.0) - System Model Name: Mac17,6 - install strategy: default (hoisted); a workspace is required to retain the orphan
5 条评论