The CI Attack Your Security Scanners Won’t Catch: Cordyceps and GitHub Actions

Your CI pipeline is green. All checks passed. The dashboard looks clean.

That means nothing if you’re using GitHub Actions in certain ways.

In June, researchers at Novee Security disclosed a CI/CD weakness they named Cordyceps. They scanned about 30,000 high-impact repos across npm, PyPI, crates.io, and Go — flagged 654, confirmed over 300 as fully exploitable. The affected tooling included projects from Microsoft, Google, Apache, Cloudflare, and the Python Software Foundation.

The entry requirement for an attacker? A free GitHub account. No org membership. No special access.

The vulnerability isn’t in any single file. It’s in the composition. The problem starts with pull_request_target and workflow_run triggers — they run in the base repository’s context with access to secrets. An attacker can trick both by putting malicious content in a pull request. GitHub Security Lab calls this the pwn request.

Three primitives do the damage. Command injection lets attacker-controlled data land unescaped in a shell. Code injection through actions/github-script evaluates attacker input as JavaScript at runtime. Cross-workflow privilege escalation lets a low-privilege workflow write data that a high-privilege workflow reads and acts on with the maintainer’s token.

No single workflow is vulnerable alone. The danger is how they connect. That’s exactly why your scanners miss it — SAST and DAST tools check one file at a time, and every YAML file here is valid, well-formed, doing what it was told.

On Microsoft’s Azure Sentinel repo, a comment on a pull request could run attacker code on Microsoft’s CI and steal a non-expiring GitHub App key — confirmed by MSRC. On Google’s AI Agent Development Kit, a single PR could escalate to owner-level access on the associated Google Cloud project. Apache Doris had a similar path.

Novee found no evidence of exploitation in the wild. But this is a proven, exploitable pattern, and it’s largely unpatched by default across the industry.

The fixes: prefer pull_request over pull_request_target. Never check out PR head code in a privileged workflow. Pin third-party actions to a commit SHA. Set default permissions to read-only. Gate privileged workflows behind manual approval.