Product1 publisher3 min readPublished
npm v12 turns lifecycle scripts off by default. That only helps if review policy changes too
The cheapest way to run code on a developer's machine now needs approval. Checkmarx expects attackers to move to runtime, and developers to start rubber-stamping the prompt.
The Product Desk · Product desk
Drafted by a language model from the sources cited here and checked against its claim ledger before publication. How we use AISend a correction
What happened
- npm v12 blocks dependency lifecycle scripts by default unless they are explicitly allowed, closing off a common software supply chain attack path.
- Before this change, installing a package from npm caused any lifecycle script bundled with it to run automatically, with no review, no approval and no second look.
- A poisoned package with a malicious postinstall script does not require a developer to do anything other than run npm install.
- In npm v12, lifecycle scripts no longer execute automatically during installation and developers have to approve them first.
- Checkmarx warns attackers may respond by shifting malicious behaviour from install-time scripts to code that executes when compromised packages run, for example when a package is imported or used.
Compiled by The Product DeskSomething wrong?How this is made
Why it matters
npm v12 no longer runs dependency lifecycle scripts automatically during installation; they are blocked unless explicitly allowed, and developers have to approve them first [4][7]. That closes the cheapest known path for getting attacker code onto a developer machine, because until now any script bundled with a package ran on install with no review, no approval and no second look [5].
The economics of the old path were hard to beat. A poisoned package with a malicious postinstall script required nothing from the victim beyond running npm install [6]. Removing that removes an attacker's guarantee of execution, and attacker cost is one of the few levers defenders actually control [8].
What it does not remove is the incentive to compromise the package in the first place [8]. Checkmarx's analysis argues attackers will simply relocate the payload from install-time scripts into code that executes when the compromised package runs [11]. Darren Meyer, security research advocate at Checkmarx [9], put it as follows: "It's a bit like locking a door in a glass house: robbers will just start breaking windows" [12]. His reasoning is about effort rather than capability: lifecycle scripts get used because they are nearly guaranteed to execute, runtime infection takes more work, and with the door locked that extra work becomes worth doing [13].
The second failure mode is procedural, and it is the one operators can actually manage. A prompt is only a checkpoint if someone reads it, and Checkmarx warns the protection weakens if developers routinely allow scripts without sufficient review [14]. Meyer expects exactly that: developers under pressure to ship will get used to approving scripts they know are usually safe, a cursory look is unlikely to catch cleverly hidden malicious content, and the likely end state is approving without reading or finding a way to auto-approve everything [15]. Which means upgrading npm changes the default but not the decision, because the decision still sits with a developer mid-sprint [10].
The recommendation from Checkmarx is not "ignore scripts and call it done." Meyer frames allowlisting as a shared decision between security and engineering rather than a blanket restriction handed down [1]. In practice that means an allowScripts policy that denies lifecycle scripts which are not essential and approves those assessed as acceptably safe, backed by configuration management that makes strict-allow-scripts the default npm behaviour, which he argues reduces alert fatigue while keeping a safety net [2]. The target is not zero scripts, since some packages genuinely need theirs to function; it is fewer unreviewed ones, enforced by default rather than left to individual judgement calls [3].
The operator reading of this is straightforward. If your response to npm v12 is a version bump, you have converted a silent execution risk into an approval queue that your team will learn to clear quickly [15][10]. The durable version is a checked-in allowlist, a default enforced by configuration management rather than by the person typing the install command [2], and detection that assumes payloads now fire at import or use time rather than at install [11].
Worth watching: whether real-world malicious packages start shipping runtime payloads instead of postinstall hooks [11], and whether teams end up with auto-approve workarounds that restore the old behaviour under a new name [15].