Skip to content

claude-e2e-verify: indirect prompt injection is absent from the lane's threat inventory (follow-up to #288) #299

Description

@kyle-sexton

Follow-up to #288. Same lane, different threat class — this one is about what reaches the AGENT, not what reaches the credentials.

The gap

claude-e2e-verify.yml's header enumerates its threats as trigger safety (:43-51), fork secret handling (:52-54), secret inheritance (:55), least privilege (:56-57), log hygiene (:58-60), template injection into run: steps (:61-64), and persisted-credential coexistence (:65-92).

Indirect prompt injection is absent from that list, and this lane feeds the agent more untrusted input than either review lane.

Three facts, all at c136b27:

  1. The agent is pointed at PR-authored text by default. e2e-spec's default (:125-129) instructs: "Inspect the PR diff (use gh pr diff) to infer the primary user journeys this change affects". PR diff content is attacker-authored on any PR.
  2. The agent is pointed at PR-authored rendered content. The whole lane exists to drive the app built from the PR head (:404, :414-416). Page text, DOM content, and console output all originate in the PR.
  3. The agent holds credentials and has no tool restriction. The default claude_args is --model claude-sonnet-5 --max-turns 75 (:130-135) — no --allowedTools. Both review lanes constrain theirs (claude-review.yml:112-114, claude-security-review.yml:228-230 both pass --allowedTools "mcp__github_inline_comment__create_inline_comment"). The e2e agent runs with an authenticated gh (:365) and, per claude-e2e-verify: persisted-credential acceptance rests on a bound that does not hold — needs human re-ratification #288 item 4, the Claude App installation token in its environment.

So: untrusted text from two independent channels reaches an agent with shell access, an authenticated gh, a 75-turn budget, and no tool allowlist.

Why this lane is the sharp case

The review lanes have partial mitigation the e2e lane lacks entirely:

  • Both review lanes take exclude-comments-by-actor and default it to the Dependabot spellings — explicitly described in-source as "prompt-injection hygiene" (claude-review.yml:135-142, claude-security-review.yml:231-238). e2e has no such input; zero occurrences in the file.
  • Both review lanes constrain --allowedTools. e2e does not.
  • The review lanes read a diff. The e2e lane reads a diff and browses a live application rendering attacker-controlled content — a channel with no equivalent in the review lanes and no filtering at all.

What I am not claiming

I am not asserting a working exploit, and I have not attempted one. The claim is narrower and, I think, sufficient: this is a recognized threat class for agentic systems, the lane's own header inventories six other threat classes and reasons carefully about each, and this one does not appear. An acceptance review cannot weigh a risk that was never written down.

Nor am I claiming the missing --allowedTools is necessarily wrong — an e2e agent that must author and run Playwright scripts plausibly needs broad tool access, which is exactly why it deserves an explicit recorded decision rather than an unstated default.

Disposition

Fold into the same human re-ratification #288 requests, as a separate question from the credential residual: not "what can read the credentials" but "what can steer the agent that holds them". The two interact — a steered agent is a more capable attacker than a passive credential reader — but they need separate answers, and the current header answers neither.

Related

Follow-up to #288.

🤖 Generated with Claude Code

https://claude.ai/code/session_013pLW2dybov9xvTFtx48Ueb

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions