[BUG] `npm install-scripts approve`/`deny` ignore `--dry-run` and write to package.json
- I have searched the existing issues
- I am using the latest npm
- This is not solely a request to bump a dependency for a CVE
Current Behavior
npm install-scripts approve <pkg> --dry-run and npm install-scripts deny <pkg> --dry-run mutate package.json instead of previewing. dry-run is declared for this command (lib/commands/install-scripts.js: static params = ['all', 'allow-scripts-pin', 'dry-run', 'json']), so a command whose stated purpose is to preview is applying the write.
The sibling subcommand prune gets this right, which suggests the write path was simply not updated: it reads the flag and reports in the conditional.
$ npm install-scripts prune --dry-run
Would remove 1 unused allowScripts entry:
[email protected] (package not installed)approve/deny additionally print in the past tense with wording identical to a real run, so nothing in the output signals that the change was applied:
$ npm install-scripts approve simple-git-hooks --dry-run
Approved simple-git-hooks:
added [email protected]Expected Behavior
With --dry-run, package.json must not be written and the pending change should be reported (ideally using the conditional wording prune already uses). Only a run without --dry-run should persist the allowScripts entry.
Steps To Reproduce
mkdir npm-dryrun && cd npm-dryrun
printf '{"name":"repro","version":"1.0.0","private":true}\n' > package.json
# any dependency declaring an install script (here: postinstall)
npm install simple-git-hooks --no-audit --no-fund
shasum -a 256 package.json # sha256sum on Linux
npm install-scripts approve simple-git-hooks --dry-run
shasum -a 256 package.json # hash differs
cat package.json # allowScripts was writtenObserved:
hash before : b814c0fb02fe7092...
$ npm install-scripts approve simple-git-hooks --dry-run
Approved simple-git-hooks:
added [email protected]
hash after : 848398be17e30c37...
allowScripts: {"[email protected]":true}deny writes the same way:
hash before : 2db672d19eb5be80...
$ npm install-scripts deny simple-git-hooks --dry-run
Denied simple-git-hooks:
removed-pinned-allow [email protected]
added simple-git-hooks
hash after : fb4ddceb2d9e7890...
allowScripts: {"simple-git-hooks":false}Controls, same npm binary, both leave the file untouched:
# sibling subcommand in the same command family
$ npm install-scripts prune --dry-run
Would remove 1 unused allowScripts entry:
[email protected] (package not installed)
# hash unchanged
# unrelated command
$ npm install --dry-run lodash
# hash unchangedRoot cause
writePolicyChanges() in lib/utils/allow-scripts-cmd.js never consults the flag, unlike runPrune() in the same file:
// lib/utils/allow-scripts-cmd.js:275
async writePolicyChanges (groups) {
const pin = this.npm.config.get('allow-scripts-pin') !== false
// ... no `dryRun` is read anywhere in this method
if (updated !== existing) {
pkg.update({ allowScripts: updated })
await pkg.save() // :302 — written unconditionally
}
this.printSummary(summary) // :305 — always past tense
}Compare with the prune path, where the contract is asserted by a comment and then implemented:
// lib/utils/allow-scripts-cmd.js:332
// package.json (never `.npmrc`/CLI policy); `--dry-run` reports without writing.
async runPrune (args) { // :333
const dryRun = !!this.npm.config.get('dry-run') // :341
if (removed.length > 0 && !dryRun) { // :371
await pkg.save() // :376
}
this.printPruneSummary({ removed, dryRun }) // :380
}printSummary() (:308) likewise has no dry-run branch, while printPruneSummary() (:383) selects `${dryRun ? 'Would remove' : 'Removed'} ...` (:394).
Suggested fix, matching the existing prune behavior: read dryRun in writePolicyChanges, skip pkg.save() when it is set, and give printSummary the conditional wording that printPruneSummary already uses.
Environment
- npm: 12.0.2 — also reproduced on 11.19.0
- Node.js: v24.21.0
- OS Name: macOS 26.6.2 (Darwin 25.6.0), arm64
- System Model Name: Mac16,9
- npm config:
; "builtin" config from .../npm/npmrc
prefix = "/opt/homebrew"
; node bin location = .../bin/node
; node version = v24.21.0
; npm version = 11.19.0Source: npm/cli