ExGuardian

Guide · AI agents with tools · Sources read on 29 October 2026

Indirect prompt injection, from CVE to test plan.

Short answer. Indirect prompt injection is an instruction hidden in content an agent reads on its own: a web page, an email, a document, a ticket, a tool result. The model cannot reliably tell data from instructions, so the attacker steers the agent without ever talking to it. Published CVEs show where it ends: data disclosure from Microsoft 365 Copilot (CVE-2025-32711, CVSS 9.3) and code execution through Cursor (CVE-2025-54135). Filters lower the success rate without reaching zero. What holds is architecture: keep private data, untrusted content and an exit channel out of the same agent context, then test every input channel before go-live.

What is indirect prompt injection?

OWASP's LLM01:2025 separates two cases. A direct injection comes from the user's own prompt. An indirect injection happens when the model accepts input from external sources such as websites or files, and that input carries instructions. For an agent, external sources include everything a tool returns: search results, inbox contents, retrieved documents, issue comments, API responses and MCP tool results.

The OWASP Top 10 for Agentic Applications 2026 lists the consequence as ASI01 Agent Goal Hijack: the agent keeps working, but toward the attacker's goal. Two OWASP scenarios show the usual shapes. A page to summarize hides instructions that make the model insert an image whose URL carries the conversation out. A document edited in a RAG store skews the answer of every user whose query retrieves it.

What does indirect prompt injection look like in real CVEs?

CVEProductWhat NVD records
CVE-2025-32711Microsoft 365 CopilotAI command injection that lets an unauthorized attacker disclose information over a network. CVSS 9.3 (NVD lists 9.3 and 7.5), published 11 June 2025.
CVE-2025-53773GitHub Copilot and Visual StudioCommand injection that lets an unauthorized attacker execute code locally. CVSS 7.8, published 12 August 2025.
CVE-2025-54135Cursor before 1.3.9The agent could create a new dotfile without approval. An indirect prompt injection wrote .cursor/mcp.json, which ran a command on the victim's machine: remote code execution without user approval.

In each case the injection was the way in, and the damage came from what the agent could do next: send data out, run a command, write a configuration file that another component trusted. The Cursor case is the clearest. Creating a file looked harmless, but the file was an MCP configuration, and the next start ran it. The impact is set by the tools and permissions around the model, so most of the defensive work happens there.

Why do filters and system prompts fall short?

The model receives one stream of tokens. Instructions from the developer, the user's request and the text of a fetched page arrive in the same channel, and no marker in that stream is enforced by the model. Prompt-level defenses try to make the boundary visible, and they help:

A defense that stops 98% of attempts still lets the rest through, and an agent that reads thousands of documents a week meets many attempts. That is why the next two sections deal with what the agent can reach, not with how well the model resists.

What is the lethal trifecta?

Simon Willison named it on 16 June 2025: an agent that combines access to private data, exposure to untrusted content and the ability to communicate externally can be made to send that data to an attacker. His advice is to avoid the combination entirely. For a review, the useful question is which leg you can remove for each agent:

LegIn a real agentHow to cut it
Access to private dataMailbox, files, CRM, code, tokens in the environmentGive this agent only the data the task needs; split read-private and read-public agents.
Exposure to untrusted contentWeb pages, inbound email, tickets, issues, uploaded files, tool and MCP resultsProcess untrusted content in a context with no tools, or with read-only tools.
Ability to communicate externallyHTTP tools, email send, rendered images and links, webhooks, commits and commentsEgress allowlist, no remote images or links in output, human approval on every send.

The third leg is the one teams miss. A Markdown image in the agent's answer is an outbound request when the client renders it. So are a link the user clicks, a comment posted on a public issue and a commit pushed to a public branch. Count every one of them as an exit.

Which design patterns resist indirect prompt injection?

A 2025 paper by Beurer-Kellner and 13 co-authors describes six patterns that give provable resistance by limiting what untrusted data can influence. Each trades some flexibility for that guarantee.

PatternHow it worksFits
Action-SelectorThe model only picks one of a fixed set of actions. Tool results never come back to it.Routing and simple commands. Strongest, least flexible.
Plan-Then-ExecuteThe model commits to a plan before it reads untrusted data, so that data cannot add or change actions.Workflows with a known shape. Arguments can still be steered.
LLM Map-ReduceIsolated sub-agents each process one untrusted item; a reduce step combines their constrained outputs.Triage of many documents, emails or reviews.
Dual LLMA privileged model plans and calls tools without ever seeing untrusted data; a quarantined model with no tools reads it.Assistants that must read untrusted content and act.
Code-Then-ExecuteThe model writes a program that calls tools and hands untrusted text to unprivileged models; the program runs.Complex tasks. CaMeL implements this idea with capabilities.
Context-MinimizationContent that is no longer needed, such as the user's prompt after the first step, is removed from the context.Answers that should not be shaped by earlier input.

Plan-Then-Execute is the easiest to retrofit into an existing agent, and the most often misread. It fixes which tools run, not what goes into them: a poisoned email can still choose the recipient of a send that the plan allowed. Pair it with argument checks on every tool that leaves the system. CaMeL's code is public in the google-research/camel-prompt-injection repository.

How do you test an agent for indirect prompt injection?

  1. List the channels. Every tool that returns text written by someone other than the user: search, fetch, inbox, file store, retrieval, ticketing, code host, MCP servers. Each one is a test target.
  2. Pick goals you can detect. A marker string that appears nowhere else, a call to a tool the task does not need, a request to a URL you control. A goal you can only judge by reading the answer is too weak to count.
  3. Plant one payload per channel. Place it where real content sits: an email body, a document footer, a ticket comment, a field of a tool result. Vary the wording; one phrasing tells you little.
  4. Run each case several times. Model output varies between runs, so a single pass proves nothing. Record the attack success rate per channel and the task success rate, since a defense that blocks the task too is not a fix.
  5. Read the traces, not the answer. The pass condition is zero forbidden tool calls and zero requests to your URL, taken from the agent's logs. A clean final answer can hide a call that already happened.
  6. Repeat after each change of model, system prompt, tool or data source.

Which open-source tools help?

ToolWhat it doesUse it for
AgentDojoAgent benchmark: 97 realistic tasks and 629 security test cases across tool-using suites, with pluggable attacks and defenses.Compare defenses and models on an agent with tools; adapt a suite to your own tools.
garak (NVIDIA)LLM vulnerability scanner. Its latentinjection probes bury instructions inside reports, resumes, legal and fact snippets, translations and whois records.Model-level baseline before you build the agent, and after each model change.
PyRIT (Microsoft)Red-teaming framework. Its XPIA workflow plants attack content in one place (a file, a mailbox, a store), then triggers the processing target and scores the response.End-to-end tests on your own agent, with your real channels.
AgentDojobenchmark run
python -m agentdojo.scripts.benchmark -s workspace --model gpt-4o-2024-05-13 --defense tool_filter --attack tool_knowledge
garaklatent injection probes
python3 -m garak --target_type openai --target_name gpt-5-nano --spec probes.latentinjection

The three cover different layers. garak measures how a model reacts to instructions buried in text, before any tool is involved. AgentDojo measures an agent with tools in a controlled environment, which makes it the place to compare defenses. PyRIT's cross-domain prompt injection (XPIA) workflow drives your own system: one target plants the attack content where your agent will find it, a callback makes the agent process it, and a scorer decides whether the attack succeeded. Only the last kind of test tells you about your real channels.

What should be true before go-live?

Indirect prompt injection questions.

What is the difference between direct and indirect prompt injection?

In a direct injection the attacker types the instruction into the prompt. In an indirect injection the instruction arrives through content the model reads from an outside source, such as a web page, a file, an email or a tool result, so the attacker never talks to the agent. OWASP covers both under LLM01:2025.

Can indirect prompt injection be fully prevented?

Not with filters or prompts alone: they lower the success rate without removing it. The defenses with guarantees are architectural. They keep untrusted data from changing which actions run, as in the Dual LLM, Plan-Then-Execute or CaMeL designs, and they remove the exit channel the attacker needs.

Is RAG vulnerable to indirect prompt injection?

Yes. A document added or edited in the retrieval store carries its instructions into every answer that retrieves it. OWASP LLM01:2025 lists this as an attack scenario: a poisoned document skews the model's answer for every user whose query pulls it in.

How do you test an AI agent for indirect prompt injection?

List every channel that brings outside content into the agent, plant a payload in each with a goal you can detect (a marker, a forbidden tool call, a request to a URL you control), run each case several times, and count forbidden actions. Benchmarks such as AgentDojo and frameworks such as garak and PyRIT automate parts of this.

Does spotlighting work?

It helps. Microsoft researchers reported attack success falling from above 50% to below 2% in their GPT-family experiments, using delimiting, datamarking and encoding of untrusted text. It is still a probabilistic defense: the remaining cases are why tool permissions and egress limits matter.