What is the lethal trifecta for AI agents?
The lethal trifecta for AI agents is three capabilities in the same system: access to private data, exposure to untrusted content, and a way to communicate externally. Simon Willison named the combination on June 16, 2025. Individually each leg is a feature. Together they're an exfiltration path.
Private data is why you gave the agent tools: mail, tickets, a CRM, a repo, and a warehouse. Untrusted content is anything an attacker can write that the model reads: a public GitHub issue, a support email, a web page, a PDF, and a tool result. External communication is any channel that can carry stolen data out: send email, open a pull request, fetch a URL, write a file that the attacker can see, or even return a markdown image that the client loads.
Willison's point is architectural, not "the model was jailbroken." LLMs follow instructions in content. They don't reliably distinguish operator instructions from text glued into the same context. If you ask an agent to summarize a page and the page says to forward password-reset mail, there's a real chance that it will try. Prompt-level "don't do that" isn't a control that you can bet a payment or a customer mailbox on.
This page covers the named combination. The anatomy of an agent incident is a different question: three permitted actions that become fraud as a sequence. You can have the trifecta without that payment story, and you can have a bad sequence without an external attacker. Those are different failures.
Why is the lethal trifecta default architecture?
Most useful agents have all three legs on purpose. An agent that can't read mail can't do support. An agent that can't send can't complete the ticket. An agent that can't see private records is a chatbot. The trifecta is the job description, not a misconfiguration that you can inventory away.
The Cloud Security Alliance (CSA) published a research note on June 6, 2026 about the AI Agent Lethal Trifecta, summarizing the AI Risk Quadrant Q2 2026 assessment of 100 production agents. It reports the trifecta in 98% of those agents, and that 11% passed a baseline security bar. Treat 98% and 11% as dated assessment figures for that sample, not a law of nature. If almost every capable agent already has the combination, "don't deploy agents that have it" isn't a program.
CSA also reports that more capable agents in that sample had weaker defenses: coding agents ranked high on capability and low on defense; computer-use agents scored worst on output guardrails. That's a category warning, not a ranking of your stack. The builder takeaway is the same as Willison's: vendors won't save you once you mix tools. MCP makes that mix the default: a mail tool, a repo tool, and a "fetch this URL" tool from three registries, wired by the user in an afternoon.
| Leg | Why the agent has it | How an attacker uses it |
|---|---|---|
| Private data | The work is in the mailbox, the ticket, the repo | The model is told to read the sensitive object |
| Untrusted content | The work arrives as email, issues, pages, tool JSON | The instruction is planted in something the agent was supposed to read |
| External communication | Completing the task means send, write, or fetch | The stolen object leaves through a channel that looks like work |
How do you break the trifecta without making the agent useless?
You break the lethal trifecta by stripping one leg in the same session, not by deleting the product. The agent can still read mail, or still send, or still see private records, just not all three without a gate between them.
Three patterns each remove a leg.
Split the agent. A reader that summarizes untrusted mail must not hold the send-mail credential. An action agent that can send must accept structured instructions from an internal orchestrator, not raw email text. CSA's note calls this decomposition. It costs complexity. It's still cheaper than one god-agent with your mailbox and SMTP.
Constrain egress. If the model has seen untrusted content in this run, deny or hold any tool that can leave the trust boundary: send, open a public pull request, fetch an attacker URL, or write to an external ticket. A tool that can load an image or post a link is an exfiltration channel. Willison's collected exfiltration-attack write-ups are mostly vendors locking that channel after the fact.
Keep untrusted text out of trusted fields. Don't interpolate a ticket body, a header, or a tool result into a string that the model treats as policy. Arcjet's May 13, 2026 pattern for defending MCP tool outputs is the same rule: trusted summary from enums and templates; raw evidence in an explicitly untrusted object. Schema descriptions that label the boundary help clients. They don't make the model safe.
A content filter on the inbound email isn't "breaking the trifecta." It's shrinking the untrusted-content leg. Willison's view of vendor guardrails still stands: a claimed 95% catch rate is a failing grade for an exfiltration path. Scan anyway. Don't let the scan be the only gate on send().
How is this different from prompt injection alone?
Prompt injection is the mechanism: trusted and untrusted tokens in one context, so the model follows the wrong author. The lethal trifecta is the architecture that turns that mechanism into data theft. You can have prompt injection without the trifecta (the model says something embarrassing). You can't have trifecta theft without a way to read secrets and a way to send them.
Willison coined prompt injection as the SQL-injection analogue: mixing trusted and untrusted content. He later had to separate it from jailbreaking (tricking a model into an embarrassing completion). Developers who hear "prompt injection" as jailbreaking ignore the mailbox-forwarding case as the vendor's problem. It's the application's problem the moment you wired the three tools.
Runtime security for LLM applications already splits prompt injection, exfiltration, and unsafe actions as three failure modes. The trifecta is why those three show up together. For more information about how that detection sits on LangChain, LlamaIndex, and Vercel AI SDK routes and tool results, see prompt injection protection for those frameworks. An injection detector on the user prompt doesn't see a GitHub issue. A personally identifiable information (PII) detector on the model output doesn't stop a well-formed createPullRequest that pastes a private file. The OWASP GenAI LLM Top 10 2026 still lists prompt injection and excessive agency; the trifecta is how those two meet in one agent.
Don't wait for a model that "cannot be injected." Willison's June 16, 2025 essay is blunt: we still don't know how to prevent this 100% of the time. Design as if the model will obey the page.
How do MCP tool outputs fit the trifecta?
MCP doesn't create the lethal trifecta. It makes the mix easy. Each server can look harmless. The combination is what the user assembled: a server that reads private repos, a server that reads public issues, and a server that can open a pull request or fire an HTTP request.
Willison's June 16, 2025 essay uses the GitHub MCP exploit as the single-tool version of the same pattern: public issues as untrusted content, private repos as the data, and pull requests as the exfiltration channel. A mail MCP is the everyday version. The attacker emails the assistant. The assistant has the inbox (private data), the email body (untrusted content), and send or forward (external communication).
Tool outputs are a second untrusted-content source that builders miss. In an agent loop, a summary or reason field in the JSON is the next prompt. If that field contains a path, a header, or a ticket comment, the tool has become a delivery mechanism for injection. For the handler-side checklist, see how to secure an MCP server or agent tool calls. The output-side rule is the preceding structured split: never put attacker text in the fields that the model is told to trust.
An MCP gateway that sits on the bus can apply policy to the traffic that it sees. It doesn't see stdio or in-process tools that it isn't in front of, and it doesn't strip a leg unless you wired deny on egress. A gateway isn't a substitute for a check inside forwardEmail().
Where does an in-app action gate sit?
An in-app action gate sits in the tool handler, queue worker, or job, at the last reversible point before the side effect. It answers "should this action happen now?" That's how you strip the egress leg, or refuse to send untrusted content into the model, without pretending the model is trustworthy.
For more information about that job, see AI agent runtime security. Guardian Agents is the oversight category; the trifecta is why a supervisor that itself reads tool output, sees private state, and can call tools needs the same constraints. Logs after send() are the CSA verification-gap point: an agent that records the theft hasn't prevented it. Compliance evidence is for the record. It isn't the gate.
Arcjet's illustration is one late guard() on the tool, MCP handler, or job (Guards): prompt-injection and sensitive-information checks on the inbound text and on tool results, then a deny on the send. Inspection can stay local to your application. Direct guard() allows with error codes when the check doesn't finish, and that allow becomes the send unless you stop it. Vercel AI SDK and LangChain wrappers fail closed unless you opt in. Tag the run with a correlation ID so that you can reconstruct it. That identifier doesn't change allow or deny. Sequence-aware deny (step 3 because of steps 1 and 2) is the frontier; for more information, see runtime controls for enterprise systems.
Arcjet isn't a shadow-AI catalog, a toxicity monitor, or an MCP gateway. It doesn't inventory agents. It puts a deterministic stop on this send, this fetch, and this tool result.
Frequently asked questions
Can I keep all three legs if I add a stronger system prompt?
No. A system prompt is more untrusted-content competition, not a removed leg. Assume the model will obey the page at least some of the time.
Is a human approval gate stripping a leg?
Only if the human is in the path of the external action and sees enough context to refuse. Rubber-stamping every tool call is not a removed leg. See the human approval gates guide.
Does scanning for prompt injection break the trifecta?
It shrinks the untrusted-content leg. It does not remove private data or egress. Keep the scan. Still deny or hold send() after the model has read untrusted text.
Is the lethal trifecta the same as the anatomy incident?
No. Anatomy is a permitted sequence that becomes fraud. The trifecta is three capabilities that make exfiltration easy. Link them; do not merge the pages.
Does Arcjet detect the trifecta automatically?
No. You choose which tools exist in one session and where the check runs. Arcjet enforces the check you put on this action. It does not inventory the combo across the estate.
AI runtime security in your code
Protect your AI agent workflows with Arcjet
Get allow, deny, and redact on agent actions before the side effect.