npm v12: Mitigating Supply Chain Attacks with New Install Controls
Software supply chain attacks have become a major concern in recent years, with high-profile incidents highlighting the risks of malicious code being injected into widely used software dependencies.
One common attack vector is through install scripts, which are scripts that run automatically during the installation of a package. Attackers can use these scripts to execute malicious code on the victim’s system without the user’s knowledge or consent.
As a response to this threat, npm v12 introduces a number of new features designed to mitigate these attacks.
npm v12 Cheatsheet for Existing Projects
- Upgrade to npm v11.16.0 or later.
- Run
npm installto identify any scripts that would be blocked. - Use
npm approve-scripts --allow-scripts-pendingto list the packages with pending scripts. - Use
npm approve-scripts --allto allow all currently pending scripts. - Commit the updated
package.jsonfile to your repository. - Review the approved scripts and use
npm deny-scriptsto revoke approval for any scripts that you want to block.
Once you’ve completed these steps, you should be ready to start using npm v12 in your projects without impacting your CI/CD pipelines.
Breaking Changes in npm v12
The release of npm v12 in July brings three major changes to the defaults of how npm install works:
- Install scripts are disabled by default. This means that
preinstall,install, andpostinstallscripts from dependencies will no longer run unless explicitly allowed. - —allow-git defaults to
none, which preventsnpm installdependencies resolving from git repositories (whether they’re direct or transitive). This feature was available since npm 11.10. - —allow-remote defaults to
none, which preventsnpm installdependencies resolving from remote URLs. This feature has been available since npm 11.15.
Install Scripts Disabled by Default
From npm v12 onwards, npm install will no longer automatically execute the preinstall, install, and postinstall scripts defined in a package’s package.json file unless they are pre-approved.
This is a significant change because these scripts have been a common vector for supply chain attacks. Malicious actors could inject harmful code into these scripts, which would then execute during the installation process, compromising the system.
The recommendation from GitHub is to upgrade npm to v11.16.0 to have access to the latest commands that will be required to manage these new features.
To find out what would be blocked in your current project, run:
npm install
npm approve-scripts --allow-scripts-pending
The recommended approach by GitHub for migrating existing projects is to allow everything currently in the dependency tree first and then review and manually approve any scripts that are blocked.
npm approve-scripts --all
Once the package.json file has been updated with the approved scripts, commit the package file to your repository. You can then later use the npm deny-scripts command to revoke approval for any scripts that you want to block.
Native Install Scripts
Any native dependencies that are installed via npm install will also be affected by this change, such as the sharp, bcrypt, node-sass packages that rely on node-gyp to compile native code during installation.
Global Install Scripts
These changes to the script execution behaviour also apply to global package installations. However, the approve/deny commands will cause an error for global installs as they only work inside a project with a package.json file.
Git Dependencies Now Require —allow-git
Previously, npm would resolve Git-based dependencies (e.g. a package pointing to a GitHub repo). From v12, Git dependencies are blocked by default unless you pass --allow-git explicitly.
This change mitigates a lesser-known code execution path where a malicious Git dependency could override the Git executable via .npmrc, even when --ignore-scripts was set. This change has been available since npm 11.10.0 and was first announced back in February 2026.
Remote URL Dependencies Now Require —allow-remote
Similar to the Git dependency resolution, any dependencies resolved from remote URLs (such as direct HTTPS tarballs) will be blocked by default.
You’ll need to opt in to enabling URL-based resolution with --allow-remote going forward. This flag has been available since npm 11.15.0.