How to check the npm worm hasn't got you
A worm called Shai-Hulud went through npm again on Tuesday morning. It got into a package called keyv at about 10am UK time on the 4th of August, and from there into flat-cache and file-entry-cache, which sit inside ESLint and get downloaded over half a billion times a week. Within an hour it had copied itself into more than 400 other packages. The poisoned versions have been pulled from npm now. They were pulled the last few times too, and it keeps coming back.
Full-time developers have seen a dozen write-ups already. But a lot of working software now gets built by people who wouldn't call themselves developers. You had an idea, you built it with Cursor or Claude or whatever you like, it works, real people use it. Underneath it there's a node_modules folder with forty thousand files in it that you have never opened once. Fine. Nobody opens it. This post is about the ten minutes of checking worth doing anyway, and the small amount of config that means you can go back to ignoring the whole subject.
What it does
The important bit is when it runs. Installing a package doesn't only download code. A package is allowed to run a script at the moment of install, with your permissions, and that's where Shai-Hulud lives. You type npm install, and before anything of yours has run, it's already going through your machine looking for credentials. npm tokens, GitHub tokens, cloud keys, SSH keys, crypto wallets, .env files.
Whatever it finds gets uploaded to a brand new public repository on GitHub. Created with your own stolen login, in your name, visible to anyone.
And if any stolen credential is able to publish npm packages, it uses that to push infected copies of those packages. Whoever installs them next goes through the same thing. That's how one hijacked maintainer account became 400+ poisoned packages before most of the UK had finished its first coffee.
Why last month's advice won't help you
Most write-ups include a list of things to search for. A filename, a domain the malware talks to, a repository name. The catch is that this worm has come back repeatedly since September 2025 and it changes all of those details on every visit. The first version created repos literally called Shai-Hulud. A later one named them after Dune characters. The file it drops has had a different name almost every time, and the domains in Tuesday's wave had never been seen before it started.
Search your laptop for last month's filename and you'll get no results, feel reassured, and have learned nothing. The checks that hold up are behavioural. Did something appear that you didn't create? Did your accounts do something you didn't do?
The ten minute check
1. Look at your GitHub account
Start here because the worm's whole purpose is to publish your secrets under your own name. Open your repositories and sort by newest. A repo you don't recognise, particularly a public one with a nonsense name or description, means you stop reading this and start rotating credentials. Have a look at the security log in your account settings while you're in there, and if you publish npm packages, check nothing has been released under your name this week.
2. Ask git what changed
git status
git diffRead the output properly rather than skimming it. Recent versions of the worm hide in config files that run things automatically, on the reasonable theory that nobody ever reads them: .github/workflows/, .vscode/tasks.json, .claude/ folders. A changed file you didn't change is the tell. Don't rationalise it away.
3. Check which versions you've actually got
npm ls keyv cacheable-request cache-manager flat-cache file-entry-cacheThat prints the versions installed in your project, including ones buried several dependencies deep. Compare them with the affected lists that Wiz and others are maintaining, linked at the bottom. The poisoned release of keyv was 6.0.0, published on the morning of the attack. If yours says 4.x and has done for months, you're looking at the safe one. Old and pinned is what good looks like here, which surprises people.
4. Look for anything weirdly large
find node_modules -name "*.js" -size +400kThe credential-stealing machinery is bulky compared with normal library code, so a sweep for oversized files catches variants regardless of what they're called this week. Some legitimate libraries are genuinely that big, so a result isn't proof of anything. It's just where to point your attention.
If it got you
Work on the assumption that every credential that machine has ever held is now public. Cloud keys first, because those turn into somebody else's Bitcoin mining bill within hours. Then GitHub and npm tokens, then SSH keys, then everything in your .env files. Revoke the old ones properly. A password you'll "change later this week" is a password the attacker still has.
Turn on multi-factor authentication for GitHub and npm if you haven't. Delete node_modules completely and reinstall once you know you're pulling safe versions.
One more that catches people out: your build pipeline installs packages too. If a deploy ran while the bad versions were live, whatever secrets that pipeline holds need rotating as well, even if your laptop is clean.
Four lines of config so you can stop worrying about this
The defence is embarrassingly small. First, commit your package-lock.json, and anywhere a machine installs your dependencies, use npm ci rather than npm install. The lockfile records the exact version of everything in your project. npm ci installs precisely those and refuses anything else, while npm install will happily fetch a release that came out forty minutes ago. On a normal day that difference is invisible. On a morning like Tuesday's it's everything.
Second, create a file called .npmrc in your project folder:
ignore-scripts=true
min-release-age=7The first line stops packages running scripts when they're installed, which is the exact door this attack walks through. The second refuses to install any release less than seven days old. It needs a reasonably current npm (11.10 or newer; check with npm --version), and what it does in practice is quietly pick the newest version old enough to trust. Every wave of Shai-Hulud so far has been caught and pulled from npm within a day of appearing, so with that one line you'd never have seen any of them. Not Tuesday's, not September's, not whatever they call the next one.
A small number of packages do need their install scripts, so run your build after turning ignore-scripts on and see if anything complains. In our experience most projects don't notice at all.
That's the lot. It's dull, it takes twenty minutes, and it's the same advice CISA gives to government suppliers. Dull is what winning looks like in security.
Sources
The people doing the actual research. Several keep their affected-package lists updated as each new wave lands, so check your versions against these rather than a list in a week-old article.
• Wiz on this week's wave, with the full package and version list.
• Palo Alto Unit 42, a running history of the whole campaign since September 2025.
• Microsoft Security with detection guidance aimed at teams rather than individuals.
• CISA's advisory, where the lockfile and
.npmrcrecommendations above come from.• StepSecurity on the CI/CD side, and on how the indicators change between waves.
• Akamai on the self-spreading mechanics, if you want the gory detail.