August 30, 2026
The npm Package I Never Chose to Install Almost Got Me
A few weeks back I was scrolling through my usual morning mix of Twitter and dev Slack channels, coffee in hand, when I saw the same word…
By Muhammad Ur Rehman
4 min read
A few weeks back I was scrolling through my usual morning mix of Twitter and dev Slack channels, coffee in hand, when I saw the same word popping up again and again. Shai Hulud. If that name means nothing to you, do not worry, it did not mean much to me either the first time I heard it, months ago, right after I wrote about a fake recruiter trying to plant malware in my own npm install process.
This time it was back, and it had learned some new tricks.
What Actually Happened
On August 4, 2026, an attacker got hold of the GitHub account belonging to the maintainer of keyv, a small key value storage library that most developers have never heard of and almost none of them installed on purpose. According to Wiz Research, the attacker used that access to publish a new version, keyv 6.0.0, with a malicious preinstall hook quietly attached.
Here is the part that made my stomach drop a little. The same maintainer also owned a whole family of related packages, cacheable, flat cache, file entry cache, cacheable request, and cache manager. Aikido Security reported that keyv alone pulls around 127 million downloads every week, and flat cache and file entry cache each cross half a billion downloads a month between them. Most of these are not things teams add to their package.json directly. They ride in quietly as someone else's dependency, four or five layers down, usually dragged in by a linter or an HTTP client nobody thinks twice about.
Within about half an hour, according to Wiz, the same payload had already shown up across nine unrelated organizations. By the end of the day, security researchers were tracking well over four hundred poisoned package names and more than a thousand poisoned versions between them. Some later reports put the combined download footprint of every affected package north of two billion installs a month.
This was, by most trackers, the sixth wave of the Shai Hulud worm since it first appeared in September last year.
The Twist That Made This One Different
Every worm before this one hid in more or less the same places, a postinstall script here, a suspicious dependency there. What made the August wave genuinely new was where it chose to hide this time.
According to security researchers at Wiz and Kodem, the payload planted persistence hooks directly inside configuration files used by AI coding assistants, specifically the settings file used by Claude Code and a task runner file used by Visual Studio Code. These are not files that traditional dependency scanners look at. They are treated as harmless project configuration, the kind of file you barely notice sitting in your repo.
I sat with that for a minute. I use Claude Code almost every day. So do a lot of the developers I know. None of us think of our AI assistant's own configuration folder as an attack surface. This incident quietly proved that it is one now.
The command and control side of the worm had its own upgrade too. Instead of relying on a fixed domain or server address that defenders could eventually block, the malware queried an Ethereum smart contract to fetch its current control server on demand. Wiz researcher Merav Bar noted that the payload traced back to the same Shai Hulud lineage, sharing tradecraft with earlier campaigns tied to groups like TeamPCP. Using a smart contract for this meant the attacker could rotate their infrastructure at any time without ever touching the malware itself.
And then there was the part that felt almost cruel. Multiple researchers, including the team at Cyber Chief, described a kind of trap built into the malware, a warning against rotating your credentials before first removing the infection properly. Do that in the wrong order and you simply hand your brand new keys straight to the worm.
Checking My Own Doorstep
Naturally, the first thing I did after reading through three or four of these reports was open my own projects and run a very unglamorous command.
npm ls keyv flat-cache file-entry-cache cacheable cacheable-request cache-managernpm ls keyv flat-cache file-entry-cache cacheable cacheable-request cache-managerThankfully, nothing came back tainted. But the exercise itself was humbling. I realized I genuinely did not know, off the top of my head, what half of my own dependency tree was made of. That is not a comfortable thing to admit as someone who has spent years working on Rails and Ruby codebases where dependency hygiene usually feels a little more visible.
I also checked something simple, which npm version my machines were running. This turned out to matter more than I expected. Researchers pointed out that npm version 12 and above disables lifecycle scripts, things like preinstall and postinstall, by default. If your team already sits on npm 12 or newer, the exact hook this entire attack depended on simply never fires. It is a strange kind of relief, being protected by a setting you upgraded to months ago for completely unrelated reasons.
What This Whole Thing Actually Teaches Us
I do not think the lesson here is to panic about AI coding tools or to abandon npm. That would be a little dramatic, and also not very practical for anyone actually shipping software in 2026.
The real lesson, at least the one I took away, is that the attack surface keeps quietly growing in places we are not yet trained to look. A year ago nobody would have thought to audit their AI assistant's own settings folder as part of a security review. Now it belongs on the list.
A few practical habits came out of this for me, and I think they are worth sharing plainly.
Keep your npm version current, since disabling lifecycle scripts by default closes off an entire category of attack on its own. Commit your lockfiles and actually enforce npm ci in your build pipeline instead of npm install, since a lockfile locks you to exactly what you tested, while a fresh install is always free to reach out and grab whatever version just got published, poisoned or not. Pin exact versions for anything that runs during your build rather than trusting a caret range to always behave. And treat any long lived token, whether it belongs to npm, GitHub, or your AI coding assistant, the same way you would treat a spare house key, something that should expire quickly and never sit around longer than it has to.
None of this makes you immune. There will very likely be a seventh wave, and an eighth after that. But being the developer who at least knows what is sitting in their own dependency tree feels like a reasonable place to start.