Prompt injection gets too much attention as a model-behaviour problem and not enough as an architecture problem. A model following a hostile instruction is a security failure, but it is not automatically a breach. The breach happens when the surrounding system gives that hijacked model a path from something valuable to somewhere an attacker can receive it.
That path is the lethal trifecta: access to private data, exposure to untrusted content, and the ability to communicate externally. Simon Willison named the pattern in 2025. I use it in design reviews because it cuts through arguments about model quality and brings the conversation back to controls you can actually enforce.
Do not ask only whether an agent can be prompt-injected. Ask whether a successful injection can reach private data and carry it out.
Fig. 01 / Risk model
The lethal trifecta
All three create the path to data theft. Remove one to break the chain.
- 01
Private data
Email, source code, credentials, customer records and authenticated sessions.
- 02
Untrusted content
Web pages, inbound email, pull requests, documents and tool output.
- 03
External communication
HTTP requests, email, rendered images, form submissions and tool calls.
The three legs
1. Access to private data
This is the asset worth stealing. It may be obvious, such as customer records, email, private source code or a credentials file. It may also be ambient authority: an authenticated browser session, an OAuth token, access to a private issue tracker, or a tool that can query internal systems. The agent does not need a tool called “read secrets”. It only needs a legitimate route to information the attacker cannot reach directly.
2. Exposure to untrusted content
This is the attacker’s way into the model’s context. Inbound email, web pages, support tickets, pull-request descriptions, retrieved documents, images, tool results and MCP responses all count. Content from your own systems can still be untrusted if an external user can influence it. Provenance matters more than where the data is stored.
3. External communication
This is the route out. Sending email and making HTTP requests are obvious examples. The less obvious routes cause trouble: rendering an external image, opening an attacker-supplied link, submitting a form, writing to a public issue, calling a webhook, or passing content to another agent with broader network access. If attacker-controlled data can influence a destination or a request payload, you have an exfiltration channel.
Why the combination matters more than any single capability
Each leg is normal on its own. Agents need private context to be useful. They need to read the outside world to do current work. They need tools to act. The danger appears when one model session can connect all three without a deterministic boundary.
- 01
Attacker plants an instruction in content the agent will read
- 02
The model treats that data as an instruction
- 03
The agent retrieves information using the victim’s authority
- 04
The information is encoded into an allowed outbound action
- 05
The attacker receives data the agent was trusted to access
This is why a stronger system prompt is not the control I would lead with. It can reduce attack success, but it leaves the route intact. If an attacker finds a wording that works, every downstream capability is still waiting. Cutting one leg changes the possible outcome even when the model fails.
Three real examples
EchoLeak: an email became a zero-click exfiltration path
EchoLeak, tracked as CVE-2025-32711, affected Microsoft 365 Copilot. The attack started with a crafted inbound email. Hidden instructions influenced Copilot when it later processed the user’s Microsoft 365 context. The chain then used rendered content and an allowed Microsoft Teams-related path to cause sensitive information to leave in an outbound request. Microsoft rated the issue critical and fixed it server-side.
- Private data: information Copilot could retrieve through the victim’s Microsoft 365 permissions.
- Untrusted content: an attacker-controlled inbound email.
- External communication: a rendered-image request routed through an allowed external path.
My takeaway is not that email summarisation is inherently unsafe. It is that retrieval permissions and rendering behaviour formed one continuous security boundary. The attack did not need malware on the endpoint. It used the assistant’s intended access and the client’s intended networking.
GitLab Duo: a public merge request reached private source code
Legit Security demonstrated that instructions hidden in GitLab project content could influence GitLab Duo. In the end-to-end example, a poisoned public merge request told Duo to retrieve changes from a private project, encode them, and place the result inside an image URL. The browser attempted to load the image, sending the encoded source code in the request. GitLab remediated both the prompt-injection and unsafe-rendering paths.
- Private data: source code and confidential issues visible to the victim.
- Untrusted content: merge-request text, comments, commit messages or source controlled by another contributor.
- External communication: HTML and image rendering that generated a request to an external domain.
This example matters for every coding agent. Repository content is not passive. README files, issues, diffs, test output and dependency metadata all enter the reasoning loop. If the same agent can read private repositories and produce network-capable output, a normal code-review workflow can contain the full trifecta.
Perplexity Comet: the browser erased origin boundaries
Brave’s researchers placed malicious instructions in a Reddit comment and asked Perplexity Comet to summarise the page. The injected steps drove the browser agent across domains: it read the user’s Perplexity account details, accessed Gmail to obtain a one-time password, then posted the information back through Reddit. The user’s ordinary “summarise this page” request became account takeover material.
- Private data: authenticated account details and an OTP in Gmail.
- Untrusted content: a social-media comment controlled by an attacker.
- External communication: cross-site navigation and posting a reply to the attacker-visible page.
Traditional same-origin controls did not save the user because the agent itself was the cross-origin actor. It held the user’s authority in every tab. This is the central browser-agent risk: one reasoning context can join data that the browser previously kept in separate security origins.
The control objective: break the chain
The most useful control decision is also the bluntest. Remove one leg wherever the product can still do its job. Meta later formalised this as the Agents Rule of Two: an autonomous session should satisfy no more than two of untrusted input, sensitive access, and external communication. If all three are necessary, add reliable supervision before the consequential step.
Option A: remove private-data access
- Run browser and research agents in an isolated profile with no existing cookies, saved credentials or internal network access.
- Issue task-scoped, short-lived tokens instead of giving an agent the user’s full standing permissions.
- Put public-repository review in a separate environment from private repositories and CI secrets.
- Filter retrieval by tenant, record sensitivity and the user’s explicit task before content reaches the model.
Option B: remove untrusted content
- Separate trusted workflows from queues that accept public email, tickets, documents or pull requests.
- Track content provenance and do not promote external text into a privileged session merely because it was copied into an internal system.
- Use a tool-less quarantine model to process raw external content and return a narrow typed structure, not free text that the privileged layer interprets as a command.
- Fix the privileged action plan from trusted user intent before any untrusted content is read.
Option C: remove uncontrolled external communication
- Block automatic loading of model-generated images and other active content.
- Allowlist destinations in code. Do not let model output choose a domain, recipient, repository or webhook.
- Inspect outbound payloads for sensitive data and enforce size, type and destination policy at the egress boundary.
- Require approval for sending, publishing, paying, deleting or changing access. Show the exact destination and payload, not a model-written summary.
What I would ask in a design review
Start with a data-flow diagram, not a model card. For each agent session, write down what can enter, what private resources can be read, and every way bytes can leave. Include browser rendering, DNS, logs, links, notifications, generated files and downstream agents. Teams routinely find the third leg hiding in a feature nobody considered a communications channel.
- Which inputs can an external party influence, directly or indirectly?
- What is the most sensitive data this exact session can retrieve?
- Can any untrusted value select an action, destination, tool argument or recipient?
- What outbound requests happen automatically when model output is rendered?
- Which policy still holds if the model follows the attacker perfectly?
- Can we prove one leg is absent, or are we only assuming the model will refuse?
Human approval is a boundary only when it is specific
An approval dialog can break the external-communication leg, but only if it makes the real operation visible. “The agent wants to complete your task” is approval theatre. “Send 18 KB containing customer records to files.example.net” gives the reviewer something concrete to reject. Put the gate outside the model’s control, show the verbatim action, and make repeated prompts exceptional rather than normal.
Test the architecture, not only the prompt
Red-team tests should assume the injection succeeds. Plant instructions in every channel the agent reads, then test whether the compromised session can obtain a marked secret and move it to a controlled sink. Measure attack success, but also record which deterministic layer stopped the chain. A classifier saying “malicious” is useful evidence. A network policy refusing the request is a security boundary.
The bottom line
The lethal trifecta is important because it gives you a practical answer to an unsolved model problem. You do not need perfect prompt-injection detection to prevent data theft. You need to stop untrusted content, private authority and outbound communication from meeting inside one uncontrolled reasoning loop.
Assume the model can be hijacked. Build the surrounding system so hijacking cannot complete the journey from attacker input to private data to attacker-controlled output.
