npm v12 stopped running install scripts: what changes in your pipeline
Wilson Vargas Martínez 10 minSeptember 21, 2026After a year of self-replicating worms in the registry, npm turned off install scripts, Git dependencies and remote URLs by default. No Node.js line ships that npm yet: you have to ask for it.
Since 8 July 2026, an npm install running npm v12 does not execute your dependencies’ install scripts, does not resolve Git dependencies and does not download tarballs from remote URLs unless you explicitly allow it. CLI version 12.0.0 shipped that day, and 12.0.2 on 27 July. It is the most aggressive change of defaults in the registry’s history, and it arrives after twelve months in which a self-replicating worm compromised first 500 packages and then more than 1,300 versions.
The detail that catches teams off guard: no Node.js line bundles that npm yet. Upgrading Node does not hand you the new defaults. You have to ask for them, and you have to prepare the repository first, because the first visible effect is a broken build.
What happened in npm between September 2025 and August 2026?
Two waves of the same worm, a year apart and an order of magnitude bigger. In the first, CISA issued an alert on 23 September 2025 over more than 500 compromised packages hit by malware named Shai-Hulud: it scanned the environment for GitHub personal access tokens and API keys for AWS, GCP and Azure, exfiltrated them to a public repository through the GitHub API, and then authenticated to the registry as the compromised developer to publish infected versions of their other packages. That last step is what makes it a worm.
The second wave is from August 2026. Advisory AD-2026-009 from Singapore’s cyber security agency, dated 6 August, puts the ChainDrop variant at more than 1,300 compromised package versions, with roughly 2 billion combined monthly downloads. The affected list explains the reach: keyv 6.0.0, flat-cache 6.1.24, file-entry-cache 11.1.6, cacheable-request 13.0.20, cache-manager 7.2.10. Nobody installs those on purpose; they arrive three or four levels down, under ESLint or an HTTP client.
How do attackers get in, if nobody installed a strange package?
Through a legitimate maintainer’s publish token, and 2026 produced three distinct ways of getting one, all documented by the victims.
- Out of the CI runner’s memory. On 11 May an attacker published 84 malicious versions across 42 TanStack packages. The method, per the postmortem: they located the GitHub Actions
Runner.Workerprocess and read/proc/<pid>/mapsand/proc/<pid>/memto extract the OIDC token the runner held in memory because the workflow declaredid-token: write. An outside researcher caught it 20 to 26 minutes later; the versions were deprecated within one hour and 43 minutes. - Out of the developer’s editor. The compromise Red Hat documents in RHSB-2026-006 started on 29 May with a GitHub account taken over through a VS Code extension carrying malware, and ended with 32 packages published under the
@redhat-cloud-servicesnamespace. No Red Hat product was built with those versions, but the vector matters: the laptop’s editor is part of the supply chain. - Without compromising anyone, through name confusion. On 28 May an actor published 14 malicious packages inside a four-hour window, with spoofed repository metadata and inflated version numbers —
1.0.7265— to fake a mature history. The second stage was a roughly 195 KB Bun-compiled binary hunting for AWS credentials in Secrets Manager across 16-plus regions, HashiCorp Vault tokens, npm publish tokens and GitHub Actions secrets.
That third case is what explains npm’s change. The theft happened during installation, through the package lifecycle hooks. The victim’s code never had to call anything: someone typing npm install was enough.
What exactly changed in npm v12?
Three automatic code-execution paths became opt-in, per the 9 June announcement:
- Install scripts. A dependency’s
preinstall,installandpostinstalldo not run unless the package is approved in theallowScriptsfield of yourpackage.json. - Git dependencies.
--allow-gitdefaults tonone: neither direct nor transitive, which closes a code-execution path that could be triggered from an.npmrc. - Remote URLs.
--allow-remotedefaults tonone: HTTPS tarballs outside the registry need authorisation too.
The same release carries other breaking changes worth reviewing before you upgrade: npm view --json now always returns an array, support for npm-shrinkwrap.json is gone, and npm adduser plus the star, stars and unstar commands were removed. The one that breaks pipelines quietly is the --json change, if any script parses that output with jq.
Do I already have npm v12 if I upgraded Node.js?
No. As of today, the Node.js distribution index shows the 26 line bundling npm 11.19.1, the 24 line — the active LTS, per the support schedule — bundling npm 11.19.0, and 22 still on npm 10.9.8. No line carries 12, so the new version is installed separately: npm install -g npm@12, or by pinning it in whatever manager you use.
This has a concrete operational consequence. If your CI uses a node:24 image and trusts the npm it brings, you are running the old defaults while your team believes otherwise. Our practice is to pin the exact npm version in the pipeline and treat it as one more dependency, never inherit it from the base image.
How do I migrate a repository without breaking the build?
By preparing it under npm 11 before touching npm 12. All of the new behaviour is available behind warnings from npm 11.16.0, so the migration can be rehearsed without changing major version:
npm installas usual, so the CLI records which packages carry scripts.npm approve-scripts --allow-scripts-pending, which only lists the pending packages and approves nothing.- Review that list, approve what makes sense with
npm approve-scripts, deny the rest withnpm deny-scripts, and commit the resultingpackage.json. - Run the full pipeline with
--strict-allow-scripts, which applies v12 behaviour while still on npm 11. If it passes, the major upgrade stops being a leap in the dark.
There is npm approve-scripts --all to approve everything pending at once. It works as an initial snapshot in a monorepo with hundreds of dependencies, and beats postponing the migration for six months, but it leaves the job half done: what it approves is today’s state, not a decision. If you use it, keep the list in package.json and review its growth in every pull request — that is where a new package asking to run code at install time becomes visible.
What about native packages like sharp or better-sqlite3?
They are blocked too, through a path that never appears in package.json. A package shipping a binding.gyp triggers an implicit node-gyp rebuild even with no install script declared, so npm treats it as a script and stops it. sharp, better-sqlite3 and canvas are the first cases to surface in any real project.
The failure is loud, which is the good part: the build breaks in the pipeline, not in production. Two reasonable exits. Approve those packages explicitly, leaving a record in the repository of which dependencies are allowed to compile; or prefer distributions with prebuilt binaries where the project offers them, which also shortens image build time.
What if the attacker comes in through the publish token?
npm closed that side in parallel, on dates that are already running. Granular access tokens with the two-factor bypass option lost the ability to perform sensitive account, package and organisation actions from August 2026 — creating or deleting tokens, changing passwords, configuring trusted publishing, managing teams — and from January 2027 they lose direct publishing: they will be limited to reading private packages and staging a publish that waits for human approval with a second factor.
The rest of the pieces appear in the summary GitHub published on 28 July: staged publishing since 22 May, trusted publishing over OIDC extended to CircleCI since 6 April, high-impact accounts held read-only for 72 hours after an email change or the use of a two-factor recovery code since 25 June, and a self-service tool to revoke credentials since 24 June.
The TanStack case tempers the enthusiasm for OIDC: the token was short-lived and they still lifted it out of process memory. Trusted publishing removes the long-lived secret, which is a lot, but the job issuing that token has to be isolated. In the pipelines we build, the publishing job runs no third-party code, external actions are pinned by SHA, and id-token: write lives only in that job, never at workflow level.
How do I avoid installing a compromised version the day it ships?
By putting time between publication and your install. Since 14 July, Dependabot applies a three-day cooldown by default on version updates, while security updates still arrive immediately. That asymmetry is the right one: the TanStack window lasted under two hours, and the cooldown would have covered all of it without delaying a real security patch.
The rest is hygiene you should already have and rarely do in full: npm ci in every pipeline instead of npm install, the lockfile committed and reviewed in each pull request, and no floating tags in production. A ^ range in package.json is harmless while the lockfile rules; it stops being harmless the day someone runs npm install on the server.
What do I do if I already installed a compromised version?
Treat the machine as compromised and rotate from a different one. CISA’s recommendations and the Singapore advisory agree on the order: remove the affected versions from development and CI environments, rotate GitHub personal access tokens, SSH keys, cloud credentials and npm publish tokens, review repositories for commits or new public repositories nobody created, and block outbound traffic to the domains used for exfiltration, with webhook.site at the top.
Two details teams skip, at a high price. Rotating credentials from the same infected machine hands the new ones to the attacker. And secret scanning with branch protection enabled is what turns the next attempt into an alert instead of an incident; CISA asks for it explicitly, alongside phishing-resistant multi-factor authentication.
Where we would start this week
- Inventory the scripts. Run
npm approve-scripts --allow-scripts-pendingin every repository and count how many packages ask to run code at install time. The number is surprising. - Pin the npm version in CI instead of inheriting it from the base image, and rehearse with
--strict-allow-scriptsbefore jumping to v12. - Move publishing to OIDC for internal packages, with the publishing job isolated and third-party actions pinned by SHA.
- Turn on the Dependabot cooldown and confirm security updates still arrive without delay.
- Rehearse a rotation with a stopwatch: how long it takes today to rotate every publish token and cloud credential a team holds. That number is your real response time.
How we help at Athrun Data Intelligence
A 30-minute call to review your repositories’ supply chain: which dependencies run code at install time, which long-lived tokens are still alive, and how exposed your publishing job is. If it fits, we leave the npm v12 migration done with the script inventory committed to the repository, publishing isolated over OIDC, and a rotation procedure rehearsed against a stopwatch rather than written in a document.
Sources
- GitHub Blog — Disrupting supply chain attacks on npm and GitHub Actionshttps://github.blog/security/supply-chain-security/disrupting-supply-chain-attacks-on-npm-and-github-actions/
- GitHub Changelog — Upcoming breaking changes for npm v12https://github.blog/changelog/2026-06-09-upcoming-breaking-changes-for-npm-v12/
- GitHub Changelog — npm install-time security y deprecación de bypass2fahttps://github.blog/changelog/2026-07-08-npm-install-time-security-and-gat-bypass2fa-deprecation/
- npm Docs — Changelog de la CLI v12https://docs.npmjs.com/cli/v12/using-npm/changelog/
- GitHub Community — Preparing for npm v12: install scripts and non-registry sources become opt-inhttps://github.com/orgs/community/discussions/198547
- CISA — Widespread Supply Chain Compromise Impacting npm Ecosystemhttps://www.cisa.gov/news-events/alerts/2025/09/23/widespread-supply-chain-compromise-impacting-npm-ecosystem
- Cyber Security Agency of Singapore — Advisory AD-2026-009 (Shai-Hulud / ChainDrop)https://www.csa.gov.sg/alerts-and-advisories/advisories/ad-2026-009/
- TanStack — Postmortem del compromiso de la cadena de suministro en npmhttps://tanstack.com/blog/npm-supply-chain-compromise-postmortem
- Red Hat — RHSB-2026-006, compromiso de paquetes @redhat-cloud-serviceshttps://access.redhat.com/security/vulnerabilities/RHSB-2026-006
- Microsoft Security Blog — Typosquatted npm packages used to steal cloud and CI/CD secretshttps://www.microsoft.com/en-us/security/blog/2026/05/28/typosquatted-npm-packages-used-steal-cloud-ci-cd-secrets/
- Node.js — Índice de distribuciones con el npm empaquetado en cada versiónhttps://nodejs.org/dist/index.json
- Node.js — Versiones y calendario de soportehttps://nodejs.org/en/about/previous-releases
Who wrote it
Sound familiar? Request the diagnostic.
You tell us the challenge and within 24 business hours we tell you in writing whether it is viable and where to start. No commitment.