npm 12 shipped on 8 July 2026 with install scripts off by default: the CI fix

npm 12 is GA and tagged latest. Install scripts no longer run unless you approve them.

Read time
12 min
Word count
1.8K
Sections
9
FAQs
8
Share
A security gate intercepting software package cubes on a conveyor into a build pipeline
npm 12 blocks dependency lifecycle scripts unless you approve them in package.json.
On this page · 9 sections
  1. What changed, precisely
  2. Why this happened: the Shai-Hulud problem
  3. The migration, in the order that works
  4. The token deadlines nobody has diarised
  5. India-specific considerations
  6. What this does not fix
  7. FAQ
  8. How eCorpIT can help
  9. References

Summary. GitHub made npm v12 generally available on 8 July 2026 and tagged it latest. Three npm install behaviours that ran automatically are now opt-in: allowScripts defaults to off, --allow-git defaults to none, and --allow-remote defaults to none. Implicit node-gyp builds are blocked too, so a package with a binding.gyp and no explicit install script still stops. All three have shipped behind warnings since npm 11.16.0, which means the warnings have been in your logs for weeks. The trigger is not a date on a calendar. It is the next CI job that pulls a fresh npm, and latest now means v12. Context for why GitHub did this: CISA reported in September 2025 that the self-replicating Shai-Hulud worm compromised over 500 npm packages, harvesting GitHub Personal Access Tokens and AWS, Google Cloud Platform and Microsoft Azure keys. It spread through postinstall. For a team in India, where PayScale puts the average DevOps engineer salary at ₹995,999 as of May 2026 across 978 profiles, an afternoon spent on npm approve-scripts is cheaper than a red pipeline during a release.

The uncomfortable part: this is the rare breaking change where the safe default is the one that breaks your build. If your install is currently silent, you have dependencies executing code you have never audited.

What changed, precisely

GitHub announced the changes on 9 June 2026 and turned them on with the v12 release on 8 July 2026. From the release note: "npm v12 is now generally available and tagged latest. This major release turns on the install-time security defaults we announced in June."

Default Old behaviour New behaviour in v12
allowScripts preinstall, install, postinstall ran from every dependency Blocked unless explicitly allowed in your project
Implicit node-gyp A binding.gyp triggered an implicit node-gyp rebuild Blocked, even with no explicit install script
prepare scripts Ran for git, file and link dependencies Blocked the same way
--allow-git Git dependencies resolved freely Defaults to none; direct and transitive both blocked
--allow-remote Remote URLs such as https tarballs resolved freely Defaults to none; direct and transitive both blocked
--allow-file, --allow-directory Resolved freely Unchanged in v12

That last row matters for monorepos. GitHub is explicit: "The related --allow-file and --allow-directory flags are not changing their defaults in v12." Local workspace links are not what this release is aimed at.

The --allow-git change closes a specific hole, and the reasoning is worth reading carefully: it "closes a code-execution path where a Git dependency's .npmrc could override the Git executable, even with --ignore-scripts." If your mitigation strategy to date has been --ignore-scripts in CI, that sentence is about you. It was not airtight.

The version numbers you need

Not every flag landed at once, which matters when you pin npm in CI:

  • --allow-remote is available in npm 11.15.0 and later.
  • All three changes are visible behind warnings in npm 11.16.0 and later.

GitHub's preparation advice is one sentence long: "Upgrade to npm 11.16.0 or later, run your normal install, and review the warnings."

Why this happened: the Shai-Hulud problem

Install scripts are how the npm ecosystem's worst year propagated. CISA's alert of 23 September 2025 describes it plainly: "A self-replicating worm, publicly known as 'Shai-Hulud', has compromised over 500 packages."

The mechanics explain the default change better than any argument about developer experience. Per CISA, after initial access the malware "scanned the environment for sensitive credentials," then "targeted GitHub Personal Access Tokens (PATs) and application programming interface (API) keys for cloud services, including Amazon Web Services (AWS), Google Cloud Platform (GCP), and Microsoft Azure." It then exfiltrated them, uploaded them to a public repository, and, in CISA's words, "used an automated process to rapidly spread by authenticating to the npm registry as the compromised developer, injecting code into other packages, and publishing compromised versions to the registry."

A worm that spreads by publishing needs code execution on the machines that install. postinstall gave it that, from any package anywhere in a transitive tree. CISA's remediation list included pinning "npm package dependency versions to known safe releases produced prior to Sept. 16, 2025" and rotating all developer credentials immediately. npm 12 removes the execution primitive rather than asking every team to keep pinning after the fact.

This is the same class of problem as agent tooling running unreviewed commands, which we covered in our notes on hardening AI coding agents against shell injection. The pattern repeats: something convenient runs code on your behalf, and the blast radius is whatever that process can reach.

The migration, in the order that works

1. Upgrade to npm 11.16.0 first, not straight to 12

Do this on a developer machine before touching CI. Run your normal install and read the warnings. This is the only step that tells you the size of the job without breaking anything.


            npm install -g npm@11.16.0
npm install
          

2. List what would be blocked


            npm approve-scripts --allow-scripts-pending
          

GitHub: "To see what would be blocked, run npm approve-scripts --allow-scripts-pending."

Expect more entries than you think. Native modules, analytics packages and anything that downloads a binary at install time will all appear, and so will transitive dependencies you have never heard of.

3. Approve what you trust, deny the rest


            npm approve-scripts
npm deny-scripts
          

Per GitHub: "Then allow the packages you trust with npm approve-scripts and block the rest with npm deny-scripts. The resulting allowlist is written to package.json and should be committed."

Commit that allowlist. It is now a reviewable artifact, and a pull request that adds a package to it is a pull request that deserves a second pair of eyes. That is the actual win here, and it only pays off if the review is real rather than a rubber stamp.

4. Handle git and remote dependencies deliberately

If your install resolves a Git dependency or an https tarball, it stops in v12 unless you pass --allow-git or --allow-remote. Before adding the flag, ask why the dependency is not coming from the registry. A fork you never upstreamed and a tarball on someone's S3 bucket are both worth fixing rather than allowlisting.

5. Only then upgrade CI

Once the allowlist is committed and your local install is clean, move CI. GitHub's framing: "After that, only the scripts you approved keep running once you upgrade. Anything you leave unapproved will stop."

Failure you will see Cause Fix
A native module is missing at runtime Implicit node-gyp rebuild blocked Approve the package, or use a prebuilt binary
Install succeeds, app fails later A postinstall that fetched an asset never ran Approve it, or vendor the asset
npm install cannot resolve a dependency Git dependency, --allow-git now none Publish it to the registry, or pass --allow-git
A tarball dependency fails to resolve --allow-remote now none Move it to the registry, or pass --allow-remote
CI is fine, one developer is broken They upgraded npm ahead of the team Pin the npm version across the team and CI

That last row is the one that wastes an afternoon. Pin npm explicitly in CI rather than inheriting whatever latest resolves to, because latest is now v12 and it moved under you on 8 July.

The token deadlines nobody has diarised

The same 8 July release note starts a second deprecation, and it has real dates attached. Granular access tokens configured to bypass 2FA are being wound down in two steps.

First, account and package management. GitHub: "npm granular access tokens configured to bypass 2FA will be unable to perform sensitive account, package, and organization management actions once the change is rolled out." That covers creating or deleting tokens, generating recovery codes, changing your password, email, profile or 2FA configuration, changing package access, maintainers or trusted publishing configuration, and managing organization and team membership. GitHub: "We expect this change to take effect in early August 2026."

Early August is roughly two weeks out from this article's publication. If a script in your org rotates npm tokens using a 2FA-bypass GAT, it stops working then.

Second, publishing. GitHub: "2FA-bypass tokens will also lose the ability to publish directly. Their publishing surface will be reduced to reading private packages and staging a publish, where a package only becomes public after a human 2FA approval." The expected date: "We expect this change to take effect around January 2027."

Change Expected date What to do instead
Install-time defaults on Shipped 8 July 2026 Commit an allowlist via npm approve-scripts
GAT 2FA-bypass loses account and org management Early August 2026 Perform those operations interactively with 2FA
GAT 2FA-bypass loses direct publish Around January 2027 Trusted publishing (OIDC) or staged publishing

GitHub's guidance for the publishing change is specific: "plan to move automated publishing to trusted publishing (OIDC) or staged publishing with a human approval step, rather than a long-lived publish token." If you publish packages from CI with a long-lived token in a secret, that is now a project with a deadline on it, not a preference.

India-specific considerations

The cost case is easy to make here because the work is bounded. PayScale puts the average DevOps engineer salary in India at ₹995,999 as of May 2026, based on 978 salary profiles last updated 5 May 2026, with a base range of ₹402,000 to ₹30,00,000. The migration is an audit plus an allowlist plus a CI pin. Compare that against CISA's post-incident list from September 2025, which required rotating all developer credentials, auditing every package-lock.json and yarn.lock in the tree, and hunting cached copies of bad versions in artifact repositories. One of those is a planned afternoon. The other is an unplanned week.

Services organisations running many client codebases on shared runners have a specific exposure: one client's approved allowlist is not another's, and a shared base image with a global npm can quietly upgrade every project at once. Pin npm per project. For teams standardising this across a fleet, our QA and test automation practice treats the install step as part of the pipeline contract rather than an implementation detail.

If you are also weighing runtimes, the install-script surface differs across them, and our Bun vs Node.js backend runtime decision covers where each lands. Teams already modernising their toolchain may want to sequence this alongside the work in our Next.js 16.3 toolchain notes rather than as a separate disruption.

What this does not fix

Blocking install scripts removes one execution path. It does not stop a malicious package from running code when you import it, which is the obvious next move for anyone building the next worm. The allowlist is only as good as the review attached to it, and an allowlist that everyone approves without reading is a slower way to reach the same place. Treat npm approve-scripts as the start of a dependency review habit, not the end of one.

FAQ

How eCorpIT can help

eCorpIT is a CMMI Level 5 certified technology organisation in Gurugram whose senior engineering teams run Node.js pipelines for clients across fintech, healthcare and ecommerce. We audit which install scripts your tree actually executes, build and review the allowlist rather than rubber-stamping it, pin npm across shared runners so one client's upgrade does not surprise another, and move CI publishing off long-lived tokens before the January 2027 deadline. If your pipeline is currently green because nobody has upgraded npm yet, contact us and we will scope the audit against your real dependency tree.

References

  1. npm install-time security and GAT bypass2fa deprecation - GitHub Changelog, 8 July 2026.
  1. Upcoming breaking changes for npm v12 - GitHub Changelog, 9 June 2026.
  1. Widespread Supply Chain Compromise Impacting npm Ecosystem - CISA Alert, 23 September 2025.
  1. npm bulk trusted publishing config and script security now generally available - GitHub Changelog, 18 February 2026.
  1. npm approve-scripts - npm CLI documentation.
  1. npm deny-scripts - npm CLI documentation.
  1. allow-scripts config - npm CLI configuration documentation.
  1. Trusted publishers - npm documentation on OIDC-based publishing.
  1. Staged publishing - npm documentation.
  1. Shai-Hulud Worm Compromises npm Ecosystem in Supply Chain Attack - Palo Alto Networks Unit 42, cited by CISA.
  1. Development Operations (DevOps) Engineer Salary in India in 2026_Engineer/Salary) - PayScale, 978 salary profiles, last updated 5 May 2026.
  1. npm v12 community discussion - GitHub Community.
  1. npm token deprecation community discussion - GitHub Community.

Last updated: 16 July 2026.

Frequently asked

Quick answers.

01 Is npm 12 already released?
Yes. GitHub made npm v12 generally available on 8 July 2026 and tagged it latest, turning on the install-time security defaults announced on 9 June 2026. Because it is tagged latest, any CI job or developer machine installing npm without a pinned version now gets v12 and its new blocking defaults.
02 What exactly stops running after upgrading to npm 12?
Dependency lifecycle scripts, meaning preinstall, install and postinstall, no longer run unless explicitly allowed. Implicit node-gyp builds are blocked, so a package with a binding.gyp and no explicit install script still stops. Prepare scripts from git, file and link dependencies are blocked the same way.
03 How do I see what will break before I upgrade?
Upgrade to npm 11.16.0 or later, where all three changes appear behind warnings, then run your normal install and review them. Run npm approve-scripts with the allow-scripts-pending flag to list which packages have scripts. This tells you the size of the job without breaking your build.
04 Where does the approval list get stored?
GitHub states the resulting allowlist is written to package.json and should be committed. That makes it a reviewable artifact in version control, so any pull request adding a package to the allowlist becomes a visible decision rather than a silent one your install performs on every machine.
05 Why did GitHub make this change?
Install scripts were the propagation path for the Shai-Hulud worm. CISA reported in September 2025 that the self-replicating worm compromised over 500 npm packages, scanned for credentials, and targeted GitHub Personal Access Tokens plus AWS, Google Cloud Platform and Microsoft Azure API keys, spreading by republishing as compromised maintainers.
06 Does --ignore-scripts already protect me?
Not completely. GitHub notes the allow-git change closes a code-execution path where a Git dependency's .npmrc could override the Git executable even with ignore-scripts set. That is why git resolution now defaults to none rather than relying on script suppression alone as the security boundary for your installs.
07 What happens to my npm publish tokens?
Two deadlines. GitHub expects 2FA-bypass granular access tokens to lose sensitive account, package and organization management abilities in early August 2026, and to lose direct publishing around January 2027. Plan a move to trusted publishing via OIDC, or staged publishing with a human approval step.
08 Do monorepo workspace links break?
No. GitHub confirms the allow-file and allow-directory flags are not changing their defaults in v12, so local file and directory resolution continues to work. The changed defaults cover dependency lifecycle scripts, git dependencies and remote URL dependencies such as https tarballs, direct or transitive.

About the author

Manu Shukla

Founder & Director

Founder of eCorpIT. Hands-on engineer leading senior-only delivery for AI apps, custom software, and cloud systems for global clients.

Subscribe

One engineering note a week. No fluff, no spam.

Senior-architect playbooks on AI agents, mobile apps, cloud, security, data, and marketing — delivered every Wednesday.

Past the reading

Read enough. Let's build something.

A senior architect responds in 24 working hours with scope, indicative cost, and a timeline. NDA before any technical conversation.