Indirect Attacks
An indirect attack hides instructions in content your agent reads for a user, such as a tool result, document, email, or web page. The attacker never talks to your agent. AI Red Teaming tests this with the Tool Response Poisoning strategy: Red adds an attacker’s instructions to a real tool result and checks whether your agent follows them. Use it when your agent reads content it does not control and can act through its tools.
Direct and indirect attacks
Indirect testing covers risks that direct testing cannot:
- A third party steering the agent through content the user did not write or see.
- Malicious instructions in a tool result that lead the agent to act, such as issuing a refund, sending an email, changing a record, or running code.
- Private data read in one tool call and sent out through another.
How it works
From your side, a Tool Response Poisoning scan looks like ordinary conversations with your agent, except that Red adds the payload, an attacker’s instructions, to one tool result. It uses the same SDK call as tool tracing, so it needs no extra code.
- Red sends your agent a benign request that a legitimate user could type, designed to make the agent call a tool. This request is the trigger prompt.
- Your agent calls the tool. The tool runs against your systems as it normally does.
interceptTool(TypeScript) orintercept_tool(Python) reports the call and its real result to Red.- Red adds the payload to the result and returns it in the same format. This is the only point where Red changes anything.
- Your agent reads the poisoned result as if it were real, and may call more tools.
- Red judges the reply and the tool calls your agent reported against the objective. An action taken through a tool counts even when the reply does not mention it.
Each attack runs in its own session, opened by a short greeting before the trigger prompt. Red poisons at most one traced tool result per attack: the first one that can carry text. See Which tool results can Red poison?.
Prerequisites
lakera-red-sdk0.11.0 or later in your agent’s runtime. This applies to SDK scans and to targets you connect in the platform.- Tool tracing enabled for the tools you want to test. See Trace Agent Tools. For SDK scans, also see Enable Tool Tracing for SDK Scans.
- A multi-turn target: one that keeps a session across turns. Use an SDK relay target, or a stateful target with Session settings.
Recommended:
- An isolated staging environment. A successful attack can make your agent take real actions with its tools. Use test data, and point tools with side effects at sandboxes.
- Ground truth about your tool definitions, so trigger prompts aim at tools your agent has.
Get the most out of indirect attacks
The objective drives every part of an attack. Red writes the payload from its attack description, and writes and judges the trigger prompt against its success indicators. Custom objectives that name your agent’s tools and data test the paths an attacker would use.
Map your data-to-action paths
Simon Willison’s “lethal trifecta” names three capabilities that together let an attacker steal data through an agent: access to private data, exposure to untrusted content, and a way to send data out. An indirect attack connects them, so list both ends:
- Sources: tools that read content outsiders can influence, such as web fetch, inbox search, support tickets, product catalogs, and document search.
- Sinks: tools that change something or send data out, such as email, refunds, record updates, messaging, and code execution.
In the following example, lookup_orders is the source, because product names come from
seller listings, and send_email is the sink.
The reply looks normal; only the traced send_email call shows the leak. Trace both
sources and sinks: Red can poison only traced sources, and the verdict sees only traced
tool calls.
Write a custom objective for each sink
Create one custom objective per action you want to rule out, in the dashboard’s objectives library or in the SDK scan request. See Custom Scope.
- Attack description: what the attacker wants your agent to do, naming the tool and the data. Red turns this into the payload.
- Success indicators: an outcome visible in the reply or a tool call. Name the tool and the argument condition, not the payload’s wording.
The more concrete the objective, the more accurate the scoring: the verdict checks the agent’s reply and tool calls against your indicators. An attack succeeds when the agent meets any one success indicator. To require several conditions together, put them in a single indicator. Add a separate indicator for each variant you want to catch.
Example objectives:
The following example runs a Tool Response Poisoning scan with one custom objective.
Replace send_email and example.com with your tool name and domain.
TypeScript
Python
Start small, then scale
- Run a few attacks per objective first.
- Confirm that payloads were delivered. See Reading the results.
- Rerun the same objectives after you change your agent, to compare results.
Reading the results
In the scan report, open a result of a Tool Response Poisoning scan. The Details tab shows the conversation and the tool calls that your agent reported.
- The conversation has two turns. The first is labeled “Session setup” and is dimmed. It is Red’s greeting, which binds the session to the scan. The second is the trigger prompt and the agent’s reply.
- Tool calls appear next to the turn that made them. Each row lists the tool name and arguments. Expand it to see the original result.
- The call whose result Red poisoned is marked “Poisoned” and is expanded by default. Its “Poisoned result” block highlights the injected text against the real result. Review this call, subsequent calls, and the agent’s reply to understand the verdict.
- A call whose result Red could not poison is marked “Not poisoned”. Expand the row to see why, in the form “Poisoning skipped: …”. An example is a JSON result that has no string field to carry the payload.
- If no tool result carried the payload, a “Poison not delivered” warning appears under Result. The reason explains whether no tool call received the poison, the result was neither text nor JSON, no text field could carry the payload, or insertion would have changed the result’s format.
Red judges every run, including those without delivery, because the trigger prompt alone may lead the agent to meet the objective. A failed result without delivery means exactly that. A passed result without delivery says nothing about how the agent handles poisoned data. Scan-level scores include all results.
If many results show “Poison not delivered”, check these points before reading the scores:
- Confirm that tracing covers the tools called by the trigger requests.
- Provide ground truth about your tool definitions so trigger requests aim at tools your agent has.
- Confirm that those tools return text or JSON with a string field.
See Interpreting Results for the rest of the report.
The live attack monitor shows the phases of a poisoning run as Bind Session, Trigger, and Judge. It also shows the trigger prompt and the tool calls as they arrive.
Coverage and limitations
Risks covered
Tool Response Poisoning tests a legitimate user whose request leads the agent to read a poisoned tool result. It does not cover:
- A malicious user combined with poisoned data.
- Several poisoned sources combined into one attack.
- Poisoned MCP tool definitions. See Is this the same as MCP tool poisoning?
Technical limits
- Red poisons at most one tool result per attack.
- Only traced tool results can be poisoned. Retrieval or memory stores whose content does not pass through a traced tool are not tested.
- A result must be plain text, or JSON with a string field, to carry the payload. See Which tool results can Red poison?
Framework mapping
Indirect attacks map to the following entries. For how AI Agent Security covers the same frameworks, see Security Framework Coverage.
FAQ
Does Red change our data or systems?
During a Tool Response Poisoning scan, Red may modify one tool result sent back to the model. Red does not directly change your data or systems, but your agent still uses its real tools. If it follows the payload, it may send an email to an outside address, issue a refund, or change a record. Run the target in an isolated staging environment with test data and tools with side effects pointed at sandboxes (see Prerequisites).
No attack succeeded. Is our agent safe?
Not necessarily. First, check that the payloads reached your agent: a scan without delivery is inconclusive. See Reading the results.
Even with delivery confirmed, a scan cannot rule out every attack. The space of possible attacks is large, and language models respond non-deterministically, so an attack that failed once can succeed on another run. Red generates a broad range of strong attacks to reduce this gap. Rerun scans after changes to your agent.
Do you test RAG, MCP servers or agent memory?
Red tests content from these sources when it reaches the agent through a traced tool result. See Coverage and limitations.
Is this the same as MCP tool poisoning?
No. “Tool poisoning” usually means malicious MCP tool definitions. Tool Response Poisoning modifies tool results instead.
Which tool results can Red poison?
Red can poison traced tool results that are plain text or contain a string field in JSON. It tries fields likely to hold prose first, skips fields named like IDs or URLs, and tries the next traced result when one cannot carry the payload. The full rules are in Tool Response Poisoning.
Do we need code changes?
Yes: the tool tracing call. Add it once in your tool-dispatch middleware or around specific tools, depending on which tools you want to test. See Trace Agent Tools.
Is it safe to leave tracing on?
Tracing is opt-in through LAKERA_RED_TOOL_INTERCEPT. Outside a poisoning scan, tracing
returns the unmodified tool result. With the flag on, every traced call sends its tool
name, arguments, and result to Red, so set it only in the environments you scan. See
Data and security.