Miasma Worm: Most Infected GitHub Repos Are Still Live

SafeDep Team
9 min read

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.

Terminal window
# Defensive scan. Nothing is checked out, nothing runs.
git clone --no-checkout --filter=blob:none https://github.com/<owner>/<repo>.git probe
cd probe
git 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"
done

Two 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)ReposShare
Still infected, payload live8670%
Cleaned, repo still up2117%
Disabled, deleted, or made private1613%

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:

miasma-still-infected-repos.csv
repoownerinfected_branchespayload_bytes
1Abner97/InvitationsApiAbner9734634669
2Abner97/tournaments-appAbner9724634669
3Agentic-Insights/dreamgenAgentic-Insights104646419
4Agentic-Insights/foundryAgentic-Insights24646419
5Agreon/budgieAgreon124634464
6Agreon/stycoAgreon64634464
7aiyeola/nextjs-blogaiyeola24477023
8aiyeola/scrapeaiyeola24477023
9A-Mitch/learningRoRA-Mitch24471909
10A-Mitch/spotify-codes-simulationA-Mitch24471909
11anasdevv/customer-portalanasdevv24361318
12anasdevv/hotel-management-backendanasdevv64448127
13anasdevv/my-calendaranasdevv24361318
14anasdevv/reservation-systemanasdevv34361318
15angular-indonesia/angular-indonesia.github.ioangular-indonesia24634519
16angular-indonesia/starter-angular-loopback-bulmaangular-indonesia114634519
17baltruschat/jira-bug-trackerbaltruschat44634733
18beatrizamante/facial-recognition-apibeatrizamante24635011
19beatrizamante/interactive-fiction-reviewerbeatrizamante44635011
20beatrizamante/utfpr_classlogbeatrizamante84635011
21beatrizamante/utfpr_classlog_frontendbeatrizamante84635011
22beatrizamante/watchme_frontendbeatrizamante44635011
23bhagyamudgal/cuju-webbhagyamudgal24377645
24bhagyamudgal/notisolbhagyamudgal24377645
25bhagyamudgal/worktree-clibhagyamudgal34377645
26bitzquad/bitzquad.combitzquad1
27bitzquad/bitzquad.com-2.0bitzquad1
28bitzquad/nebula-docsbitzquad1
29braune-digital/bd-php-to-ts-converter-bundlebraune-digital24634733
30braune-digital/BrauneDigitalImagineBundlebraune-digital44634733
31Code-Web-Basic/CompilerGoCode-Web-Basic54634925
32CurryMessi/tiktok-videoCurryMessi24635243
33czech-sfl/konferenceczech-sfl24634914
34dandycheung/Framesdandycheung14559058
35dcc-cc3002/citric-liquid-Benjjvvdcc-cc300284619268
36dcc-cc3002/citric-liquid-cpereiramdcc-cc300294619268
37dcc-cc3002/citric-liquid-ihumiredcc-cc300294619268
38dcc-cc3002/citric-liquid-Jarinxdcc-cc300294619268
39dean-s-list/business-visa-serverdean-s-list24377645
40dean-s-list/deanslist-platformdean-s-list24647225
41dean-s-list/deanslist-servicesdean-s-list24377645
42dsrikant/smarcartdsrikant44620322
43dzhu8/dzhu.github.iodzhu824634066
44EdsonVillarroel/e-commerceEdsonVillarroel64635467
45EdsonVillarroel/portfolioEdsonVillarroel114635467
46EdsonVillarroel/vision-kitEdsonVillarroel24635467
47erbieio/erbieerbieio154390381
48Factlink/js-libraryFactlink34548618
49Factlink/url_normalizerFactlink24548618
50GNF-Labs/millenium-lms-web-appGNF-Labs44463853
51jchable/gpx-utility-analyzerjchable24634567
52jedsada-gh/ApiMovie-UPjedsada-gh34645945
53jedsada-gh/blockchain-playgroundjedsada-gh24634301
54jedsada-gh/co-work-adminjedsada-gh44634301
55jedsada-gh/co-work-androidjedsada-gh34634301
56jedsada-gh/co-work-apijedsada-gh34634301
57jedsada-gh/co-work-katalonjedsada-gh24634301
58jedsada-gh/co-work-providerjedsada-gh34634301
59jedsada-gh/node-js-netpiejedsada-gh94634301
60jgutierrezdtt/skills-hello-github-actionsjgutierrezdtt24643795
61jgutierrezdtt/Sports-Centerjgutierrezdtt104643795
62jgutierrezdtt/Vulndemojgutierrezdtt34643795
63killerapp/mermaid-renderkillerapp44646419
64KSU-Quantum-Capstone/CS4850-DL1KSU-Quantum-Capstone44630939
65kylezap/ctrl-alt-winkylezap214645618
66kylezap/kylezapcicdotcomkylezap24377461
67kylezap/rightsize-mealskylezap214645618
68kylezap/tree-viewkylezap24377461
69leanderloew/explainability-simulationleanderloew24633915
70lucasconnellm/openclaw-fluxerlucasconnellm44633825
71messismore/Digitale-Ausstellungmessismore44634208
72messismore/Studio-Grottomessismore44634208
73metersphere/helm-chartmetersphere34646584
74mhar-andal/MyBlokmhar-andal64508470
75mhar-andal/stock-forum-ethereummhar-andal24508470
76mmlngl/contacttracing.app-graphql-apimmlngl34647729
77mmlngl/flua-launchmmlngl34647729
78nasher721/3dgeneratornasher72124634138
79nasher721/AnkiFellowCollabnasher72184634138
80nasher721/Extract721nasher72134646453
81nasher721/Medical-OCRnasher721134646453
82nasher721/note-claritynasher72144634138
83nasher721/remix-of-remix-of-round-robin-notesnasher721204646453
84nasher721/remix-of-round-robin-notesnasher72164634138
85nasher721/schedulernasher721214646453
86nasher721/textcleanernasher72134634138
87neilfarmer/k8s-healthneilfarmer94634793
88neilfarmer/platform-specneilfarmer84634793
89Netpoc/companyNetpoc24646713
90nodejs-indonesia/blogsnodejs-indonesia24634519
91nodejs-indonesia/starter-loopback-fireloopnodejs-indonesia144645302
92otaviosoaresp/what-the-forkotaviosoaresp24622883
93paulmojicatech/angular-dc-meetup-may-24-2022paulmojicatech44634921
94paulmojicatech/pmtpaulmojicatech64634921
95paulmojicatech/wonder-wormpaulmojicatech24634921
96PositionExchange/decentralized-perpetual-trading-protocol-cross-chainPositionExchange274645446
97PositionExchange/dptp-client-sdkPositionExchange34648910
98PositionExchange/evm-matching-enginePositionExchange44648910
99PositionExchange/position-protocolPositionExchange275351744
100Pouleyy/nodeAirBnBPouleyy24635440
101rhemlock7/ecommerce-back-endrhemlock744646471
102rhemlock7/express-note-takerrhemlock774646471
103rhemlock7/minimalist-portfolio-mkiirhemlock794646471
104rhemlock7/NoSQL-Social-Networkrhemlock754646471
105rhemlock7/SQL-Employee-Trackerrhemlock764646471
106rhemlock7/svg-logo-makerrhemlock764646471
107rhemlock7/TeamSync-Clientrhemlock734646471
108rhemlock7/TeamSync-KanBanrhemlock784646471
109rhemlock7/weather-app-apirhemlock794646471
110rudy-marquez/WebGoatNetrudy-marquez34634940
111Saul9201/daily-trendsSaul920124645778
112Saul9201/hello-circleciSaul920124376490
113Skipperlla/rn-swiper-listSkipperlla84611776
114Slickteam/hubspot-javaSlickteam44549341
115snoopyrain/rails102snoopyrain94635175
116Summit-Bank-Limited/corporate-websiteSummit-Bank-Limited124646713
117tumolaha/lerning-setuptumolaha24646776
118Weasledorf-Inc/taskmasterWeasledorf-Inc74634793
119WilChrist/SecurityAuditAppWilChrist24549461
120WilChrist/WilCovEstWilChrist24549461
121wormholes-org/wormholes-clientwormholes-org54650163
122Zaynex/13f-visZaynex94549400
123Zaynex/x-atmZaynex34549400
123 rows
| 4 columns

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-website carries the payload on 12 branches, with commits forged under the address abubakarzubair@summitbankng.com.
  • A DeFi exchange. PositionExchange/position-protocol and three sibling repos are live, the protocol repo poisoned across 27 branches, commits forged as justin.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.cl addresses.
  • A capstone team. KSU-Quantum-Capstone/CS4850-DL1 at Kennesaw State.
  • An infrastructure project. metersphere/helm-chart, the Helm chart for the MeterSphere testing platform, with commits forged under its real @fit2cloud.com maintainers.

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.

Terminal window
# PositionExchange/position-protocol, develop branch
# https://github.com/PositionExchange/position-protocol/commit/ce456191d71c3399c114b3d539666477df1322bc
author: Justin <justin.gun@position.exchange>
date: 2022-11-14T15:51:31Z # backdated ~3.5 years
verified: false # unsigned
message: Update README.md [skip ci] [skip ci] [skip ci] [skip ci] skip-checks:true

angular-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.js using 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 Logo

SafeDep Team

safedep.io

Share

The Latest from SafeDep blogs

Follow for the latest updates and insights on open source security & engineering

Axios Typosquats Deliver the Epsilon Stealer

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,...

SafeDep Team
Background
SafeDep Logo

Ship Code.

Not Malware.

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