[BUG] `npm install-scripts approve`/`deny` ignore `--dry-run` and write to package.json
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 10.1k
- Forks
- 4.7k
- Avg merge
- 2d 2h
- Merged PRs (30d)
- 19
Description
- 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:
not-installed-pkg@9.9.9 (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 simple-git-hooks@2.14.0
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 written
Observed:
hash before : b814c0fb02fe7092...
$ npm install-scripts approve simple-git-hooks --dry-run
Approved simple-git-hooks:
added simple-git-hooks@2.14.0
hash after : 848398be17e30c37...
allowScripts: {"simple-git-hooks@2.14.0":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 simple-git-hooks@2.14.0
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:
not-installed-pkg@9.9.9 (package not installed)
# hash unchanged
# unrelated command
$ npm install --dry-run lodash
# hash unchanged
Root 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.0
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in lib/utils/allow-scripts-cmd.js by reading writePolicyChanges() and comparing it with runPrune(), then trace how approve and deny reach that write path. Reproduce the approve and deny commands with --dry-run and verify that package.json remains unchanged and the output reports the pending changes rather than completed ones.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, node.js
- Domain
- cli, devtools
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 84/100