On April 2, 2026, a GitHub account created that morning opened 475 pull requests in 26 hours. Each carried the title "ci: update build configuration." Each injected a five-phase payload that exploited a well-known GitHub Actions trigger to steal repository secrets. The campaign was detected, analyzed, and reported with a reassuring conclusion: success rate was less than 10%.
That framing is the problem.
The Reassuring Version
Here's how the security community described prt-scan:
"Across over 450 analyzed exploit attempts, researchers observed a <10% success rate. In most cases, successful attacks were against small hobbyist projects, and only exposed ephemeral GitHub credentials for the workflow."
That's a defense story. Small targets, temporary tokens, low hit rate. The message: the ecosystem held.
The Actual Numbers
The actor's marginal cost per PR: effectively zero. The defender's cost per review: a human's attention, or an AI code review tool that may or may not flag it. At machine speed, <10% is not a failure rate. It's a business model.
Three Weeks, Six Accounts, One Evolution
The campaign ran March 11 through April 3, 2026. SafeDep and Wiz Research traced six GitHub accounts to a single actor, all registered via Proton Mail plus-addressing (testedbefore@proton.me, testedbefore+89, +55, +99, then elzotebo@proton.me, elzotebo+88).
What evolved was the payload sophistication:
os.system() calls. Makefile injection. 47 PRs across two accounts. Manual reconnaissance, manual targeting./proc scanner daemon polling every 2 seconds for 10 minutes. Two new accounts.conftest.py (pytest auto-load). Node gets hijacked package.json scripts. Rust gets build.rs. Go gets test file injection. 475 PRs in 26 hours from a single account created that morning.The jump from Phase 1 to Phase 3 took three weeks. From human-crafted payloads to AI-generated ones took the same actor from 47 PRs across five days to 475 in a single day. That's a 10x throughput increase from the AI transition alone.
Zero Infrastructure
The detail that makes prt-scan structurally different from previous supply chain attacks: the attacker used no external command-and-control infrastructure.
All exfiltration flowed through GitHub's own systems:
| Channel | Method | Detectable? |
|---|---|---|
| CI build logs | gzip + base64 between ==PRT_EXFIL_START== markers |
No — legitimate log output |
| PR comments | Compressed data posted via workflow's GITHUB_TOKEN |
No — all traffic to api.github.com |
| Cloud metadata | Probes 169.254.169.254 for AWS/GCP/Azure credentials |
No — link-local, never leaves host |
| /proc daemon | Background process captures secrets loaded by later CI steps | No — runs within workflow container |
Network monitoring is useless against this. Every byte of stolen data travels through GitHub's own API endpoints, indistinguishable from legitimate CI activity. The pipeline is simultaneously the build system, the attack surface, and the exfiltration channel — three roles that cannot be separated because they are the same system.
The AI Reviewed It
Multiple targeted repositories used AI-powered code review tools. The results:
One tool caught it. One missed it entirely. One approved it. This is AI-powered defense in practice: inconsistent, configuration-dependent, running against an attacker who controls the iteration cycle.
The attacker's AI generates language-aware payloads that adapt per tech stack. The defender's AI runs once per PR with whatever heuristics it shipped with. The attacker iterates 475 times in a day. The defender doesn't iterate at all.
The Cycle Closes
In "The Self-Generating Supply Chain" I argued that AI coding supply chains are cyclic graphs — attacks feed back into the system they exploit. prt-scan is the first campaign where that cycle is directly observable:
pull_request_target → steals npm token2. Stolen token publishes 106 malicious versions of @codfish packages
3. Other projects install compromised package via
npm install4. Compromised package executes in those projects' CI workflows
5. Those workflows have their own secrets → return to step 1
The <10% success rate on the initial PRs doesn't bound the total damage. Each successful exfiltration creates new attack vectors. Each compromised package multiplies the surface without the attacker submitting another PR. The seed cost is six Proton Mail addresses and some API calls. The propagation cost is zero.
The Economics of Less Than Ten Percent
In security reporting, low success rates are presented as evidence that defenses work. That framing imports an assumption from targeted attacks: that each attempt has a cost. When the attacker's marginal cost is zero and throughput is machine-speed, the frame breaks.
At 475 PRs per day with <10% success:
- ~47 repositories leaked credentials in a single day
- Targets included AWS, SAP, Palo Alto Networks, Red Hat, Svelte (80K stars), Sentry, OpenSearch, NixOS, Zephyr RTOS, Jina AI
- 2 npm packages compromised → 106 malicious versions → unknown downstream installs
- Total campaign infrastructure: six free email addresses
This is offense that scales with compute — not a targeted intrusion that succeeds or fails, but a distribution. Hundreds of attempts, most failing, the ones that succeed compounding through the dependency graph. The actor didn't need 90% success. They needed the math to work at scale.
It did.