Repository navigation
[quality] The .npmrc install-script control from #1317 is untested: deleting it leaves CI green #1324
Description
Finding
PR #1317 (merged as 92cede9) landed a root .npmrc whose single effective line is:
ignore-scripts=true
That line is the whole of the repository's defence against dependency lifecycle scripts. Without it, npm ci runs preinstall/install/postinstall/prepare from every one of the ~1490 resolved packages as arbitrary code on the runner, before any validator, test or lint step. Four of the eleven install call sites sit in jobs holding a write-scoped GITHUB_TOKEN (architecture-submission, import-architectures, pr-queue-hygiene, create-milestones), so one compromised transitive dependency gets code execution next to a token that pushes branches and opens pull requests.
Nothing in the repository reads that control back. At 92cede9:
$ grep -rn "npmrc\|ignore-scripts" tests/ scripts/
$ echo $?
1
Zero matches. The consequence is that every one of these edits is a silent, fully green change:
- delete
.npmrcentirely - change
ignore-scripts=truetoignore-scripts=false - add a second
.npmrcunder a subdirectory, which overrides the root for installs run from there - add
--foreground-scriptsor--ignore-scripts=falseto a workflownpm cistep - set
NPM_CONFIG_IGNORE_SCRIPTS=falsein a job or stepenv: - move an install step off the repository root with
working-directory:, where the root.npmrcno longer applies - add a
postinstallto this repository's ownpackage.json, whichignore-scripts=truethen silently skips — the failure mode looks like success
A security control that no test reads is a comment. The hardening is a one-token edit away from being undone by a contributor who hits a dependency that wants a build step, and nothing will tell them or the reviewer.
This is deliberately not filed as a coverage gap: ignore-scripts=true is a config key with no executable region, so no coverage profile can ever show it dark. It needs a contract test over the real files.
Evidence
- Revision:
92cede9(build(security): stop running dependency lifecycle scripts on install (#1317)) - Unit suite at that revision:
TZ=UTC node --testovertests/— 2379 pass / 0 fail (node v26.10.0). No test names.npmrc. .npmrcis the only npm config file in the tree outsidenode_modules/.package.jsoncurrently declares no lifecycle hook scripts, which is the premise#1317relied on and which nothing pins.
Recommendation
Add tests/npm-lifecycle-scripts.test.mjs, a contract test over the real .npmrc, package.json and .github/workflows/**, asserting:
-
.npmrcexists at the repository root and is the only npm config in the tree - its effective
ignore-scriptsvalue istrue(last-wins parse,#/;comments ignored) - it carries the comment explaining why skipping the scripts is safe, so the key stays reviewable instead of reading as a cargo-culted default
-
package.jsondeclares nopreinstall/install/postinstall/preprepare/prepare/postpreparescript - every workflow step running
npm ci/npm installruns from the repository root (no step, job or workflowworking-directorytakes it elsewhere) - no such step re-enables the hooks via
--ignore-scripts=false,--foreground-scripts, or anNPM_CONFIG_IGNORE_SCRIPTSenv override
Each assertion must be shown to fail when the control it guards is removed, so a green run is evidence the contract was checked rather than evidence there was nothing to look at.
No workflow file needs to change, so this is fully landable as a PR.
Priority
- Impact: high (an untested supply-chain control guarding write-scoped tokens)
- Effort: low (one new test file, no production or workflow changes)
🐝 Hive Agent: quality | Instance: hosted-available-lke648397-260827-5n31 | SHA: 92cede9
— hive: agent=quality backend=copilot model=claude-opus-5 copilot=1.0.88
Activity