Hackers have quietly planted malware in 42 real GitHub repositories. The traps fire when a developer opens a project with Claude, Gemini, Cursor, or VS Code. The repositories belong to 11 legitimate owners, including individual developers, students, and one company.
The hidden code sits in a file named .github/init.js, and it is huge, between 12.9 MB and 13.7 MB. Small settings files for each tool call this script when a session starts or a folder opens.
Developers already worry about GitHub comment prompt injection that tricks AI agents, but this case needs no clever prompt. It simply abuses the startup hooks that the tools trust.
Researchers at JFrog Security Research identified the armed repositories while digging into an npm worm they track as the “testmetest” campaign.
Another firm, SafeDep, had first described the npm side on October 22 and reported no confirmed victims. JFrog’s look at the GitHub side showed that real repositories had already been armed.
The payload is a rebuild of a hidden CI publisher called ci.js. A CI publisher is a script that runs inside GitHub Actions and pushes new packages to npm.
JFrog says the operator wanted to turn stolen GitHub and npm access into fresh malicious releases, which then feed more stolen access back into the loop. The same TrapDoor supply chain campaign also aimed at AI coding assistants, so this trick is not entirely new.
In each victim repository, the hook files wait for a trigger. The Claude settings file, for example, runs node .github/init.js || bun .github/init.js at session start.
Matching triggers were placed for Gemini, Cursor’s MCP configuration, and VS Code folder-open tasks. Some projects also had package.json, composer.json, or Makefile changes. In plain terms, a normal habit like opening a workspace could run the payload.
The files stayed hidden because of their size. GitHub code search did not index the huge blobs, and the contents API could not return them.
JFrog’s first sweep found 26 repositories, and the files looked empty. Checking the repository trees directly raised the total to 42. The Register also covered the findings.
All 10 wrapper variants decode to the same payload, so the size differences do not change what it does. The attacker also faked history. On September 28, scripted batches copied old commit messages and dates from real history and added skip-checks:true. That made the activity look much older than it was.
JFrog traced several hijacked identities. One account, leamasuero, belonged to a developer with real work history from 2014 to 2022.
By July 2026 it was used for testing, and on September 28 the attacker renamed repositories and armed 13 of them in two minutes. In the company account, Crayon-Biz-LLP, commits came from at least two members, which suggests several stolen credentials.
The npm side also changed tactics. Three packages carry a compiled native addon and an encrypted file called .napi_cache. The addon decrypts it with the XOR key 0x96, drops a script into a temporary folder, and runs it in the background.
It stays asleep on CI systems, containers, small virtual machines, and debuggers, so automated scanners miss it. The Windows build also checks for tools such as Wireshark, Fiddler, and x64dbg. This is the kind of trusted-looking bait seen in the fake AI tool campaign that targets developers.
JFrog also found scanner workflows in the same hijacked accounts. They download a tool to /tmp/.svc and scan for cPanel and WHM systems vulnerable to CVE-2026-41940, an authentication bypass.
JFrog found no proof that any cPanel server was actually breached. The link is operational, and not one proven chain from every npm infection to the exploit.
Defenders should remove the malicious package versions and rebuild affected systems from trusted sources. They should rotate GitHub, npm, cloud, SSH, and AI-provider credentials, and check every reachable repository for the hook files above.
Teams should read repository trees directly instead of relying on search. They should also watch for HalluSquatting poisoning AI assistants, since AI tools can be steered toward fake resources.
One order matters: remove the net-health-checker and pkg-runtime persistence before revoking the stolen GitHub token, because the worm contains a token-revocation wiper.
| Type | Indicator |
|---|---|
| Scanner C2 | 43[.]228[.]157[.]68:80 |
| Scanner download pattern | hxxp[:]//43[.]228[.]157[.]68:80/api/dl/<amd64\|386\|arm\|arm64> |
| Exfiltration endpoint | hxxps[:]//aiarmor[.]website/v1/api |
| Native addon loader download | hxxps[:]//raw[.]githubusercontent[.]com/CementBeetleSteeple/node-cache-utils/main/loader.js |
| GitHub operator account | CementBeetleSteeple |
| Campaign marker | msync-3f7a |
| Token dead-drop commit prefix | AutoSyncComplete: |
| Commit message / trailer | chore: sync; skip-checks:true |
| Infection commit marker | shellpanel execution smoke |
| Branch patterns | chore/ci-<hex>; deps/upd-<digits> |
| Exfiltration file pattern | data/snap-<timestamp>-<n>.json |
| Malicious npm packages | data-pipeline-testmetest, event-bus-lite-testmetest, event-emitter-testmetest, log-rotate-testmetest, log-sink-testmetest, net-codec-testmetest, npm-publish-testmetest, pixel-map-testmetest, retry-utils-testmetest, test-brotli-testmetest, test-brotli2-testmetest, @iobandamwer/cache-helper, @iobandamwer/native-helper, path-stream-utils-testmetest |
SHA-256, victim publisher payload _p | 9d83919bfdb0e14018f1a6f2b8b6586de59b60dfdcfbdb5ae1a1556c23cc4793 |
SHA-256, victim Bun bootstrap _b | 3dc40fc55302664c07c9d9182ffd95910aeb366fcd417fbdad0a90cdeb8ea69e |
SHA-256, ci.js publisher payload | 8506042b383ab46545c27a402eb2dc973696a93af71181b175b1d87972843a43 |
SHA-256, ci.js v3 publisher payload | 783a4ac906cc9b2eaf5cd13408ea95db6ce52a46d9af11aad1a5b167af69589f |
SHA-256, stable-scripts-9965 loader | a516dd7867af83f8be0cc3e926fdfd964097f0e7665596b05fe5234be7e2b5e9 |
SHA-256, stable-scripts-9965 decoded stage | ccfd769acc024edc409ef879198d2a2398beee60930f2b3f2771ded1e14e0c57 |
SHA-256, stable-scripts-9965 publisher payload | 7ad216b3afd2f40a147c80733c7309fb3d3a7f52ee78149c8e51672cfb617d07 |
| SHA-256, Shellpanel mock | e3cb383615fc70bce9b5bcd1c7a394132b50483daaf231a04ee90d4690962c73 |
| SHA-256, Shellpanel marker tool | 17614e98b613b17d0e1bb7125ab203715dd397661b897d8d18dbb77864c1839f |
| Files and paths | .github/init.js, /tmp/.svc, addon.node, addon_win.node, .napi_cache (XOR key 0x96), /tmp/.<pid>, /tmp/.hc, %TEMP%\.nc<PID>.js, fake CodeQL artifact scan-output |
| Persistence | net-health-checker, pkg-runtime, ~/.local/bin/net-health-checker.sh, ~/.config/net-health-checker/, ~/.local/share/runtimes/sync.py, /var/tmp/.fontconfig_state |
| AI and IDE hooks | .claude/settings.json, .gemini/settings.json, .cursor/mcp.json, .cursor/rules/setup.mdc, .vscode/tasks.json |
Note: IP addresses and domains are intentionally defanged (e.g., [.]) to prevent accidental resolution or hyperlinking. Re-fang only within controlled threat intelligence platforms such as MISP, VirusTotal, or your SIEM.
Give your SOC the alert context to stops incidents quickly. Integrate Threat Intel from 16K SOCs.
OpenAI has launched GPT-6 Cyber, a security-focused AI model that found 81 new high-severity Firefox…
Shopify is making IP allowlisting mandatory for public apps that call its Admin API, adding…
Microsoft’s Windows Paint vulnerability, CVE-2026-83844, could allow an unauthenticated attacker to execute code over a…
Melbourne, Florida, November 5th, 2026, CyberNewswire OpenMatter Network, a pioneer in decentralized infrastructure for agentic…
A new Android bug named GhostMap can leave a phone stuck in a crash loop…
A China-aligned hacking group is abusing Sogou Input Method, one of the most popular Chinese…