Transcript
If you find out your machine was compromised by the TanStack NPM attack on May 11th, and your first instinct is to go revoke your GitHub token, that will wipe your entire home directory. Not kidding. It's not hypothetical. The malware installed a background service that pulls GitHub every 60 seconds using your stolen token, and the moment it gets a 400 error back, it returns rmrf on your root directory. Your code, your SSH keys, your project history, gone. This attack is not just a bad package got published story, it is one of the most technically sophisticated NPM supply chain attack documented. And the remediation order matters in a way that almost no one is talking about. By the end of this video, you will know exactly how to check if you were hit, how to safely remove the persistent mechanisms before touching your credentials, and what to change in your workflow so this class of attack cannot reach you again. On May 11, 2026, 84 malicious NPM package versions were published across 42 packages in the TanStack namespace. Packages like TanStack React Router, TanStack View Router, TanStack Solid Router, React Router alone gets over 12.7 million downloads a week. Here's what makes this attack different from every supply chain attack before it. The attacker did not steal TanStack's credentials. They hijacked the TanStack's legitimate release pipeline mid-workflow and published using TanStack's own trusted identity. The packages shipped with valid Salsa or SLSA build level three provenance attestations. That's a mouthful, but it's the cryptographic certificates that are supposed to help ensure the package is safe. In this case, those certificates were legitimate. The pipeline was not. The attack chained three vulnerabilities. First, a Pwn request, a pull request that exploited a misconfiguration target in a GitHub actions workflow to run attacker-controlled code in the base repo security context. Second, GitHub actions cache poisoning. The malicious code poisoned the build cache eight hours before the release workflow ran with a key pre-computed from TanStack's public lock file. And third, OIDC token extraction from runner memory. The attacker-controlled binaries read the runner's process memory to steal the OIDC token before the legitimate publish step ever ran. Within hours, the worm had self-propagated to Mistral AI, UiPath, and dozens of other NPM namespaces. By the end of the day, over 170 packages were affected. This is wave four of a campaign called ShyHalut, attributed to a threat group called Team PCP, which is also linked to the Bitwarden CLI compromise in April and the Aqua security trivia scanner attack in March. Now, before you do anything else, pause for a second, take a breath, and let's figure out whether you are actually affected. Here's how you can check safely without executing any lifecycle scripts. You'll run npm pack at TanStack slash react router at that version, and then use the dry run option. You want to check if there is a router underscore init dot js file present. That version is compromised then. You can also check by the hash using this command on screen. And check your installed packages for the malicious optional dependency the worm uses to spread itself. The confirmed clean TanStack families are TanStack query, table, form, virtual, and store. If you only use those, you're not in scope of this attack. Now, if you find evidence of compromise, keep watching. Do not touch your credentials just yet. This is the most important section of this video. The remediation order is not optional. The malware installs a deadman switch, basically a background service that monitors whether your stolen GitHub token has been revoked. On Linux, it registers as a systemd user service. On macOS, it registers as a launch agent. If it detects the token was revoked, it destroys your home directory. So you want to disable the monitor service first, then rotate credentials second, in that order. You can see how to disable the monitor service for your respective operating system on screen here, or you can check for more details in the blog shared in the description below. After the deadman switch is gone, remove the editor persistent hooks. The worm writes itself into two places, your Cloud Code project settings, and your VS Code workspace tasks. Both are designed to re-execute the payload every time you open up either tool. Also, check for dead drop commits authored under the fake identity the attacker used, which was a claude at no reply dot GitHub dot com type of address. That email is the attacker impersonating the anthropic claude GitHub app. Any commits from that identity in your repo are likely malicious. And with that, you have neutralized the persistence mechanism of this attack. You can safely move on to handling your credentials now. When it comes to your credentials, rotate them in this priority order. Start with the credentials that could be used to push malicious packages or access your most sensitive infrastructure. First, npm publish tokens and oidc federation grants for any package you publish from an affected repo. Second, handle GitHub personal access tokens and fine grained tokens. Third, any AWS credentials, both static keys and instance role trusts via imds v2. Fourth, any HashiCorp vault tokens. Fifth, Kubernetes service account tokens. Sixth, SSH private keys and seventh GCP service account credentials. One more thing, the worm specifically targeted Claude code session history files at Claude projects file. It's a dot JSON L file. If you use Claude code and ran npm install on an affected version, treat anything that appeared in your Claude code sessions as potentially harvested. That includes command you ran API keys you typed code you wrote. From there, reestablish your oidc federation grants only after confirming your publishing workflow is clean. Now that you've seen exactly how this attack worked, here are three changes that will make you meaningfully harder to hit in the future. The first is release age cooldowns. The malicious versions were live for approximately three hours. A seven day cooldown on your package manager would have fully blocked this attack. So add this to your dot npm rc configuration file. For pnpm, you can use minimum release age equals 10,080. That's in minutes. For bun, you could use minimum release age equal 604,800. That's in seconds. Also consider the allow get equals none setting for npm version 11 plus to block get URL dependencies. That is the exact vector the tan stack setup optional dependency used to spread. The second is oidc configuration pinning. If you publish npm packages from GitHub actions, your trusted publisher config probably looks something like this repository and then just the owner and repo. That is vulnerable, you need to pin it to a specific branch and workflow. Here's an example of that. Also scope your ID token configuration right to only the specific job that publishes not the entire workflow. This will limit the blast radius of any further runner compromise. The third is pull request target hygiene. Any workflow that uses this checks out for code and writes to GitHub actions cache is a candidate for this exact attack. Either switch to just the pull request key, which is for context and read only or separate for code execution from base repo cache rights entirely. Purge your existing caches of any repo where this configuration was present before. And finally do not rely on salsa SLSA provenance alone. This attack demonstrated for the first time that a worm can produce valid salsa build level three attestations for malicious packages. Provenance tells you a package was built by a specific workflow. It does not tell you if that workflow was already compromised though. Behavioral analysis at install time is the complimentary control here. Before we wrap up, three things to take away. One, if you installed any at 10 stack slash router family package on May 11, treat that environment is compromised. Check for that router underscore init.js file in the tarball before assuming you're clean. Two, remediation order matters. Kill the dead man switch and remove editor persistent hooks before you rotate any credentials. revoking first risk triggering home directory destruction. And three, two settings protect you against this entire class of attack going forward. Set minimum release age to seven in your NPM RC configuration file and pin your OIDC trusted publisher config to a specific branch and workflow file, not just a repository. The full indicators of compromise list all the detection scripts and the sneak security database entry for this campaign are linked in the description below. That does it for this video. If you got value out of it, be sure to like it down below and share with somebody who could put it to use. And if you made it this far, subscribe to the channel so you don't miss out on upcoming videos. Thanks for watching and happy safe coding everyone.