Miasma Worm: Most Infected GitHub Repos Are Still Live
Table of Contents
Eight days after the Miasma worm forged a 4.3 MB credential stealer into public GitHub repositories and three days after the maintainer it locked out got his account back, most of the infected repos are still serving the payload. SafeDep re-scanned the victim list published on June 5 and ran a fresh GitHub code-search sweep on June 11. Across both, 123 repositories spanning 56 accounts still carry the live dropper on 665 branches. Anyone who clones one of them and opens it in VS Code, Cursor, Claude Code, or Gemini runs the credential harvester with no further interaction.
The repos that got cleaned almost all had a security team behind them. Microsoft’s Azure/durabletask and Azure-Samples/llm-fine-tuning are clean. So are all five of icflorescu’s repos, all of jagreehal, and jahirfiquitiva/Frames. What stays live is the long tail of individual developers, students, and small teams who do not have a security org watching their repos. A Nigerian bank’s corporate site, a DeFi exchange’s trading protocol, a university course’s student submissions, and dozens of personal projects are still serving malware right now.
What we measured
The detection is the one icflorescu published after the worm hit him: do not trust the commit author, check for the payload file across every branch. The dropper lives at .github/setup.js, a single line of roughly 4.3 MB. No legitimate repository ships that file.
The scan never checks out a working tree and never executes anything. A blobless --no-checkout clone plus a per-branch object existence test is enough.
# Defensive scan. Nothing is checked out, nothing runs.git clone --no-checkout --filter=blob:none https://github.com/<owner>/<repo>.git probecd probegit fetch origin '+refs/heads/*:refs/remotes/origin/*'for b in $(git for-each-ref --format='%(refname:short)' refs/remotes/origin | sed 's#origin/##'); do git cat-file -e "origin/$b:.github/setup.js" 2>/dev/null && echo "INFECTED: $b"doneTwo datasets fed the run. The first is the 123 repositories SafeDep published on June 5 as carrying the node .github/setup.js hook at disclosure time. The second is a fresh GitHub code search for that same invocation string, run on June 11, which surfaced 58 candidate repositories. 41 of those 58 were not on the original published list. Every result was then git-scanned across all branches to confirm the payload is present and to record which branch each poisoned commit sits on.
Both numbers undercount. GitHub code search only indexes default branches and skips files larger than roughly 384 KB, so the 4.3 MB setup.js itself never indexes. The small launcher configs are the only reason any of this is searchable, and the worm’s habit of hiding on side branches puts most of the payload beyond code search entirely.
Seventy percent of the published list is still infected
Of the 123 repositories published on June 5, the state on June 11 breaks down as follows.
| Outcome (June 11) | Repos | Share |
|---|---|---|
| Still infected, payload live | 86 | 70% |
| Cleaned, repo still up | 21 | 17% |
| Disabled, deleted, or made private | 16 | 13% |
Six days after a public list named these repositories, and after the campaign made international news for hitting 73 Microsoft repositories, seven in ten were still serving the live payload. The 21 that cleaned up are almost entirely the names with a security team behind them: Azure/durabletask, Azure-Samples/llm-fine-tuning, the icflorescu and jagreehal and jahirfiquitiva repos. The 16 that vanished were taken down by GitHub or their owners. Everything else is untouched.
Who is still serving the payload
The full list of repositories confirmed still-infected on June 11, with the number of poisoned branches and the payload size per repo:
| repo | owner | infected_branches | payload_bytes | |
|---|---|---|---|---|
| 1 | Abner97/InvitationsApi | Abner97 | 3 | 4634669 |
| 2 | Abner97/tournaments-app | Abner97 | 2 | 4634669 |
| 3 | Agentic-Insights/dreamgen | Agentic-Insights | 10 | 4646419 |
| 4 | Agentic-Insights/foundry | Agentic-Insights | 2 | 4646419 |
| 5 | Agreon/budgie | Agreon | 12 | 4634464 |
| 6 | Agreon/styco | Agreon | 6 | 4634464 |
| 7 | aiyeola/nextjs-blog | aiyeola | 2 | 4477023 |
| 8 | aiyeola/scrape | aiyeola | 2 | 4477023 |
| 9 | A-Mitch/learningRoR | A-Mitch | 2 | 4471909 |
| 10 | A-Mitch/spotify-codes-simulation | A-Mitch | 2 | 4471909 |
| 11 | anasdevv/customer-portal | anasdevv | 2 | 4361318 |
| 12 | anasdevv/hotel-management-backend | anasdevv | 6 | 4448127 |
| 13 | anasdevv/my-calendar | anasdevv | 2 | 4361318 |
| 14 | anasdevv/reservation-system | anasdevv | 3 | 4361318 |
| 15 | angular-indonesia/angular-indonesia.github.io | angular-indonesia | 2 | 4634519 |
| 16 | angular-indonesia/starter-angular-loopback-bulma | angular-indonesia | 11 | 4634519 |
| 17 | baltruschat/jira-bug-tracker | baltruschat | 4 | 4634733 |
| 18 | beatrizamante/facial-recognition-api | beatrizamante | 2 | 4635011 |
| 19 | beatrizamante/interactive-fiction-reviewer | beatrizamante | 4 | 4635011 |
| 20 | beatrizamante/utfpr_classlog | beatrizamante | 8 | 4635011 |
| 21 | beatrizamante/utfpr_classlog_frontend | beatrizamante | 8 | 4635011 |
| 22 | beatrizamante/watchme_frontend | beatrizamante | 4 | 4635011 |
| 23 | bhagyamudgal/cuju-web | bhagyamudgal | 2 | 4377645 |
| 24 | bhagyamudgal/notisol | bhagyamudgal | 2 | 4377645 |
| 25 | bhagyamudgal/worktree-cli | bhagyamudgal | 3 | 4377645 |
| 26 | bitzquad/bitzquad.com | bitzquad | 1 | |
| 27 | bitzquad/bitzquad.com-2.0 | bitzquad | 1 | |
| 28 | bitzquad/nebula-docs | bitzquad | 1 | |
| 29 | braune-digital/bd-php-to-ts-converter-bundle | braune-digital | 2 | 4634733 |
| 30 | braune-digital/BrauneDigitalImagineBundle | braune-digital | 4 | 4634733 |
| 31 | Code-Web-Basic/CompilerGo | Code-Web-Basic | 5 | 4634925 |
| 32 | CurryMessi/tiktok-video | CurryMessi | 2 | 4635243 |
| 33 | czech-sfl/konference | czech-sfl | 2 | 4634914 |
| 34 | dandycheung/Frames | dandycheung | 1 | 4559058 |
| 35 | dcc-cc3002/citric-liquid-Benjjvv | dcc-cc3002 | 8 | 4619268 |
| 36 | dcc-cc3002/citric-liquid-cpereiram | dcc-cc3002 | 9 | 4619268 |
| 37 | dcc-cc3002/citric-liquid-ihumire | dcc-cc3002 | 9 | 4619268 |
| 38 | dcc-cc3002/citric-liquid-Jarinx | dcc-cc3002 | 9 | 4619268 |
| 39 | dean-s-list/business-visa-server | dean-s-list | 2 | 4377645 |
| 40 | dean-s-list/deanslist-platform | dean-s-list | 2 | 4647225 |
| 41 | dean-s-list/deanslist-services | dean-s-list | 2 | 4377645 |
| 42 | dsrikant/smarcart | dsrikant | 4 | 4620322 |
| 43 | dzhu8/dzhu.github.io | dzhu8 | 2 | 4634066 |
| 44 | EdsonVillarroel/e-commerce | EdsonVillarroel | 6 | 4635467 |
| 45 | EdsonVillarroel/portfolio | EdsonVillarroel | 11 | 4635467 |
| 46 | EdsonVillarroel/vision-kit | EdsonVillarroel | 2 | 4635467 |
| 47 | erbieio/erbie | erbieio | 15 | 4390381 |
| 48 | Factlink/js-library | Factlink | 3 | 4548618 |
| 49 | Factlink/url_normalizer | Factlink | 2 | 4548618 |
| 50 | GNF-Labs/millenium-lms-web-app | GNF-Labs | 4 | 4463853 |
| 51 | jchable/gpx-utility-analyzer | jchable | 2 | 4634567 |
| 52 | jedsada-gh/ApiMovie-UP | jedsada-gh | 3 | 4645945 |
| 53 | jedsada-gh/blockchain-playground | jedsada-gh | 2 | 4634301 |
| 54 | jedsada-gh/co-work-admin | jedsada-gh | 4 | 4634301 |
| 55 | jedsada-gh/co-work-android | jedsada-gh | 3 | 4634301 |
| 56 | jedsada-gh/co-work-api | jedsada-gh | 3 | 4634301 |
| 57 | jedsada-gh/co-work-katalon | jedsada-gh | 2 | 4634301 |
| 58 | jedsada-gh/co-work-provider | jedsada-gh | 3 | 4634301 |
| 59 | jedsada-gh/node-js-netpie | jedsada-gh | 9 | 4634301 |
| 60 | jgutierrezdtt/skills-hello-github-actions | jgutierrezdtt | 2 | 4643795 |
| 61 | jgutierrezdtt/Sports-Center | jgutierrezdtt | 10 | 4643795 |
| 62 | jgutierrezdtt/Vulndemo | jgutierrezdtt | 3 | 4643795 |
| 63 | killerapp/mermaid-render | killerapp | 4 | 4646419 |
| 64 | KSU-Quantum-Capstone/CS4850-DL1 | KSU-Quantum-Capstone | 4 | 4630939 |
| 65 | kylezap/ctrl-alt-win | kylezap | 21 | 4645618 |
| 66 | kylezap/kylezapcicdotcom | kylezap | 2 | 4377461 |
| 67 | kylezap/rightsize-meals | kylezap | 21 | 4645618 |
| 68 | kylezap/tree-view | kylezap | 2 | 4377461 |
| 69 | leanderloew/explainability-simulation | leanderloew | 2 | 4633915 |
| 70 | lucasconnellm/openclaw-fluxer | lucasconnellm | 4 | 4633825 |
| 71 | messismore/Digitale-Ausstellung | messismore | 4 | 4634208 |
| 72 | messismore/Studio-Grotto | messismore | 4 | 4634208 |
| 73 | metersphere/helm-chart | metersphere | 3 | 4646584 |
| 74 | mhar-andal/MyBlok | mhar-andal | 6 | 4508470 |
| 75 | mhar-andal/stock-forum-ethereum | mhar-andal | 2 | 4508470 |
| 76 | mmlngl/contacttracing.app-graphql-api | mmlngl | 3 | 4647729 |
| 77 | mmlngl/flua-launch | mmlngl | 3 | 4647729 |
| 78 | nasher721/3dgenerator | nasher721 | 2 | 4634138 |
| 79 | nasher721/AnkiFellowCollab | nasher721 | 8 | 4634138 |
| 80 | nasher721/Extract721 | nasher721 | 3 | 4646453 |
| 81 | nasher721/Medical-OCR | nasher721 | 13 | 4646453 |
| 82 | nasher721/note-clarity | nasher721 | 4 | 4634138 |
| 83 | nasher721/remix-of-remix-of-round-robin-notes | nasher721 | 20 | 4646453 |
| 84 | nasher721/remix-of-round-robin-notes | nasher721 | 6 | 4634138 |
| 85 | nasher721/scheduler | nasher721 | 21 | 4646453 |
| 86 | nasher721/textcleaner | nasher721 | 3 | 4634138 |
| 87 | neilfarmer/k8s-health | neilfarmer | 9 | 4634793 |
| 88 | neilfarmer/platform-spec | neilfarmer | 8 | 4634793 |
| 89 | Netpoc/company | Netpoc | 2 | 4646713 |
| 90 | nodejs-indonesia/blogs | nodejs-indonesia | 2 | 4634519 |
| 91 | nodejs-indonesia/starter-loopback-fireloop | nodejs-indonesia | 14 | 4645302 |
| 92 | otaviosoaresp/what-the-fork | otaviosoaresp | 2 | 4622883 |
| 93 | paulmojicatech/angular-dc-meetup-may-24-2022 | paulmojicatech | 4 | 4634921 |
| 94 | paulmojicatech/pmt | paulmojicatech | 6 | 4634921 |
| 95 | paulmojicatech/wonder-worm | paulmojicatech | 2 | 4634921 |
| 96 | PositionExchange/decentralized-perpetual-trading-protocol-cross-chain | PositionExchange | 27 | 4645446 |
| 97 | PositionExchange/dptp-client-sdk | PositionExchange | 3 | 4648910 |
| 98 | PositionExchange/evm-matching-engine | PositionExchange | 4 | 4648910 |
| 99 | PositionExchange/position-protocol | PositionExchange | 27 | 5351744 |
| 100 | Pouleyy/nodeAirBnB | Pouleyy | 2 | 4635440 |
| 101 | rhemlock7/ecommerce-back-end | rhemlock7 | 4 | 4646471 |
| 102 | rhemlock7/express-note-taker | rhemlock7 | 7 | 4646471 |
| 103 | rhemlock7/minimalist-portfolio-mkii | rhemlock7 | 9 | 4646471 |
| 104 | rhemlock7/NoSQL-Social-Network | rhemlock7 | 5 | 4646471 |
| 105 | rhemlock7/SQL-Employee-Tracker | rhemlock7 | 6 | 4646471 |
| 106 | rhemlock7/svg-logo-maker | rhemlock7 | 6 | 4646471 |
| 107 | rhemlock7/TeamSync-Client | rhemlock7 | 3 | 4646471 |
| 108 | rhemlock7/TeamSync-KanBan | rhemlock7 | 8 | 4646471 |
| 109 | rhemlock7/weather-app-api | rhemlock7 | 9 | 4646471 |
| 110 | rudy-marquez/WebGoatNet | rudy-marquez | 3 | 4634940 |
| 111 | Saul9201/daily-trends | Saul9201 | 2 | 4645778 |
| 112 | Saul9201/hello-circleci | Saul9201 | 2 | 4376490 |
| 113 | Skipperlla/rn-swiper-list | Skipperlla | 8 | 4611776 |
| 114 | Slickteam/hubspot-java | Slickteam | 4 | 4549341 |
| 115 | snoopyrain/rails102 | snoopyrain | 9 | 4635175 |
| 116 | Summit-Bank-Limited/corporate-website | Summit-Bank-Limited | 12 | 4646713 |
| 117 | tumolaha/lerning-setup | tumolaha | 2 | 4646776 |
| 118 | Weasledorf-Inc/taskmaster | Weasledorf-Inc | 7 | 4634793 |
| 119 | WilChrist/SecurityAuditApp | WilChrist | 2 | 4549461 |
| 120 | WilChrist/WilCovEst | WilChrist | 2 | 4549461 |
| 121 | wormholes-org/wormholes-client | wormholes-org | 5 | 4650163 |
| 122 | Zaynex/13f-vis | Zaynex | 9 | 4549400 |
| 123 | Zaynex/x-atm | Zaynex | 3 | 4549400 |
| No matching rows | ||||
The victims cluster by account, because the worm walks every repository a stolen token can reach. rhemlock7 has 9 repos still infected, nasher721 has 9, jedsada-gh has 8, beatrizamante has 5. One harvested credential poisons an entire personal account.
A sample of who is affected, by category rather than by GitHub stars:
- A bank.
Summit-Bank-Limited/corporate-websitecarries the payload on 12 branches, with commits forged under the addressabubakarzubair@summitbankng.com. - A DeFi exchange.
PositionExchange/position-protocoland three sibling repos are live, the protocol repo poisoned across 27 branches, commits forged asjustin.gun@position.exchange. - A university course. Four
dcc-cc3002/citric-liquid-*repos are student submissions for a software design course at the University of Chile, each poisoned across 8 to 9 branches with commits forged under students’@ug.uchile.claddresses. - A capstone team.
KSU-Quantum-Capstone/CS4850-DL1at Kennesaw State. - An infrastructure project.
metersphere/helm-chart, the Helm chart for the MeterSphere testing platform, with commits forged under its real@fit2cloud.commaintainers.
Payload sizes range from 4.36 MB to 5.35 MB across roughly 38 distinct sizes. The dropper is recompiled per victim, with rotated AES keys and Caesar shift, so the file hash changes while the architecture stays constant. Only the small launcher configs stay the same, and the scan matches on those.
The commits are forged under the maintainer’s own name
The reason these infections survive on the side branches is that they are built not to be noticed. SafeDep’s original analysis described the github-actions <github-actions@github.com> author on icflorescu’s main branches. That was the loud half. Across the 808 payload-introducing commits catalogued in this scan, all but three are forged under the real maintainer’s own name and email, not the bot’s. Every single one carries [skip ci] to suppress notifications, and the commit dates are backdated across a span from 2013 to 2026 so each poisoned commit blends into that branch’s own history.
# PositionExchange/position-protocol, develop branch# https://github.com/PositionExchange/position-protocol/commit/ce456191d71c3399c114b3d539666477df1322bcauthor: Justin <justin.gun@position.exchange>date: 2022-11-14T15:51:31Z # backdated ~3.5 yearsverified: false # unsignedmessage: Update README.md [skip ci] [skip ci] [skip ci] [skip ci] skip-checks:trueangular-indonesia/starter-angular-loopback-bulma carries its payload in commit 57ae9502, forged under Julian GM Alimin <dmastag@yahoo.com> and backdated to 2017. The org’s GitHub Pages site angular-indonesia.github.io is poisoned the same way, under maintainer Irfan Maulana <mazipanneh@gmail.com>. The MeterSphere commits preserve the original Chinese-language messages of the engineers they impersonate. The forgery is tuned to each repo and each branch.
If you audit your own repos, do not detect this by commit author. A backdated commit forged under your own name passes every “is this the bot?” check. Detect by the payload file.
Cleaning main is not enough
The clearest illustration is metersphere/helm-chart. Its default branch v3.x was cleaned on June 8 by a commit titled Remove Miasma config injection indicators. The maintainers knew and fixed it. Yet on June 11 the 4.6 MB payload is still live on three other branches, fix/pvc, pr@main@build_redis, and refactor/kafka.
This is how most of them stay infected. A maintainer sees the commit on main, reverts or resets it, and considers the job done. The worm planted the same payload on every branch it could reach, each under a plausible backdated commit, and those branches never get scanned. Of the 86 still-infected repos from the published list, many have a cleaned-looking default branch and a live payload one branch over.
Root cause: a credential that outlived everything
The entry vector is now confirmed, from GitHub’s own logs, in icflorescu’s follow-up. The commits were made with his GitHub CLI OAuth token, created on January 17 and used five months later from a Microsoft Azure IP range through the GitHub API. The credential was a token, not his password or his 2FA, which is why neither stopped it.
One structural detail explains why a single theft could walk so many accounts. GitHub stacks many access tokens under one OAuth app authorization, one per machine and per re-login. Revoking an individual token, re-authenticating, even GitHub’s own automatic eviction of old tokens, none of it revokes the grant. Only revoking the whole authorization kills every token under it. icflorescu rotated tokens and rebuilt both his laptops from scratch in May, and the January token still worked in June because the authorization underneath was never revoked. The token was most likely harvested earlier through the TanStack supply-chain wave, a compromised dependency pulled during some routine npm install and reused weeks later.
That is the Shai-Hulud loop. A poisoned dependency harvests a write-scoped credential, the worm uses it to push the payload into every repository the credential can reach, and the payload harvests more credentials when a developer opens any of those repos in an AI editor.
The technique refines an older one. The s1ngularity attack on the Nx build system in August 2025 compromised 2,180 GitHub accounts and exposed 7,200 repositories, exfiltrating stolen data to public repos named s1ngularity-repository. That malware used the developer’s own installed Claude, Q, and Gemini CLIs to enumerate and harvest secrets. Where s1ngularity used AI agents to harvest credentials, Miasma plants the config files that make those same agents run the stealer in the first place.
How to check your own repos
The published npm packages for these projects are clean. No malicious version of mantine-datatable, the AntV packages, or the others was ever shipped to a registry in this arm. The danger is entirely local and it survives npm uninstall. If you have cloned any repository that was active during a Shai-Hulud or Miasma wave:
- Do not open the working copy in VS Code, Cursor, Claude Code, or Gemini, and do not run
npm test. A no-checkout clone keeps the payload off your disk. - Scan every branch for
.github/setup.jsusing the command at the top of this post. Detect by the payload file, never by the commit author. - If you find it, reset the affected branches to the parent of the poisoned commit rather than
git revert, which leaves the dropper retrievable at the old SHA. Then ask GitHub to garbage-collect the orphaned objects. - Revoke the OAuth app authorization for any CLI you suspect, not just its tokens. Revoking a token leaves the grant alive.
- Treat
.claude/,.gemini/,.cursor/, and.vscode/files as executable code in every diff and every repo you clone. They auto-run commands on folder-open and session-start, and most review workflows ignore them.
The worm’s bet is that cloning source code feels safe. AI coding agents and IDE auto-run features changed that, and on the evidence of 123 repos still live eight days in, most of the ecosystem has not caught up.
Related reading: Miasma worm targets AI coding agents via GitHub repos (the original source-repo analysis), Mini Shai-Hulud “Miasma” hits @redhat-cloud-services (the staged Bun loader, reversed), and the nx build system compromise (the s1ngularity precedent).
- github
- malware
- supply-chain
- shai-hulud
- ai-coding-agents
Author
SafeDep Team
safedep.io
Share
The Latest from SafeDep blogs
Follow for the latest updates and insights on open source security & engineering

Inside the Miasma Software Supply Chain Attack Toolkit
The Miasma worm source code appeared on GitHub through compromised developer accounts. The codebase is a full supply chain attack toolkit with credential exfiltration across AWS, Azure, GCP, and...

Miasma Worm Targets AI Coding Agents via GitHub Repos
A Miasma worm variant injects a 4.3 MB dropper into GitHub repos across multiple maintainers, wiring it to auto-run through Claude Code, Gemini, Cursor, and VS Code config files. No npm package is...

Config Files That Run Code: Supply Chain Security Blindspot
Editor and package-manager config files auto-execute commands when a developer opens a folder or installs dependencies. The Miasma worm wired one dropper into seven of them across Claude Code,...

Axios Typosquats Deliver the Epsilon Stealer
Two axios typosquats on npm, turbo-axios and faster-axios, form a campaign delivering Epsilon Stealer through a four-stage chain. The Electron infostealer grabs browser credentials, crypto wallets,...

Ship Code.
Not Malware.
Start free with open source tools on your machine. Scale to a unified platform for your organization.
