Comment and Control: One Payload, Three Vendors

One payload broke three vendors. On April 15, 2026, researcher Aonan Guan, working with Johns Hopkins University's Zhengyu Liu and Gavin Zhong, disclosed the first public cross-vendor prompt-injection attack against AI coding agents. A single pattern, delivered through ordinary GitHub data, hijacked Anthropic's Claude Code Security Review, Google's Gemini CLI Action, and GitHub's Copilot Agent running in GitHub Actions and turned each into a channel for stealing the host repository's secrets. Guan named it "Comment and Control," because GitHub itself serves as the command-and-control medium: the payload arrives in a PR title, issue body, or comment, and the loot leaves through a PR comment, a public issue reply, or a git push. No external server is involved at any point.

The attack fires itself

Classic indirect prompt injection waits for a victim to ask the agent something. This one does not. GitHub Actions workflows trigger automatically on pull_request, issues, and issue_comment events, so simply opening a PR or filing an issue runs the agent against attacker-authored text. All three agents share the same flow: read GitHub data, treat it as task context, execute tools based on it. The injection surface is anything an outsider can write into your repo, which on a public repo is nearly everything.

Three findings, one shape

AgentInjection vectorWhat leakedExfiltration channel
Claude Code Security ReviewPR title, interpolated unsanitized into the prompt (claudecode/prompts.py)ANTHROPIC_API_KEY, GITHUB_TOKENPosted back as a PR comment (or read from the Actions log)
Gemini CLI ActionIssue comment appending a fake "Trusted Content Section" claiming key-sharing is safeGEMINI_API_KEYPosted as a public issue comment
Copilot Agent (SWE Agent)Payload hidden in an HTML comment, invisible in rendered Markdown4 credentials, including GITHUB_TOKEN (ghs_) and GITHUB_COPILOT_API_TOKEN (ghu_)git push of a base64-encoded file to github.com

The Claude finding is the bluntest: the claude CLI was invoked without --allowed-tools or --disallowed-tools, so a hostile PR title could instruct it to run env and post the output. The Copilot finding is the most instructive, because it defeated all three of GitHub's runtime defenses at once. Environment filtering only stripped secrets from the bash subprocess; the parent Node.js and MCP-server processes kept the full environment, so ps auxeww reading /proc/[pid]/environ recovered everything. Secret scanning matched ghs_/ghu_ patterns; base64-encoding the dump walked past it. The network firewall allowlisted github.com; the exfiltration was a git push. Layered runtime defenses all failed because the layers shared one wrong assumption: that the agent holding the secrets could safely read the attacker's text.

The root cause is architectural, not a bug

Guan's framing is the part worth keeping: "the prompt injection here is not a bug; it is context that the agent is designed to process." These agents must read issue bodies and PR titles to do their jobs, and they hold production secrets to do their jobs. Those two requirements conflict directly. Blocklisting the conflict away is whack-a-mole: Anthropic blocked ps, and cat /proc/*/environ achieves the same result. This is the lethal trifecta assembled inside a CI runner: secrets as the private data, attacker-authored GitHub text as the untrusted content, and GitHub's own write APIs as the exfiltration leg.

The vendors paid $1,937 and assigned no CVEs

VendorResponseBounty
AnthropicAccepted at Critical (CVSS 9.3, raised to 9.4 on 2025-11-25); stated the action "is not designed to be hardened against prompt injection"; shipped --disallowed-tools 'Bash(ps:*)'; on 2026-04-20 reclassified the severity to None$100
GoogleAccepted via VRP$1,337
GitHubClosed as "Informative," reopened after Guan supplied reverse-engineered source evidence, resolved as a "previously identified architectural limitation"$500

No CVEs were assigned and none of the three vendors published a public advisory. Reports ran from October 2025 to February 2026 before the April disclosure. All three eventually engaged, and Anthropic shipped a mitigation and docs. But $1,937 in total bounties and a severity quietly reclassified to None is the market saying the agents' own runtimes are out of scope.

The property that would have prevented it

Guan's prescription matches this handbook's: allowlist-only capability and secret scoping, treating every agent like a new employee with need-to-know. A code-review agent that does not need bash should not have it. An agent that only summarizes issues should not hold a write-scoped GITHUB_TOKEN. Above all, never run the agent that ingests untrusted input in the same runtime that holds production secrets. That is the brain and hands split this section's environment design is built on: credentials never enter the sandbox where attacker-influenced execution happens, and deny-by-default egress gives a stolen value nowhere to go. Note the github.com lesson, though: when the exfiltration channel is the tool the agent needs to do its job, egress filtering alone is not enough, and the credential's scope has to carry the control. Breaking the chain walks the full casebook through which link to cut.

Sources: Aonan Guan, first-party disclosure · SecurityWeek · Cybersecurity News.