---
title: "The Lethal Trifecta: A Practical Test for Prompt Injection Risk"
description: "Prompt injection cannot be filtered away. A practical 2026 guide to the lethal trifecta — private data, untrusted content, external communication — how to run it as an audit on your own agents, and which leg is realistic to cut."
author: Aleksei Aleinikov
date: 2026-08-31
lang: en
tags: [prompt-injection, lethal-trifecta, ai-agent-security, llm-security, data-exfiltration, mcp-security, owasp-llm-top-10]
canonical: https://www.alekseialeinikov.com/en/blog/topics/security/prompt-injection-defense-2026-lethal-trifecta-test
source: alekseialeinikov.com
---

# The Lethal Trifecta: A Practical Test for Prompt Injection Risk

Every few weeks another vendor announces that they have solved prompt injection. A new classifier, a stronger system prompt, a guardrail model watching the output. And every few weeks a researcher publishes a bypass, usually within days, usually embarrassingly simple.

This is not incompetence. It is the shape of the problem. An LLM receives one stream of tokens and has no reliable way to tell which of them were instructions from you and which were data from somewhere else. The OWASP GenAI project states it plainly: given the way these models work, it is unclear whether fool-proof prevention exists at all. So the useful question is not "how do I block injections" but "what happens when one succeeds" — and there is a very good test for that.

![The lethal trifecta: prompt injection becomes data theft when an AI agent has private data access, untrusted content exposure and external communication at the same time.](https://www.alekseialeinikov.com/blog/prompt-injection-2026.webp)

## Why 99% Is a Failing Grade

The comparison everyone reaches for is SQL injection, and it is exactly the wrong one — not because the mechanism differs, but because the *solution* does not transfer.

SQL injection was solved. Parameterised queries create a real boundary: the query structure travels in one channel, the values in another, and the database never confuses them. The class of bug is closed by construction.

There is no parameterised query for an LLM. Your instructions, the retrieved document, the support ticket and the web page all arrive concatenated into the same context, and the model decides what to obey based on nothing more durable than tone and plausibility. Simon Willison, who was among those who independently coined the term "prompt injection" in the summer of 2022, has made the consequence a slogan worth memorising:

> In application security, 99% is a failing grade.

A spam filter at 99% is excellent, because spammers are not adaptive in the moment. A prompt injection filter at 99% is a door that opens on the hundredth attempt, and the attacker has unlimited attempts and no deadline.

We have watched this play out. Microsoft ships classifiers for what it calls XPIA — cross/indirect prompt injection attack. The EchoLeak vulnerability in Microsoft 365 Copilot, tracked as CVE-2025-32711, got past them by the sophisticated technique of *phrasing the malicious email as if it were addressed to a human*, never mentioning AI or assistants at all.

## The Lethal Trifecta

In June 2025, Willison named the pattern that turns an annoying injection into a breach. An LLM system is dangerous when it has all three of:

1. **Access to private data** — your emails, your repositories, your customer records
2. **Exposure to untrusted content** — anything an attacker can influence
3. **The ability to communicate externally** — any channel that carries data back out

![Three legs of the lethal trifecta: private data access, untrusted content exposure, and external communication. Any two are survivable; all three together allow an attacker to exfiltrate data through a prompt injection.](https://www.alekseialeinikov.com/blog/lethal-trifecta-legs-2026.webp)

Any two of these are survivable. An agent with your private data and an outbound channel but no attacker-controlled input has nobody giving it malicious orders. An agent reading hostile web pages with no private data has nothing worth stealing. It is the third leg that closes the circuit.

If that shape feels familiar, it should. The Chromium security team has run on the **Rule of 2** since 2019: never combine untrustworthy inputs, an unsafe implementation language, and high privilege — pick at most two. The trifecta is the same engineering instinct applied to agents, which is a point in its favour. This is not a novel framework invented for AI; it is a pattern that already survived a decade of browser security.

## Run the Test on Your Own System

The value of the framing is that it converts to an audit. Three questions, in order.

**What private data can this agent reach?** Not what it is *supposed* to reach — what its credentials actually permit. The Supabase MCP incident is instructive: the agent operated with the `service_role` key, which bypasses row-level security entirely. The intended scope and the effective scope were different documents.

**Where does untrusted content enter?** This is the leg people consistently underestimate. Malicious instructions have arrived through Zendesk tickets converted into Jira issues, public GitHub issues, a PDF with white-on-white text, a web page using 1px font, code comments, and package documentation telling the agent to run an installer. If a stranger can influence any bytes the model reads, this leg is lit.

**How could data get out?** Also broader than it looks. Not just HTTP calls — a rendered Markdown image, a link the user might click, a reply posted to the public issue that started the attack, a database write, a search query sent to an attacker-controlled URL. In the Notion 3.0 report, the exfiltration channel was the agent's *own web search tool*, because it accepted URLs as well as queries.

Three yeses means you have the trifecta. Not "you might be vulnerable" — you have the precondition, and the only remaining question is whether anyone has bothered.

## One Integration Can Be All Three

The trap that catches teams is assuming the legs come from different places, so nobody owns the aggregate.

The GitHub MCP server exploit, published by Invariant Labs in May 2025, needed no combination at all. One official integration provided private repository access, exposure to attacker-authored public issues, and a publication channel through pull requests. A user asking their agent to "take a look at the issues" was enough to leak the names of their private repositories.

The same shape has since been demonstrated against the Supabase MCP and Atlassian's remote MCP server — the latter neatly summarised by Cato's researchers as "living off AI". Atlassian's own description gives it away: summarise work, create issues, perform multi-step actions. That is private data, untrusted input from public issues, and an outbound channel, in one product.

This is why per-tool authorisation matters more than per-server trust, and it is the practical case for the [user-level permission controls I wrote about for MCP gateways](https://www.alekseialeinikov.com/en/blog/topics/security/user-level-permission-controls-mcp-tool-access-gateway-2026): deny by default, allow-list individual tools, enforce at execution and not only at inference. If you are building servers rather than consuming them, the same boundary thinking applies from the [MCP server guide](https://www.alekseialeinikov.com/en/blog/topics/ai/mcp-servers-explained-build-and-run-safely-2026).

## Which Leg to Cut

You cannot remove private data access — that is usually the entire point of the agent. You cannot realistically eliminate untrusted content either; the list above shows how many doors that leg has, and Cursor's response to the Jira exploit was essentially to ask users to only connect trusted sources, which is not a control.

That leaves external communication, and it is the right answer for a reason that has nothing to do with convenience: **it is the only leg that can be enforced deterministically, outside the model, by code that cannot itself be talked into changing its mind.**

OpenAI's Lockdown Mode, now live, does exactly this — it limits outbound network requests to prevent the final stage of exfiltration. Their own documentation is refreshingly honest that it does not stop injections from occurring; it stops them from paying off.

The catch is that "restrict outbound" is much harder than it sounds, and the public record is a museum of allow-lists that failed:

| System | The allow-list | How it broke |
| --- | --- | --- |
| Microsoft 365 Copilot | CSP image sources | `*.teams.microsoft.com` hosted an open redirect |
| Salesforce AgentForce | Trusted CSP domains | An allowed domain had **expired** — researchers bought it |
| Google Antigravity | Browser domain allow-list | `webhook.site` was on the default list |
| Claude Cowork | Outbound HTTP allow-list | Anthropic's own API domain was allowed; files uploaded there with the attacker's key |

![Exfiltration allow-lists that failed in production: open redirects on trusted domains, expired domains still listed, logging services on default lists, and the vendor's own API used as the channel.](https://www.alekseialeinikov.com/blog/exfiltration-allowlist-failures-2026.webp)

Read that Claude Cowork row twice. The exfiltration channel was the vendor's own API, on the allow-list for obvious reasons, used with an attacker-supplied key. Any domain that accepts a write and lets someone else read it later is an exfiltration vector, and that category is much larger than a threat model usually admits.

## The 2026 Counter-Argument

In August 2026, Anthropic made auto mode the default in Claude Code and published numbers worth taking seriously. A third-party evaluation by Trajectory Labs ran 72 held-out indirect prompt injection scenarios; **none of 720 attack attempts succeeded** against their models in auto mode. In the same body of work, a study of 1,053 paid testers swapped one permission prompt for a clearly dangerous command: only **13.6%** of humans refused it, while auto mode would have blocked **89%**.

Both halves deserve honesty. The second is the more uncomfortable one — it is strong evidence that click-to-approve is not a real control. Confirmation fatigue is not a character flaw, it is the predictable result of asking a person to adjudicate every few seconds.

But 720 held-out scenarios is a measurement against known attack shapes, not a proof that the class is closed. Willison — who is on record predicting a serious coding-agent security incident in 2026 — has asked for independent confirmation and offered an attack he does not think auto mode addresses: a malicious package whose documentation tells the agent to fetch and run something else during a legitimate-looking test setup.

The reasonable position is that these mitigations raise the cost of attack substantially and change nothing about the architecture you should build. A lower probability of a successful injection is worth having. It is not a reason to hand an agent your production credentials.

## What to Actually Do

![Prompt injection defence checklist: cut the exfiltration leg, scope credentials to non-production, allow-list individual tools, require human approval for side effects, and design for survivable compromise.](https://www.alekseialeinikov.com/blog/prompt-injection-defence-2026.webp)

**Cut the outbound leg where you can.** Deterministic, code-enforced, not adjudicated by a model. Deny by default and audit the allow-list for open redirects, expired registrations and anything that stores data others can read.

**Scope credentials so theft is boring.** The single most effective control for coding agents: any key an agent can see belongs to a non-production account with hard spending limits. If it leaks, you rotate it and move on.

**Allow-list tools, not servers.** One integration can carry all three legs. Grant individual capabilities, and re-run the trifecta test each time you add one — the risk is in the combination, not in any single addition.

**Require human approval for side effects, not for everything.** The 13.6% number is what happens when you ask people to approve constantly. Reserve the interrupt for actions that leave the system: sending, posting, writing, publishing.

**Treat untrusted content as data.** Segregate and mark external content rather than concatenating it into the instruction stream, per the OWASP LLM01 guidance. This does not solve the problem — nothing at the prompt layer does — but it makes the boundary explicit for the humans reading the code.

**Test adversarially.** OWASP's recommendation is to treat the model as an untrusted user and pen-test the trust boundary. Assume the injection lands; verify the blast radius.

## The Bottom Line

Prompt injection is now four years old as a named problem. It is LLM01 in the OWASP Top 10 for LLM Applications, catalogued in MITRE ATLAS, and still shipping in flagship products from Microsoft, Google, Atlassian, Salesforce, Notion and Anthropic — companies with serious security organisations. That track record should end any expectation that your classifier will be the one that holds.

The lethal trifecta is valuable precisely because it stops asking that question. It does not try to make the model trustworthy. It asks what the model is *permitted* to do when it is inevitably fooled, and gives you three concrete things to look at in an architecture you already have.

Run the test on whatever agent you shipped last quarter. Private data, untrusted content, external communication. If you can say yes to all three, you do not have a hypothetical risk — you have a working exploit chain waiting for someone to care enough to use it.
