If you were hit by this worm: a cleanup guide
I went through this on my own repositories and want to leave a complete, practical guide for anyone who lands here after being hit. It applies to the Miasma / Shai-Hulud-family worm that plants a .github/setup.js payload. Read the whole thing once before you start.
The full account of how this happened to me, and the forensics behind it, can be found here and here. This is just the practical fix.
These steps rewrite Git history and force-push. Understand each command before you run it, and keep the evidence backup from Step 3. I am sharing what worked for me, as is; adapt it to your situation.
Rule zero: never let an affected repo run
The payload executes when you open the repo in an AI-assisted editor (VS Code, Cursor, Claude, Gemini) or run npm install / npm test. So while cleaning up:
- Do not open the repo folder in any editor.
- Do not run
npm install,npm test, or any script from it. - Work from a clone that has no files checked out, so nothing can run.
Step 1: Clone with no checkout
git clone --no-checkout https://github.com/<you>/<repo>.git fix cd fix git fetch origin '+refs/heads/*:refs/remotes/origin/*'
Nothing lands on your disk as runnable files. This is safe.
Step 2: Detect by the payload file, not by author
The commits are disguised. On default branches they look like the github-actions bot. On side branches they are often forged under your own name and backdated to look like old, routine work. Do not trust the author. Check every branch for the payload file instead:
git for-each-ref --format='%(refname:short)' refs/remotes/origin | while read b; do git cat-file -e "$b:.github/setup.js" 2>/dev/null && echo "INFECTED: $b" done
Every branch printed as INFECTED needs cleaning. Check all of them, not just main.
Step 3: Back up the evidence once
git clone --mirror https://github.com/<you>/<repo>.git evidence.git tar czf evidence.tar.gz evidence.git
Keep this somewhere safe in case you need it later.
Step 4: Remove the malicious commit from each infected branch
For these attacks the malicious commit is usually the tip of the branch. Confirm it, then reset the branch back to that commit's parent:
# Confirm the latest commit on the branch is the one that added the payload: git log -1 origin/<branch> -- .github/setup.js # Reset the branch to the parent of the malicious commit (drops it from history): git push --force-with-lease origin <MALICIOUS_SHA>^:refs/heads/<branch>
I chose reset over git revert on purpose: a revert leaves the 4.3 MB payload retrievable at the old commit, and for live malware you want it gone, not archived.
If a branch has legitimate commits on top of the malicious one, do not use the command above as-is, it would drop your real work too. You'll have to rebase just the bad commit out.
Step 5: Purge the copies you cannot see
Because of GitHub's fork network, a commit can stay reachable by its SHA even after it is off every branch. Removing it from your branches is not the definitive end. Open a support ticket, give them the full list of malicious commit SHAs, and ask them to garbage-collect and purge them through their sensitive-data removal process. That is the real kill.
Step 6 (the one people miss): rotate credentials properly
Cleaning the repos is pointless if the credential that let them in is still live. The attack is typically a harvested token, not your password, so 2FA does not stop it.
- The big one: revoke the OAuth app authorization, not just individual tokens. GitHub stacks many tokens under one authorization (for example the GitHub CLI). Revoking single tokens, re-logging-in, or reinstalling your machine does not close it. Go to Settings → Applications → Authorized OAuth Apps, and revoke any app you do not fully trust (the GitHub CLI included, then re-authorize fresh).
- If the GitHub UI errors out while you are revoking an app or token (the unicorn page), do not assume it worked. I cannot stress that enough. Come back when the platform is stable and confirm the app or token is actually gone. A revoke that failed behind an error page looks identical to one that succeeded, and the days when GitHub is straining are exactly the days you are most likely to be revoking something urgently.
- Rotate your npm tokens, and any cloud credentials (AWS, GCP, Azure) that ever sat on an affected machine.
A note that may reassure you
In my case the published npm packages were never touched. The attack was on the GitHub source repositories only. Check your own npm versions, but if you publish, your users may well have been safe the whole time.
If any of this is unclear, or your situation does not match these steps, comment here and I will try to help.