4 min read

Less Than Ten Percent

Less Than Ten Percent

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

What Was Reported
<10% success rate
"Most targets were hobbyist projects"
"Only ephemeral credentials"
What That Means
~47 successful exfiltrations in one day
Targets included Svelte (80K stars), Sentry, AWS, SAP, Palo Alto Networks
2 npm packages compromised → 106 malicious versions published

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:

Phase 1 — March 11–16
Raw bash scripts. os.system() calls. Makefile injection. 47 PRs across two accounts. Manual reconnaissance, manual targeting.
Phase 2 — March 28–29
Standardized 352-line bash script with five stages: environment dump → API reconnaissance of secrets and workflows → temporary workflow injection for secret value theft → label-based gate bypass → background /proc scanner daemon polling every 2 seconds for 10 minutes. Two new accounts.
Phase 3 — April 2–3
AI-generated, language-aware payloads. No human intervention per target. Python repos get 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:

CodeRabbit
Consistently flagged critical issues
Codacy
Zero findings on malicious PRs
gstraccini
Auto-approved the attack

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:

1. Malicious PR exploits pull_request_target → steals npm token
2. Stolen token publishes 106 malicious versions of @codfish packages
3. Other projects install compromised package via npm install
4. 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:

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.