Mini Shai-Hulud: The Worm That Punishes Cleanup
84 packages poisoned in six minutes. The cleanup is the trap. Here’s the right order to respond.
So far, every project I started with an AI coding tool has followed the same pattern:
Claude generates the package.json. I briefly skim through the framework, the main packages, and run `npm install`. Then I start building.
But more and more attacks keep happening.
And I never once opened the lockfile to check. Not what version got pulled. Not the timestamp. Not whether any of those packages had been poisoned three days before I ran the install.
This is how most people building with AI tools work right now. And it’s exactly what the attack Tox is writing about today is built around.
On May 11, 2026, a worm called Mini Shai-Hulud poisoned 84 npm packages across 42 @tanstack/* libraries in six minutes. By the time most people noticed, the blast had already reached Mistral AI, Guardrails AI, and OpenAI’s internal environment. The malware had one more trick: it was designed so the moment you tried to clean it up, it would wipe your home directory.
The cleanup was the trap.
I follow ToxSec because Tox writes about attacks that actually affect the tools I build with. When this one landed, I asked him to write it here instead.
Tox writes ToxSec - hacker, M.S. Cybersecurity, CISSP. Career spent breaking and defending systems at the NSA, US Government defense contractors, and big tech. Now focused on what he calls the most dangerous frontier in security: AI systems that think, adapt, and lie on command. The newsletter covers what happens when those systems get jailbroken, distilled, poisoned, and weaponized.
If you build with npm, pip, or anything an AI model scaffolded for you, this post is for you.
Over to Tox.
Six Minutes, 84 Packages, Provenance Says It’s Fine
Between 19:20 and 19:26 UTC on May 11, 2026, somebody pushed 84 malicious versions across 42 @tanstack/* npm packages. Six minutes, end to end. @tanstack/react-router alone pulls 12.7 million weekly downloads, so the blast radius started large and got worse fast. By end of day, the same worm had jumped to @mistralai on both npm and PyPI, @uipath, @squawk, guardrails-ai 0.10.1, OpenSearch, and 160+ other packages.
Four days later, OpenAI disclosed that two employee laptops got compromised through the same attack. Internal credentials were exfiltrated. Signing certificates had to be rotated. The blast reached one of the biggest AI companies on Earth through node_modules.
Here’s the part that should keep you up. Every single one of those 84 poisoned packages shipped with a valid provenance certificate. That’s the cryptographic stamp meant to prove a package came from the source code it claims. The whole point of provenance was to make this exact attack impossible. It validated anyway.
If you ship anything that touches npm or pip, including projects Claude Code generated for you with a long list of dependencies, this one matters. Not because the malware is exotic. Because the cleanup is the trap.
The Chain in Plain English
Three vulnerabilities chained together. None of them alone is enough. All three together is the full exploit.
Step one: the fork.
On May 10, an attacker forked TanStack’s router repo and renamed it zblgg/configuration so it wouldn’t show up in fork-list searches. Then they authored a commit under a fabricated identity impersonating Anthropic’s Claude GitHub bot. Nice touch.
Step two: the Pwn Request.
TanStack’s CI had a workflow that ran on something called pull_request_target. It’s a GitHub Actions setting that lets a workflow do trusted things to the main repo, but it runs the code from whoever sent the pull request. Which in this case was the attacker. GitHub Security Lab has been warning about this exact pattern since 2021. The attacker opened a PR, the workflow fired, and their code ran with permissions it never should have had.
Step three: cache poisoning.
The attacker’s code didn’t try to publish anything yet. It just poisoned the GitHub Actions build cache, the place where the runner stores dependencies between jobs, with a tampered copy of the project’s dependencies. Then it waited. Days later, when a real maintainer’s PR triggered the release pipeline, the poisoned cache restored on the runner. The malicious code that had been sitting in the cache woke up and stole the short-lived publishing token that GitHub Actions hands to its runners. That token is alive for only a few minutes. A few minutes is all you need to publish 84 packages.
No npm credentials were stolen. No maintainer accounts were phished. The team had 2FA. Provenance certificates validated. None of it mattered.
Every poisoned package shipped with a prepare lifecycle hook, the kind of script that runs automatically the moment anybody installs the package. It drops a 2.3MB obfuscated payload, deliberately written for Bun, a newer JavaScript runtime, specifically because most security monitoring tools watch Node.js and skip Bun entirely.
The Trap Nobody Tells You About
The payload installs a persistence daemon, a hidden background program that survives restarts. On macOS it lives at ~/Library/LaunchAgents/com.user.gh-token-monitor.plist. On Linux you’ll find it at ~/.config/systemd/user/gh-token-monitor.service. Once installed, it polls the GitHub API every 60 seconds using your stolen token.
The moment the API hands back an error code, which is exactly what happens the second you revoke that token, the daemon runs rm -rf ~/.
Your entire home directory. Gone.
The remediation is the trigger.
A few caveats worth knowing. The first version of the payload had a bug where modern Linux refused to run the delete command without a specific flag, and the malware didn’t pass it. Wiz spotted an updated version staged within 24 hours that fixed the bug. The daemon also exits silently after 24 hours of inactivity, so if you somehow didn’t touch your token for a day, you’re not getting nuked. The malware additionally checks for Russian-language system configuration and quietly terminates on those hosts. None of this matters if you don’t know the daemon exists, see it, and remove it before you start rotating credentials.
The Only Safe Order to Clean Up
If you installed anything from the affected list on May 11, 2026, treat that machine as compromised and walk this list in order. Do not freelance the sequence. Each step has the plain-English version first and the actual command underneath. If the commands look intimidating, the prose tells you what to look for and you can hand the actual cleanup to someone comfortable in a terminal.
1. Don’t touch your GitHub token yet.
Every instinct says rotate first. Every instinct is wrong here. The daemon is watching for that exact error response.
2. Check for the persistence daemon.
What you’re looking for: a file named something like gh-token-monitor sitting in the folder where your system keeps background programs that start automatically. If it’s there, the malware is on your machine.
macOS command: open Terminal and run
ls -la ~/Library/LaunchAgents/ | grep -i tokenLinux command:
ls -la ~/.config/systemd/user/ | grep -i tokenIf either command returns a result, you have it. If neither does, you’re probably clean, but keep going through the rest of the list to be sure.
3. Stop the daemon, then delete the file.
What you’re doing: first you tell the operating system to stop running the malicious background program. Then you delete the file so it doesn’t start back up at the next reboot. Order matters. Stop first, delete second.
macOS commands:
launchctl unload <path-to-the-plist-file> Then delete the file from the LaunchAgents folder.
Linux commands:
systemctl --user stop gh-token-monitor Followed by:
systemctl --user disable gh-token-monitorThen delete the file.
Verify the process is actually gone before you move on. If any of this feels over your head, this is the moment to call in a friend who lives in a terminal.
4. Sweep .claude/ and .vscode/ directories.
What you’re doing: the malware drops backup copies of itself inside hidden folders that AI coding tools use. These backups survive npm uninstall because they live outside the normal package folder. You’re going in and deleting them by hand.
What to look for: files named router_runtime.js or setup.mjs inside the .claude/ and .vscode/ folders in your home directory. Delete them.
5. Audit your lockfiles.
What you’re doing: every npm or pip project keeps a “lockfile” that records every exact version of every dependency it pulled. You’re searching those files for any version that came from the bad batch.
What to search: open package-lock.json, pnpm-lock.yaml, and yarn.lock in any project that ran npm install recently. Search for any @tanstack version dated May 11, 2026. Also search for @uipath, @squawk, @mistralai, intercom-client, lightning, and opensearch-project/opensearch. On the Python side, look for guardrails-ai 0.10.1 and mistralai 2.4.6.
6. Now rotate.
What you’re doing: now that the malware can’t punish you for it, you replace every credential that the infected machine could have touched. GitHub personal access tokens first, then npm tokens, then your cloud accounts (AWS, Google Cloud, Azure), then any other secret that lives on that machine. Treat every credential on that machine as burned.
7. Block the attacker’s infrastructure.
What you’re doing: the malware tries to phone home through three different channels. Blocking those addresses at your router or in your DNS settings cuts off any leftover communication from anything you missed.
What to block: git-tanstack[.]com, *.getsession.org, and the IP 83.142.209.194.
8. Upgrade to clean versions.
Any version of the affected packages published after May 12, 2026, by the real maintainers should be safe. Pin specific versions rather than range matchers for the next month while the dust settles.
Why This Doesn’t End at TanStack
The reason this matters past the news cycle is that this was wave four or five from a threat group calling itself TeamPCP. They popped Trivy in March, Bitwarden CLI in April, the SAP npm packages in late April, and now TanStack. Each wave escalates. This one was the first documented case in history where malicious packages shipped with a valid provenance certificate. The whole point of provenance was that this couldn’t happen.
The source code briefly went public on GitHub before getting taken down. Mirrors are already circulating. The last time a worm’s source went public like this was Mirai in 2016, and the security industry got roughly a decade of derivative botnets out of it. Expect a steady drip of Mini Shai-Hulud variants for the foreseeable future.
The other thing worth sitting with is who got hit. @mistralai. Guardrails AI. OpenAI’s corporate environment. The AI ecosystem is now a top-tier target for supply chain attacks, and the dependency graphs under our favorite tools are deep enough that nobody, including the people building the models, has full visibility into what gets pulled at install time. Every claude code project that runs npm install is downstream of this.
The pattern under all of it: AI coding tools ship dependencies faster than humans can audit them, hardcoded secrets are already the default in AI-generated apps, and now the registries those dependencies come from are getting actively poisoned. I wrote earlier this year about the compound attack surface that vibe-coded apps quietly ship with, and Mini Shai-Hulud just gave the chain a new opening move. We’re going to keep seeing this. IBM’s 2026 X-Force data already flagged a 4x increase in supply chain compromises since 2020, and we’re nowhere near the ceiling.
Ilia:
Claude generates the package.json. We run `npm install`. Nobody checks what versions actually landed, including me. That changes now. Every install gets audited against the affected package list before I start building.
The TL;DR for anyone who runs npm install or pip install and isn’t a full-time security person: pin your versions, audit before you rotate, and assume the cleanup of any future incident will try to punish you for cleaning up. The attackers have figured out that the playbook says “rotate immediately,” and they’re booby-trapping that exact response.
Update your habits before the next wave, because there will be a next wave.
Frequently Asked Questions
Q: How do I know if my Claude Code project pulled a compromised version?
Open Terminal in your project folder and run npm list @tanstack/router @tanstack/react-router @tanstack/start, plus the other affected names listed above. Compare any version you see against the published lists of bad versions on the Socket and StepSecurity blogs. If you find a match dated May 11, 2026, treat that machine as compromised and walk the cleanup steps above in order. Don’t rotate first. Don’t run npm uninstall first. Check for the daemon, kill the daemon, then proceed.
Q: Does this affect me if I only use AI through the browser and never run npm install?
Directly, no. Indirectly, your favorite AI tools sit on top of dependency graphs that include packages from this attack. OpenAI got popped through this. Mistral’s own npm and PyPI packages were compromised. The trust assumptions that let AI products ship updates safely are the exact ones this worm targets. You’re not the direct victim, but you’re downstream of every company that is.
Q: What’s the single biggest lesson here for non-developers?
Speed of remediation is no longer a virtue by itself. Attackers now design their malware around the standard incident response playbook, which means “rotate everything immediately” can be the worst possible move. The lesson for anyone running AI-built apps, automated workflows, or even just connected accounts: when something feels wrong, isolate the machine first, identify the malware second, and rotate credentials third. The cleanup needs the same paranoia as the prevention.










Awesome working with Ilia Karelin here! Really appreciate this opportunity. This article is really interesting because you can see how the threat actors anticipated people reaction, and designed their campaign around that.
Great post guys! The package.json observation hit home. I literally never check lockfiles and I installed quite a few packages on my VPS.