npm v12 is due in July 2026 with three security changes that can break existing CI builds. Dependency install scripts will require approval, while Git dependencies and remote URL dependencies will need explicit permission. A runner upgrade or base image update could bring those defaults into a pipeline before its dependencies are ready.
As of July 1, the preparation path is to use npm 11.16.0 or a later 11.x release to see advisory warnings before v12 arrives. Some affected installs will fail outright. Others may appear to succeed but leave out a native binary or another file that an install script normally produces.
The three defaults changing in v12
GitHub announced the breaking changes on June 9, 2026, with all three available as opt-in warnings in npm 11.16.0. They affect both what npm can fetch and what a dependency can execute during installation.
Dependency install scripts will be disabled by default. Running npm install will no longer execute a dependency's preinstall, install, or postinstall scripts without explicit approval. This includes legitimate build work. A package that uses node-gyp to compile C++ extensions may install without producing its native binary until it is added to an allowlist.
Git dependencies will be blocked by default. A direct repository reference in package.json, such as "some-lib": "github:org/repo", will require an explicit --allow-git flag or configuration entry. This control has been available since npm 11.10.0 and was first announced in February 2026. In v12, the restrictive setting becomes the default.
Remote URL dependencies will also be blocked. Packages fetched directly from HTTPS tarball URLs will require --allow-remote. This affects private packages served as raw tarballs from Artifactory or Nexus rather than through their npm registry endpoints.
These defaults replace an assumption that has been common throughout npm's roughly fifteen-year history: declaring a dependency also permits its installation code to run. That has made package setup convenient, but it gives a compromised dependency access to the build environment before the application itself runs.
What an install script can expose
The May 19, 2026 Shai-Hulud wave shows the risk. According to StepSecurity's account of the attack, a single compromised npm maintainer account led to 639 malicious package versions being published across 323 packages in 22 minutes.
Affected packages included @antv/g2, @antv/g6, echarts-for-react, timeago.js, and size-sensor. The compromised packages collectively accounted for approximately 16 million weekly downloads. echarts-for-react alone accounted for roughly 1.1 million.
The reported payload harvested more than 20 credential types, including AWS access keys, GCP service account tokens, Azure credentials, GitHub tokens, SSH private keys, Kubernetes configuration files, HashiCorp Vault tokens, Stripe API keys, and database connection strings. It also attempted to escape Docker containers through the host socket.
Stolen data was encrypted with AES-256-GCM and wrapped with RSA-OAEP. The payload used GitHub API dead-drops, including the legitimate antvis/G2 repository as a covert channel, before falling back to direct HTTPS connections to a command-and-control server.
The entry point was a malicious postinstall script published through the compromised account. A pipeline installing an affected version with scripts enabled would run that code with the build runner's privileges. Credentials exposed through the runner's environment variables were therefore within reach.
Reviewing every dependency is difficult at normal release speeds. DevOps.com reports that the average npm project has 79 transitive dependencies, meaning packages brought in by other packages. Reviewing 15 or 20 direct dependencies is manageable for many teams. Repeating that review across the full dependency tree, including every new patch release, is much harder.
An approval requirement doesn't eliminate supply chain attacks. It does remove automatic install-time execution as the starting point. A package must receive permission before its setup code can run.
Where CI builds can fail
The most exposed workloads are those that depend on installation doing more than unpacking package files:
- Native addons. Packages such as
bcrypt,canvas,sharp, andbetter-sqlite3can depend on native compilation or setup. Anything invokingnode-gypneeds particular attention. If a required script is skipped, the package may be present while its compiled extension is missing. - Packages with other setup scripts. Install hooks can generate TypeScript definitions, download platform-specific binaries, or set up local certificates. Examples include Puppeteer's Chrome download, esbuild's platform binary, and Playwright's browsers. Where installation relies on those scripts, approval is needed.
- Git-sourced internal dependencies. Private packages and forks referenced through Git in
package.jsonwill encounter resolution failures without permission. These references often appear in monorepo setups or where a team maintains a patch while waiting for an upstream fix. - Private HTTPS tarballs. Raw tarball downloads from Artifactory or Nexus, including setups using custom authentication, will be blocked without the remote dependency allowance.
A direct resolution error stops the build near the cause. A skipped setup script can be harder to diagnose. Installation may finish, the pipeline may package its artifacts, and only a later test or staging deployment may reveal that a native module is missing.
This is especially likely to catch teams that receive npm upgrades indirectly. A Node.js base image update can change the package manager even when no application dependency has changed. Checking only whether npm install exits successfully won't catch every consequence of disabled scripts.
Review approvals on npm 11.x
The migration path is to move CI to npm 11.16.0 or a later 11.x release before adopting v12. Those releases expose the upcoming security defaults through advisory warnings, so builds can continue while teams identify pending script approvals and dependencies that need Git or remote resolution allowances.
The migration workflow includes upgrading npm and reviewing pending script approvals in each project:
# Upgrade npm in your CI environment to enable advisory mode
npm install -g npm@latest
# In each project, review what has pending script approvals
npm approve-scripts --allow-scripts-pending
# Review each flagged package deliberately, then commit the result.
# The allowlist lives in package.json and is versioned with your code.
The npm@latest command follows the current release tag; it doesn't guarantee an 11.x version. For an advisory-mode rollout, the selected release needs to remain on npm 11.16.0 or later within the 11.x line.
Review scripts individually. A long list of pending approvals creates pressure to enable everything and move on. That would restore much of the old exposure. Each approval should have a clear reason, such as native compilation, a required binary download, or type generation. The allowlist belongs in source control so changes can receive the same review as dependency updates.
The useful artifact is more than a list that makes the build pass. It records which packages have permission to execute code on the runner. Recording the reason for each approval also helps later security reviews distinguish required setup from permissions that are no longer needed.
Move private tarballs to registry endpoints where practical. GitHub Packages, JFrog Artifactory's npm registry interface, and Sonatype Nexus provide alternatives to direct HTTPS tarball references. Adding --allow-remote can keep an existing setup working in the short term, but retaining raw URLs also retains a pattern that can complicate dependency reviews and audits.
Reconsider Git references. For a fork waiting on an upstream fix, options include carrying a local patch file or publishing the fork to an internal registry as a versioned package. A patch: protocol is another option to evaluate where the project's tooling supports it. Explicit Git permission remains available, but the migration is a reasonable time to decide whether a repository reference is still the best way to distribute that dependency.
The security gain and the remaining work
Other package ecosystems have also separated package distribution from some forms of setup code. Cargo, Rust's package manager, has a declared build-script mechanism. Python packaging has largely moved away from setup.py install hooks toward static wheel distributions. These ecosystems have different execution rules, so they aren't interchangeable security models.
The npm changes address a specific source of exposure: code that runs automatically during dependency installation. The May 2026 Shai-Hulud campaign, the SolarWinds breach in 2020, and the xz-utils backdoor in 2024 involved different mechanisms, but each illustrates the operational cost of compromised software dependencies. That cost can include incident response, credential rotation, and emergency pipeline repairs.
An explicit script allowlist is a useful improvement because permission becomes visible and reviewable. It also creates maintenance work. Approvals need attention as packages and versions change, and an approved package is still trusted to run code with the runner's privileges.
CI teams can do that review while advisory warnings are available on npm 11.x, then test the resulting builds before upgrading runners to v12. That is a better use of time than debugging build failures under deadline pressure after an image update has already changed the defaults.