Safeguard
AI Security

Prompt injection via AI agent CI/CD workflow tampering

A single malicious PR title was enough to make three major AI coding agents leak API keys straight out of a GitHub Actions runner.

Safeguard Research Team
Research
6 min read

In early 2026, security researchers disclosed a vulnerability class nicknamed "Comment and Control" — a play on command-and-control — showing that three widely deployed AI coding agents wired into GitHub Actions could be hijacked with nothing more than a PR title. Anthropic's Claude Code Security Review, Google's Gemini CLI Action, and GitHub Copilot's agentic SWE Agent were all affected. In the Claude Code case, an attacker could open a pull request whose title broke out of the agent's prompt context and instructed it to run commands like env or read /proc/self/environ, then post the results back as a "security finding" comment on the same PR — exfiltrating the runner's ANTHROPIC_API_KEY in plain sight, no outbound network connection required. Anthropic rated the underlying issue CVSS 9.4 and tracked it through HackerOne report #3387969, shipping fixes in Claude Code 2.1.128 and Claude Code GitHub Actions 1.0.94 that blocked reads of sensitive /proc paths and scrubbed environment variables from spawned subprocesses. This post explains why agentic CI/CD is uniquely exposed to this attack, how it actually plays out, and what stops it.

What made three unrelated AI agents vulnerable to the same attack?

All three agents shared one architectural pattern: a tool-using AI process running inside a CI runner that has ambient access to secrets, wired to a trigger that feeds it untrusted external text as part of its normal job. Claude Code Security Review and the Gemini CLI Action both run on pull_request or issue-comment triggers specifically so they can read PR titles, descriptions, and comments and act on them — that's the point of the integration. GitHub Copilot's SWE Agent works the same way when assigned an issue. None of the three had a hard boundary between "text I'm supposed to summarize" and "instructions I'm supposed to execute," so a PR title crafted to look like a system directive was processed the same way a legitimate one would be. The Gemini CLI variant went further, using a forged "Trusted Content Section" injected after the real content to override the model's own safety instructions, according to researcher writeups on the disclosure.

How did the attack actually exfiltrate secrets without leaving GitHub?

The exfiltration channel was GitHub itself, which is what made the attack quiet and hard to catch with traditional network monitoring. Once an agent was steered into running a shell command or invoking a file-read tool against something like /proc/self/environ, it had the secret in its own context window — and the same automation that let it post PR comments, commit code, or update issues gave it a built-in way to publish that secret somewhere the attacker could read it, such as a public PR comment or a commit message. No egress to an external server was required, so controls built to catch a runner "phoning home" to an unfamiliar IP never fired. The Cloud Security Alliance's research note on the disclosure specifically calls out this pattern as a structural risk of CI/CD systems: any AI agent with both credential access and an output channel back to attacker-visible surfaces is, by construction, a potential secrets pipeline.

Is this really different from classic GitHub Actions script injection?

It's the same root problem — untrusted input reaching an execution context — playing out with a much larger blast radius. GitHub's own documentation has long warned that unsanitized ${{ }} expression contexts in workflow YAML, such as directly interpolating github.event.issue.title into a run: step, let an attacker's issue title become shell code executed by the runner. That's script injection, and it predates AI agents entirely. The difference with an agentic action is that there's no ${{ }} expression to audit or lint in the YAML at all — the "injection point" is the agent's own language understanding, which has no fixed syntax boundary between data and instruction the way a shell interpolation does. A reviewer can grep a workflow file for unsanitized expression contexts; nobody can grep an LLM's prompt for every phrasing that might be interpreted as a command.

What did the vendors actually fix, and does it fully close the hole?

The disclosed fixes targeted the specific exfiltration primitives researchers demonstrated, not the underlying prompt-injection problem itself, which vendors and researchers alike describe as unsolved in the general case. Anthropic's Claude Code 2.1.128 blocked the agent's file-read tool from accessing sensitive /proc paths, and Claude Code GitHub Actions 1.0.94 added environment-variable scrubbing for spawned subprocesses plus a wrapper around the gh CLI that validates arguments and blocks known exfiltration-capable URL patterns. Those changes close the specific paths researchers proved out, but they're mitigations against known techniques, not a structural fix — an agent that can read files and run commands inside a secrets-bearing runtime remains a large attack surface for any injection technique not yet cataloged. That's why the disclosure was covered as an architecture-level warning by Microsoft's Security Blog and the Cloud Security Alliance, not just a single-CVE bug report.

What guardrails actually reduce this risk in practice?

The controls that matter most are the ones that reduce what an agent can do and see even if it gets tricked, rather than trying to make the agent immune to trickery. Least-privilege scoping is first: an agent invoked on pull_request_target or issue-comment triggers should run with a GITHUB_TOKEN limited to read-only or the narrowest write scope the task needs, never with organization-wide secrets in the same runner. Separating the "read untrusted text" step from the "execute privileged action" step — for example, having the agent draft a suggested diff that a human or a separate, unprivileged job applies — breaks the direct line from attacker text to runner execution. Treating agent identities as first-class, policy-governed principals also matters: giving each CI agent a scoped, auditable identity that can be blocked or quarantined the moment its behavior looks anomalous is a materially different posture than an ambient service account with standing access to every secret in the pipeline.

How does Safeguard help?

Safeguard's Guard product maintains a first-class inventory of the AI agents and MCP servers in an environment rather than treating them as invisible service accounts, which is the starting point for containing exactly this kind of incident. Every registered agent accumulates a running risk score and anomaly score from its actual request history, and can be blocked or quarantined immediately through a dedicated lifecycle action the moment its behavior deviates — reading unexpected paths, invoking tools outside its normal pattern — rather than waiting for a manual credential rotation after the fact. Policy can also be scoped to a specific agent or server instead of applying blindly org-wide, so a CI agent with access to deployment secrets can be held to materially tighter rules than a read-only documentation bot. None of that replaces careful workflow design — least-privilege tokens and separating untrusted-input processing from privileged execution stay the first line of defense — but it gives security teams a governed, auditable layer to fall back on when an agent is compromised despite those precautions.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.