The Lethal Trifecta, Explained With Real Incidents
Private data, untrusted content, and an exfiltration channel. When an agent has all three, someone else decides what it does with your secrets. Here is the pattern, drawn from three real breaches.
In the last post I argued that an AI agent belongs in your threat model as an insider: credentialed, autonomous, and endlessly gullible. That framing raises an obvious question. If I cannot stop my agent from being socially engineered, what exactly determines whether an injection becomes a breach?
The answer has a name. Simon Willison calls it the lethal trifecta, and once you can see it, you cannot unsee it. It is the single most useful lens for reasoning about agent security, because it turns a vague dread ("agents are dangerous") into a concrete, checkable property of a specific system.
The three ingredients
An agent is exposed to catastrophic data theft when it combines all three of the following:
- Access to private data. The agent can read something an attacker wants: your emails, your source code, customer records, secrets in a vault.
- Exposure to untrusted content. The agent processes text it did not author and you did not vet: a web page, an incoming email, a pull request description, a document shared from outside, a tool result.
- An exfiltration channel. The agent can cause data to leave: an outbound HTTP request, an email it sends, a rendered image whose URL it controls, a link it emits.
Any one of these alone is harmless. Any two are survivable. All three together mean that anyone who can get text in front of your agent can read anything your agent can read. The untrusted content carries the attacker's instructions, the private data is the loot, and the exfiltration channel is the getaway car.
The critical insight, which Willison has stressed and which the research community now broadly accepts, is that this is not a vulnerability. There is no bug to patch. Each of the three capabilities is a feature that someone deliberately gave the agent because it makes the agent useful. The danger is emergent from the combination. That is exactly why the fix is architectural, not a filter.
Three incidents from the past year show the pattern operating in production systems built by companies that know what they are doing.
Incident one: EchoLeak (Microsoft 365 Copilot)
Aim Labs reported EchoLeak, tracked as CVE-2025-32711, against Microsoft 365 Copilot. It is the cleanest textbook example of the trifecta you will find.
Line up the three ingredients:
- Private data: Copilot has read access to the user's mailbox, files, and Teams messages. That is the entire point of the product.
- Untrusted content: an attacker simply sends the victim an email. They do not need the victim to open it or click anything. It sits in the mailbox as content Copilot may later retrieve.
- Exfiltration channel: Copilot could render Markdown, including images. An image URL is an outbound request the moment the client renders it.
The attack: the malicious email contained hidden instructions. Later, when the user asked Copilot an unrelated question, Copilot's retrieval pulled in the poisoned email, executed the embedded instructions, gathered sensitive data from the user's own context, and encoded it into the URL of a Markdown image pointing at the attacker's server. Rendering the response fired the request. Data gone, zero clicks.
Notice that nothing "broke." Retrieval worked. Markdown rendering worked. The model followed instructions in its context, which is what models do. The trifecta was the whole vulnerability.
Incident two: CamoLeak (GitHub Copilot Chat)
CamoLeak, a CVSS 9.6 flaw in GitHub Copilot Chat, is the same shape with a more inventive exfiltration channel, and it is worth studying precisely because the developers had already thought about exfiltration.
- Private data: Copilot Chat can read the private repositories the developer has access to, including secrets committed to them.
- Untrusted content: an attacker opens a pull request against the target repo and hides prompt-injection instructions in the PR description using GitHub's invisible-comment syntax. A developer asks Copilot to review the PR, and the hidden instructions ride in alongside the real diff.
- Exfiltration channel: here is the clever part. GitHub uses a Content Security Policy that should prevent Copilot's output from calling arbitrary external servers, and it proxies all images through its own Camo service. So the researchers built a dictionary of pre-signed Camo URLs, one per character. The injected prompt made Copilot emit a sequence of these image references encoding the stolen repository content. The victim's browser fetched them through GitHub's own trusted proxy, and the order of requests spelled out the secrets to the attacker.
CamoLeak matters because GitHub had a defense: the CSP and the image proxy existed specifically to close the naive exfiltration path that EchoLeak used. The attackers found a channel anyway, by abusing infrastructure GitHub itself trusted. That is the lesson about exfiltration channels. They are plural, they are creative, and an allowlist that stops the obvious one does not stop all of them. GitHub's eventual fix was to disable image rendering in Copilot Chat entirely, which is to say, they removed the exfiltration leg of the trifecta rather than trying to sanitize it.
Incident three: the GitHub MCP server
The agentic version predates both. In May 2025, Invariant Labs demonstrated that an agent connected to GitHub's MCP server could be turned against its own user through nothing more than a public issue.
- Private data: the agent has the user's credentials and can read their private repositories.
- Untrusted content: anyone on the internet can file an issue on a public repo. The agent reads it as part of its work.
- Exfiltration channel: the agent can open pull requests. A PR is world-readable and fully attacker-observable.
A malicious public issue instructs the agent to go read a private repository and publish its contents in a pull request on the public one. The agent, holding the user's access and unable to distinguish the attacker's instructions from its legitimate task, complies. No CVE in a single product here, because there is no single product at fault. It is the composition, the same trifecta assembled from an MCP server, a code host, and an obedient agent.
What to do with this
The reason to internalize the trifecta is that it converts security review from art into a checklist. For any agent you build or deploy, answer three questions:
- Can it reach private data?
- Can it ingest content from a source I do not control?
- Can it send data anywhere an attacker could observe?
If the honest answer to all three is yes, you have shipped the lethal trifecta, and you are one clever injection away from an incident. The remedy is to break the triangle deliberately by removing a leg:
- Cut private data access. The agent that reads the open web runs with no production credentials and no vault. It reads untrusted content and can exfiltrate, but it has nothing worth stealing.
- Cut untrusted content. If an agent must hold real credentials, tightly constrain what it ingests to sources you vet. This is the hardest leg to remove cleanly, because "read this document for me" is often the whole job.
- Cut the exfiltration channel. Strict egress allowlists, no arbitrary outbound requests, no rendering attacker-controlled URLs, human approval before anything leaves. CamoLeak is the warning that this leg is harder to sever fully than it looks, but reducing the channel count still raises the attacker's cost.
You will notice this is the same move from the insider-threat post, now with a sharper tool for deciding which leg to cut for a given agent. You cannot make the model stop believing what it reads. You can make sure that when it inevitably believes the wrong thing, the blast radius is a failed request instead of your source tree.
Prompt injection is not going away. The trifecta is how you make sure it does not matter.