← Real-world cases
Case study

Poisoning Claude Code: one GitHub issue hijacks the claude-code-action CI supply chain

Disclosed vulnerability01 Jun 2026🗺️ Tool-Using Agent

GMO Flatt Security's RyotaK showed that a single attacker-opened GitHub issue could indirect-prompt-inject Anthropic's claude-code-action CI agent — whose permission check reportedly trusted any "[bot]" actor — coaxing Claude to leak CI secrets and OIDC tokens, gain repository write access, and potentially poison the shared action that downstream repos pull via a floating tag.

Root cause — why it happened

Many teams let an AI coding agent run automatically in their build system: open a GitHub issue, and the agent reads it and acts on the repository. To stop strangers from abusing it, the agent was supposed to check that whoever triggered it actually has permission. But per the researcher, that check trusted any account that looked like an automated 'bot' — so an attacker who registered their own bot app could trigger it. The attacker's issue then contained hidden instructions (a prompt injection) that talked the agent into printing the build machine's secret keys into a public run log. With those keys the attacker gained write access to the repository — and because Anthropic ran the very same agent on its own repos, they could potentially tamper with the shared agent that thousands of other projects automatically pull in.

Risks this case illustrates

Named in the standard (OWASP/ATLAS/NIST) lens. Click a highlighted component in the diagram below to see which risks attach where.

How it unfolded

UntrustedAgent coreOversightThe real worldgoalscopes🧑User🎛️Orchestrator /Agent Loop🧠LLM🔐Identity &Permissions🔧Tool RuntimeHuman ApprovalGate🔌External APIs🗄️BusinessDatabase🌐UntrustedContent📝Audit Logging🌐Attacker'sGitHub App +🔐CI runnersecrets📝Public workflowrun summary🌐Downstreamrepos (pin @v1
InstructionsDataActionsControl / decisionFeedback / logscrosses a trust boundary
👆 Click a component or flow to inspect
SetupStep 1 / 6

A shared AI agent runs inside CI

A project wires up an AI coding agent so it runs automatically in its build system: mention it in a GitHub issue and it reads the request and works on the repository. To keep strangers out, it's supposed to check that whoever triggered it is actually allowed to.

⚙️Workflow wiring (illustrative)config
# .github/workflows/claude.yml (illustrative)
on:
  issues: { types: [opened] }
  issue_comment: { types: [created] }
jobs:
  claude:
    permissions: { contents: write, id-token: write }   # OIDC + repo write
    steps:
      - uses: anthropics/claude-code-action@v1   # FLOATING tag, not a pinned SHA
# Gate: checkWritePermissions() is meant to admit only write-access callers.
Step 1 / 6

Controls & guardrails — what would have stopped it

The load-bearing fix is the permission check on who can trigger the agent: verify that the caller really has access, instead of trusting anything that looks like a bot. Two more things blunt the damage even if the agent is fooled — don't keep powerful secret keys where the agent can read and print them, and lock the shared agent to an exact reviewed version so a poisoned copy can't quietly reach everyone downstream. The honest catch: filtering hidden instructions helps but never fully works, so the real guarantees are the authorization check, the secret hygiene, and the pinning.

Preventive
  • Inter-agent authentication & admission control

    Identity proves who an agent is, not that it is behaving honestly — an authenticated-but-compromised agent still needs isolation, taint-marking, and monitoring. Admission vetting is only as strong as the policy, and dynamically discovered agents in open ecosystems remain hard to fully vet.

  • Least-privilege identity & scoped credentials

    Doesn't prevent manipulation — only caps its reach. Hard to get right operationally; over-broad scopes are the common real-world failure.

  • Delimiting / spotlighting of untrusted content

    A trained convention, not enforcement. Determined payloads still break out, especially when content is long or the attack is novel. Combine with action-layer controls.

  • MCP/plugin pinning, manifest hashing & re-review

    Review catches what reviewers understand; a subtle malicious directive can pass. Pinning helps only if you actually re-review on update rather than auto-accepting.

  • Egress allowlisting & DLP on tool arguments

    Allowlists fight an open-ended channel; legitimate-but-broad destinations (any URL fetch, any email) are hard to constrain without breaking usefulness. Encoding can evade naive DLP.

Detective
Corrective
  • Governance: risk assessment, red-teaming & incident response

    Process reduces likelihood and speeds recovery but executes no technical control itself; weak follow-through makes it theatre.

  • Loop/cost circuit-breakers & consistency checks

    Thresholds are blunt — too tight breaks legitimate long tasks, too loose lets damage accrue first. Catches runaway dynamics, not a single well-formed bad decision.

Lessons

  • Authorize the *trigger*, not the actor type: an autonomous CI agent that trusts any '[bot]'/GitHub App account as authorized is a confused deputy — a self-registered app can drive it. Verify the caller's actual granted permission before the agent processes their content.
  • A CI agent that reads attacker-controllable content (issues, PRs, comments) and holds repository write authority is an indirect-injection target: its output can leak the runner's ambient secrets into world-readable logs.
  • Never leave high-value CI secrets (long-lived tokens, OIDC identities) where a manipulated agent can read and echo them to a public surface — scope them minimally, keep them short-lived, and keep them out of the agent's reachable environment.
  • A shared, reusable AI action pinned by a floating tag is a supply-chain concentration point: poison it once and every downstream repo on that tag inherits it — pin by immutable commit SHA and re-review on bump.
  • The reported figures (CVSS 7.8 v4.0, ~$4,800 bounty, ~4-day fix, v1.0.94) are the researcher's and Anthropic's own; the GitHub Actions supply-chain issue carries no dedicated CVE and is distinct from CVE-2025-66032.

Proposals & gaps this case surfaced

Non-destructive suggestions for the library — proposed, not adopted.

★ proposed sub-riskAgent trigger-authorization bypass (actor-type trust)under #43

The permission gate deciding WHO may invoke an autonomous agent over untrusted input authorizes a class of principal by a coarse heuristic — e.g. trusting any actor whose login ends in '[bot]', or any GitHub App — instead of verifying the caller's actual granted permission/installation scope. An attacker who can present as that principal type (registering their own app/bot) triggers the privileged agent and then drives it via indirect prompt injection.

✚ proposed guardrailAuthorize agent triggers by verified caller permission, not actor-type heuristicsAgent Access & Tool Control

Before an autonomous CI/event-driven agent processes a caller's content, verify the caller's actual granted permission/installation scope on the target resource — never authorize a whole class of principal by name pattern (e.g. a '[bot]' suffix, or 'is a GitHub App'). Treat the invocation itself as a privileged operation gated on verified authorization, and keep high-value runner secrets (long-lived tokens, OIDC identities) out of the agent's reachable environment and off any public artifact so a mis-triggered run cannot exfiltrate them.

This case shows a gap: we focus on limiting what an agent can do, but not enough on checking who is allowed to set it off. Trusting anything that looks like a 'bot' let an attacker's own app trigger a powerful agent — so 'verify the trigger's real permission' deserves to be its own safeguard.

These surface as proposals across the Control Library and Risk Taxonomy; adopt them by hand when ready.

Sources

Practise the risk class — related scenarios

📧The Email That Gave Orders

A support email hides instructions — and the assistant obeys them

🕵️Lies in the Loop

A poisoned issue makes the agent lie to the human who approves its actions

👂Overheard Through the Cache

A speed optimisation becomes a cross-tenant listening device

🏭Poisoning the Agent Factory

Compromise the pipeline that builds agents, and every new worker is born malicious

🪟Stealing the Model

Two doors to the same secret: reconstruct the model through its API, or just walk off with the weight file

🪤The Bug Report That Ran Code

A fake Sentry error report hijacks a developer's coding agent into running a shell command

📼The Compromised Flight Recorder

The forensic record is itself the attack surface — an agent's log is poisoned, then quietly rewritten

👁️The Invisible Webpage Command

A shopping page tells the agent to do something the user never asked for

🕵️The Logs That Lied

An attacker plants prompt injection in the audit trail — so the LLM that hunts them erases the evidence

🧠The Memory That Wouldn't Die

A single poisoned document plants a standing instruction that survives every reset

📡The Message in Morse

Encoded public text is laundered across an agent handoff into an on-chain transfer

🔓The Model That Forgot to Say No

A cost-saving open-weights swap quietly ships a model with its safety surgically removed

🖼️The Picture That Whispered

A screenshot that's harmless at full size becomes an order once the system shrinks it

💤The Sleeper

A capable third-party model that behaves perfectly — until it sees the trigger

🎫The Stolen Session

An attacker captures the agent's bearer token — and inherits its authority

🔌The Tool With a Hidden Agenda

A trusted MCP email tool quietly BCCs every message to an attacker

🥸The Uninvited Agent

A forged peer registers on the agent directory — and the planner enlists it

🛡️The Watcher Watched

The eval gate that was supposed to catch the agent is itself the thing being attacked

🪪The Worker Who Spoke for the Boss

A poisoned web page hijacks a research agent — and the planner acts on its behalf

🖱️What You Click Is Not What You Get

A GUI agent clicks 'Continue' — but the screen moved, and it lands on 'Send'

🖼️Zero-Click Leak by Picture

An inbox summary quietly ships a secret to an attacker's server

AI RiskAtlas is an educational model of how GenAI & agentic systems work and fail. Architectures and payloads are illustrative and simplified for learning — not operational guidance. Real-world cases are summarised from public reporting.

Sources & further reading →·Built by Shi Yuan ↗