GitHub engineer Jamie Magee proposes making install scripts opt-in by default — the biggest npm security change in years. How it affects every JavaScript developer, what to do today, and a migration guide for package authors.
On May 15, 2026, GitHub engineer Jamie Magee published an RFC proposing a fundamental change to
how npm handles install scripts (preinstall, install, postinstall,
prepare). The core idea is simple: instead of running dependency install scripts automatically,
npm should require explicit opt-in from the developer.
The RFC was featured in JavaScript Weekly Issue 786 (May 19, 2026) and Node Weekly Issue 625 (May 21, 2026), where it was described as a long-overdue security improvement for the npm ecosystem. The proposal directly addresses a glaring asymmetry in the JavaScript package manager landscape: npm is the only major package manager that runs dependency install scripts by default.
Under the proposed change, when a developer runs npm install <package> and that package
has install scripts, npm would either:
allowedScripts configuration, or--allow-scripts flag to run themThe exact UX mechanism is still under discussion, but the direction is clear: zero trust by default for install scripts.
Install scripts are the primary attack vector in npm supply chain attacks. When you run npm install,
any package in your dependency tree can execute arbitrary code through lifecycle scripts — before the
package source is even fully installed. This is not a theoretical concern.
The Mini Shai-Hulud worm (April–May 2026)
compromised over 170 npm packages across TanStack, Mistral AI, UiPath, and other major ecosystems by injecting
credential-stealing code through preinstall hooks. The attack worked because npm ran these scripts
automatically — no user confirmation, no warning, no audit trail.
The malware used a preinstall hook to download and execute a Bun-based payload that stole GitHub
tokens, npm credentials, cloud keys, and CI/CD secrets. It then used those credentials to publish compromised
versions of other packages, creating a worm-like propagation chain. The entire attack vector depended on npm's
default behavior of running scripts without question.
Install script attacks are not rare. Security researchers have documented hundreds of cases where malicious
packages used postinstall or preinstall hooks to:
.bashrc or shell profilesover 60% of malicious npm packages used install scripts as their execution vector. Making scripts opt-in would have prevented the majority of all npm supply chain attacks in the past three years.
npm is the outlier. Every other major package manager treats install scripts with suspicion. Here's how they compare:
| Package Manager | Runs Install Scripts by Default? | Opt-In Mechanism |
|---|---|---|
| npm (current) | Yes — always | --ignore-scripts or .npmrc ignore-scripts=true |
| npm (proposed RFC) | No — opt-in required | allowedScripts config or --allow-scripts flag |
| pnpm | No — blocked by default | pnpm allow-scripts or allowedScripts in package.json |
| Yarn Classic | Yes (legacy behavior) | yarn install --ignore-scripts |
| Yarn Berry (v2+) | Requires configuration | dependenciesMeta with built: false |
| Deno | No — scripts never run | N/A — Deno downloads flat URLs, no lifecycle hooks |
| Bun | Runs by default (npm-compatible) | --ignore-scripts flag supported |
The takeaway is clear: pnpm already has the exact mechanism npm is proposing, and it has not broken the ecosystem. Deno and Berry prove that a different model is viable. npm's proposed change brings it in line with the rest of the ecosystem — finally.
Even before the RFC lands (RFCs at npm historically take months to years to implement), there are concrete steps you should take today.
ignore-scripts=true in Your .npmrcThis is the single most impactful security measure you can take:
# ~/.npmrc or .npmrc in project root
ignore-scripts=true
With this setting, npm will download packages but not execute any install scripts. You can then selectively allow scripts for trusted packages:
# Run install normally for a specific package
npm install some-package --ignore-scripts=false
Or, use a more granular approach with npm query to inspect which packages have scripts before
installing:
# Use npq (npm install qualifier) to audit before installing
npx npq install some-package
You may already have packages that rely on install scripts. Find them with:
# List all packages with lifecycle scripts
npx npm-query-lifecycle-scripts
# Or manually check package.json files
find node_modules -name "package.json" -exec grep -l '"postinstall"\|"preinstall"\|"prepare"' {} \;
Common legitimate uses of install scripts include:
sharp, node-gyp, esbuild compile or download platform-specific binariesprisma generate, graphql-codegenIn CI/CD, always use strict mode:
# CI/CD best practice — never run install scripts
npm ci --ignore-scripts
# Then build with explicit steps
npm run build
This ensures that no install script can execute in your build pipeline. If a package requires a script to function (e.g., a binary download), you should pre-download the binary or switch to a package that ships pre-built binaries.
If you maintain an npm package that uses install scripts, the RFC affects you directly. When npm makes scripts opt-in, developers may not approve your scripts — and your package will break silently.
Check your package.json for any lifecycle scripts:
{
"scripts": {
"postinstall": "node postinstall.mjs",
"preinstall": "node setup.mjs",
"prepare": "husky"
}
}
Ask yourself: does this script actually need to run on install? In most cases, the answer is no.
Here's a practical migration path for common install script use cases:
| Current Pattern | Better Alternative |
|---|---|
postinstall for build steps |
Move to build or prepare (runs on publish, not install) |
postinstall for native binary download |
Use optionalDependencies per platform with pre-built packages (e.g., @img/sharp-libvips-linux-x64) |
postinstall for code generation |
Require explicit npm run generate or use prepublishOnly for package authors |
postinstall for setup messages |
Replace with a README.md section or a setup script documented in the README |
prepare for git hooks (husky) |
Husky v9+ already moved to husky init — explicit, not automatic |
preinstall for compatibility checks |
Use engines field in package.json with engine-strict config |
allowedScripts or Documentation Path
If your package legitimately needs an install script (and after audit, you're certain), document it clearly
and provide an allowedScripts configuration snippet that developers can copy. For example:
# .npmrc — add this to your project root
allowedScripts[my-package]=true
This makes the trust decision transparent and reproducible — the same way you'd document a build step.
The install scripts RFC is not happening in isolation. 2026 has been a transformative year for npm security. The ecosystem is undergoing the most significant hardening in its history.
Announced in the same week as the RFC, npm's new staged publishing model provides a review period before packages go live. This prevents attackers from publishing malicious versions that would be immediately available to millions of developers — a key mitigation for the "publish and exploit" pattern used in supply chain attacks.
A prerelease of npm 12.0 has been published, signaling major architectural changes. While the exact changelog is emerging, the version bump from 11.x indicates significant breaking changes — likely including the install scripts RFC or its early implementation.
npm's script security feature, which provides warnings about packages with lifecycle scripts during installation, graduated to General Availability in February 2026. This was the first step in the current hardening trajectory. The RFC is the logical next step: from warning about scripts to blocking them by default.
npm now enforces two-factor authentication for maintainers of high-download packages, closing the account takeover vector that has been used in several supply chain attacks. This complements the install scripts RFC by addressing a different part of the attack surface: who can publish, rather than what runs on install.
Here's a step-by-step workflow to audit your project today, regardless of the RFC's status:
.npmrc — Run npm config ls to see if
ignore-scripts is already set. If not, add it.
npx npm-query-lifecycle-scripts
to see every package in your tree that defines lifecycle scripts.
ignore-scripts=true temporarily and run
your full test suite. Any failures reveal packages that genuinely need scripts. Document these exceptions.
socket.dev or snyk.
If you do nothing else today, add this to your ~/.npmrc:
ignore-scripts=true. Then, when a package you trust needs scripts, grant permission
explicitly with npm install --ignore-scripts=false. This single change blocks the
attack vector used by Mini Shai-Hulud and most other npm supply chain attacks.
For secure web application development, get in touch
The npm install scripts opt-in RFC represents a fundamental shift in how the JavaScript ecosystem approaches security. It acknowledges that the default behavior that made npm successful — install scripts running automatically — has become its greatest weakness.
As developers, we've been trusting install scripts by default because there was no alternative. The RFC changes that. It moves the ecosystem from "trust by default, audit by exception" to "audit by default, trust by exception". That's the right direction.
If you're building a web application and want a developer who takes dependency security seriously — not as
an afterthought but as a built-in practice — reach out to me. I'm a
full-stack developer with 20+ years of experience, and I build every project with
--ignore-scripts, pinned dependencies, lockfile integrity checks, and automated security
scanning from day one.
Have a project in mind? I'll help you choose a secure, modern tech stack and build it right. Free initial consultation.