npm install Is the Riskiest Command You Run Every Day
Shai-Hulud is back and a Trusted Publishing flaw exposed 1,500 npm packages. Five defences every frontend developer should set up this week.
CarnielOn this page10 sections
On October 8, version 0.5.144 of the tensorlake npm package shipped with malware inside. Anyone who installed it ran code that went looking for npm tokens, GitHub tokens, AWS and Kubernetes credentials, SSH keys, crypto wallets and even the config files of AI tools like Claude. Then it used what it found to publish poisoned versions of other packages (SC Media).
Five days later, researchers at Bloom Security revealed that a flaw in npm's Trusted Publishing had left more than 1,500 packages, with over 10 million total downloads, open to takeover by anyone who registered the name of a renamed or deleted GitHub repository (Help Net Security). GitHub has shipped a fix. Nobody knows whether anyone used the hole before it closed.
This is the same worm family, Shai-Hulud, that first hit 187 packages in September 2025 (Krebs on Security). It keeps coming back because the trick still works: get one install on a developer's machine, steal their tokens, publish more poisoned packages.
You don't have to maintain a popular package to be part of that chain. You just have to run npm install. Here's how to make that command a lot less dangerous.
Why frontend developers are the target
A fresh Next.js app pulls in hundreds of packages before you write a line of code. Any one of them, or any one of their dependencies, can run code on your machine the moment it's installed. And your machine is full of things worth stealing: a GitHub token with write access to your company's repos, cloud keys in .env files, an npm token in ~/.npmrc.
AI coding agents make the numbers worse. Bloom estimates that agents on a typical developer machine install close to 350 packages a month, not counting dependencies, usually without anyone looking at what they are. And models sometimes recommend packages that don't exist at all. One study of 576,000 code samples found that 19.7% of the packages LLMs suggested were hallucinated (arXiv). Attackers register those made-up names and wait. It has a name now: slopsquatting.
Defence 1: stop install scripts from running
Most npm malware runs through a postinstall script, which executes automatically during npm install. The good news: both major package managers now block those scripts by default.
npm 12 (released July 2026) skips install scripts for any dependency you haven't approved. I installed esbuild in a fresh project to see it happen:
$ npm install esbuild
npm warn install-scripts 1 package had install scripts blocked because they are not covered by allowScripts:
npm warn install-scripts esbuild@0.25.0 (postinstall: node install.js)
npm warn install-scripts Run `npm install-scripts ls` to review, or `npm install-scripts approve <pkg>` to allow.When you've checked a package and you trust it, approve it:
npm install-scripts approve esbuildThat writes a pinned entry to your package.json:
{
"allowScripts": {
"esbuild@0.25.0": true
}
}The pin matters. If a future version of esbuild is ever hijacked, its scripts won't run until someone approves that exact version again. Commit the file so your whole team and your CI get the same policy.
pnpm goes further and stops the install with an error until you decide:
$ pnpm add esbuild
Error: ERR_PNPM_IGNORED_BUILDS
Ignored build scripts: esbuild@0.25.0
help: Run "pnpm approve-builds" to pick which dependencies should be allowed to run scripts.If you're still on npm 10 or 11, this is the best reason to upgrade this week. One thing to know before you do: npm 12 needs Node.js 22.22.2 or later on the 22 line, 24.15 or later on the 24 line, or Node 26.
Defence 2: don't install anything that's a day old
Poisoned versions tend to be spotted and pulled quickly, often within hours or a day or two. A release-age cooldown means your installs simply never pick a version that new.
pnpm 11 and later already do this by default, with a one-day window. You can widen it in pnpm-workspace.yaml. The value is in minutes:
# pnpm-workspace.yaml
minimumReleaseAge: 4320 # 3 daysnpm has the same idea, but it's off by default and the value is in days. Add it to your project's .npmrc:
# .npmrc
min-release-age=3Watch the units if your team uses both tools. 3 in npm is three days. 3 in pnpm is three minutes.
The cost is that you wait a few days for new releases. When you need a security fix right away, both tools let you exempt a single package: min-release-age-exclude in npm, minimumReleaseAgeExclude in pnpm.
Defence 3: let the lockfile do its job
Your lockfile records the exact version of every package. Treat changes to it like code changes:
Use
npm ciorpnpm install --frozen-lockfilein CI, so a build never quietly resolves to a newer version than the one you reviewed.Read lockfile diffs in pull requests. A small feature that adds 40 new packages deserves a question.
Be careful with broad
npm updateruns right after big news like this week's. That's exactly when a poisoned version is most likely to still be live.
Defence 4: have less worth stealing
Assume one install will get through eventually. What it finds on your machine decides how bad the day gets.
No long-lived npm tokens. If you publish packages, use Trusted Publishing from CI instead of a token in
~/.npmrc. If you must have a token, give it an expiry date.Scope your GitHub access. Fine-grained tokens limited to the repos you actually work on do much less damage than a classic token with access to everything.
Keep production keys off your laptop. Local
.envfiles should hold test keys. Real payment and cloud credentials belong in your hosting provider's secret store.Give AI agents a box. Running agents inside a dev container means a bad package they install sees the container, not your home folder.
Defence 5: put rules in front of your AI agent
If an agent can add dependencies on its own, it needs the same rules as a new teammate. Put them in your AGENTS.md:
## Dependencies
- Never add, upgrade or remove a dependency without asking first.
- When suggesting a package, give its npm page, weekly downloads and
the date of its latest release, so I can check it exists and is maintained.
- Prefer what's already installed or built into the platform.Before you approve one, take ten seconds to look at the npm page. A package with a handful of downloads, created last week, with a name almost the same as a popular one, is not something to install on a hunch. I wrote more about setting up this file in AGENTS.md in Next.js 16.3.
One thing blocking scripts won't stop
Install-script blocking stops code that runs during npm install. It doesn't stop malicious code inside the package itself, which runs when your app imports it, during next build or in your dev server. That's why the defences stack: the cooldown keeps fresh poisoned versions out, the lockfile keeps surprise upgrades out, and keeping secrets off your machine limits the damage if something still gets in.
If you maintain a package
Bloom's advice after the Trusted Publishing flaw is short. Run npm trust list and check that every linked repository still belongs to you. If you've renamed or transferred a repository, delete the trust binding and create it again. And remove the binding before you ever delete a repo.
If you installed a bad version
Remove the package and reinstall from a clean lockfile.
Rotate everything the machine could see: npm and GitHub tokens, cloud keys, SSH keys, and any API keys in
.envfiles.Check your GitHub account for repositories, workflows or deploy keys you didn't create. Shai-Hulud has a habit of creating public repos to dump what it steals.
Tell your team. If the worm used your token, packages you can publish may already be affected.
The five-minute version
Upgrade to npm 12 or pnpm 11+ so install scripts are blocked by default.
Approve scripts only for packages you've checked, and commit the list.
Add a release-age cooldown:
min-release-age=3for npm,minimumReleaseAge: 4320for pnpm.Use
npm ciin CI and read your lockfile diffs.Keep long-lived tokens and production keys off your laptop.
Don't let AI agents add packages without asking.
None of these slow you down much. And the next time a worm makes the news, you'll read about it instead of rotating every key you own.