Post
Shai-Hulud Came for Your Coding Agent
A worm hit PyTorch Lightning on PyPI and crawled into the one place nobody was checking: your AI coding tools. It rewrites .claude/settings.json so the malware launches every time you open Claude Code.
So, did you pip install lightning yesterday? You might want to check your tokens.
On April 30, two malicious versions of the PyTorch Lightning Python package β lightning 2.6.2 and 2.6.3 β were published to PyPI carrying a fresh strain of the Shai-Hulud worm. And this one is special. It doesn't just steal your AWS keys and call it a day. It crawls into your .claude/settings.json and your .vscode/tasks.json, hides there, and waits for you to open your editor.
The AI training tool got wormed. The worm now lives in your AI coding tool.
Let that sink in.
What is Shai-Hulud and why does it keep coming back
If you missed the Dune reference: Shai-Hulud is the giant sandworm. The "great maker." The threat actor behind this campaign has been naming everything after it for months, with commit prefixes like EveryBoiWeBuildIsAWormyBoi and repos titled "A Mini Shai-Hulud has Appeared." It's branding. It's almost cute. It is also one of the most aggressive software-supply-chain operations of the last year.
The earlier campaigns hit npm. This one hops ecosystems. PyPI is the entry point. Once it's inside, it looks for npm publish credentials and reproduces β injecting itself into every package the stolen token can publish, bumping versions, and republishing. A worm in the literal computer-science sense.
The host this time was lightning, the package behind PyTorch Lightning, used by basically anyone training a model on PyTorch. So we're not talking about an obscure left-pad clone. We're talking about the lab-bench library that sits next to half the AI experiments in the wild.
What it actually does on your machine
Here is the part that should worry you more than the credential theft (and the credential theft is bad).
| Path it touches | Why it's there |
|---|---|
_runtime/start.py | Python loader inside the package β runs on import lightning |
_runtime/router_runtime.js | The 14.8 MB obfuscated JS payload doing the actual work |
.claude/router_runtime.js | A copy of the payload dropped into your repo |
.claude/settings.json | New SessionStart hook with matcher "*" β runs on every Claude Code launch |
.claude/setup.mjs | The dropper the hook actually executes |
.vscode/tasks.json | Auto-run task with the same intent |
.vscode/setup.mjs | Secondary dropper for the VS Code path |
The last few lines are the new trick. Most supply chain malware grabs what it can during pip install and then prays the developer never notices. This one assumes you will notice β and survives anyway. Even if you uninstall the bad version, the hooks in your .claude/settings.json and .vscode/tasks.json live on. Open the project tomorrow. The worm wakes up. It exfiltrates again. It looks for new tokens.
It's persistence by parasitism. Your editor becomes the host.
What it steals
Pretty much everything an AI/cloud developer has lying around:
- GitHub tokens β anything starting with
ghp_,gho_,ghs_ - npm publish tokens β the engine for the worm's reproduction
- AWS credentials β env vars,
~/.aws/credentials, IMDSv2, ECS task metadata - Azure β through
DefaultAzureCredential - GCP β
GoogleAuthsecrets - GitHub Actions secrets β for the CI side
- Shell environment β anything you've
export'd - Local files β over 80 known credential paths, slurping up to 5 MB each
If you trained a model on a box yesterday, the worm has a pretty thorough map of your cloud accounts.
The bit nobody is saying out loud
Here's the thing that bothers me. We spent two years training developers to install AI tools that deeply integrate with their codebases. Claude Code, Cursor, Copilot CLI, Codex CLI, you name it β they all read project-local config: .claude/, .cursor/, .vscode/, .codex/. That's how they personalize behavior per repo. It's a feature. It's a good feature.
It's also a wide-open mouth.
A SessionStart hook with matcher "*" is not exotic. It's literally the documented way to wire up a custom workflow when Claude Code opens a project. Same idea on the VS Code side with tasks.json and runOptions.runOn. The malware isn't exploiting a bug. It's using these tools exactly as designed β just with someone else's intent.
This is the closest analogue I can think of:
Imagine you discover a stranger has copied your house key. You change the locks. You feel safe. Then you remember: last week, your smart-home installer added a "convenience routine" that auto-disables the alarm whenever your fridge connects to Wi-Fi. The locks don't matter. The routine is still there.
That routine is .claude/settings.json. The fridge is pip install.
What you should do today
If you touched lightning 2.6.2 or 2.6.3 (or you're not sure):
- Pin to a known-good version. The maintainers have unpublished the bad ones, but check anyway:
pip show lightningand read the version field. - Audit your repos for
.claude/router_runtime.js,.claude/setup.mjs,.vscode/setup.mjs, and any_runtime/directory you didn't put there. - Open
.claude/settings.jsonand look for anySessionStarthook runningnodeon a file you don't recognize. Same for.vscode/tasks.jsonβ look forrunOptions.runOn: folderOpen. - Rotate everything. GitHub PATs, npm tokens, AWS keys, GCP service accounts, Azure secrets, anything you
export'd into your shell in the last 48 hours. - Check your published npm packages if you have publish rights. The worm bumps the patch version and republishes β
npm view <pkg> versionswill show whether something appeared that you didn't push.
If the answer to step 1 is "I never touched that package, I don't even use PyTorch": still scan. The cross-ecosystem propagation means a teammate's poisoned npm token can reach your repos through a shared dependency.
My take
I'm not surprised this happened. I'm surprised it took this long.
The AI coding agent ecosystem grew up in maybe 18 months. It went from "GitHub Copilot suggests a line" to "Claude Code modifies your repo according to a JSON config file you barely read" with very little stop-and-think in between. We normalized the idea that opening a folder in your editor is enough to run code from that folder. The tools have great UX. The threat model⦠exists, but mostly in the heads of people writing the tools, not the people using them.
Shai-Hulud is showing us the bill. The good news: the fix is mostly hygiene. Treat your editor's project-level config the way you already treat package.json scripts. Read it before you open the folder. Don't trust hooks you didn't write. Be suspicious of anything that auto-runs.
The bad news: this is going to keep happening. The attack surface is too good. A single stolen npm token now compromises every package downstream and every Claude Code session that ever opens an infected repo. That's a worm with two sets of teeth.
Anyway β go check your .claude/settings.json. I'll wait.
Sources
- Semgrep β Malicious dependency in PyTorch Lightning used for AI training β the primary technical writeup with full IOCs, file list, and the cross-ecosystem propagation mechanics
- Hacker News discussion thread β community reactions and additional context on the Mini Shai-Hulud history
- PyPI β lightning package page β verify the current version and check release history
- Anthropic β Claude Code hooks documentation β what
SessionStarthooks are and how they're meant to be used - VS Code β Tasks documentation β including
runOptions.runOn: folderOpen, the feature this attack abuses