The short answer
The lethal trifecta is Simon Willison’s name for three capabilities that, together, make an AI agent leak data: access to your private data, exposure to untrusted content, and the ability to externally communicate.1 If one agent has all three, someone who controls the untrusted content can instruct it to read the private data and send it out. Willison’s advice is blunt: the only way to stay safe is to avoid the combination entirely.1
You don’t fix it by finding a smarter filter. You fix it by making sure no single agent session holds all three legs at once.
Deedglass
A tool call meets your rules before it runs
Asks to run
DELETE FROM visits;
Deletes every row in visits
Deedglass checks your rules
- Reading dataAllowed
- Deleting a whole table (DROP, TRUNCATE)Never
- Changing or deleting rows without saying which (WHERE)This one
- Other changesWait for a person
The three legs
| Leg | What counts | Examples |
|---|---|---|
| Private data | Anything you wouldn’t post publicly | A database, private repos, email, files, secrets in .env |
| Untrusted content | Text written by someone other than you, that the model reads | Web pages, issues and pull requests on public repos, support tickets, emails, documents, tool results |
| External communication | Any way for data to leave | Sending email, posting a comment, opening a pull request, fetching a URL (the URL itself carries data), writing to a shared ticket |
Two details trip people up.
Untrusted content includes tool output. A model doesn’t separate “instructions from the user” from “text a tool returned.” A ticket body, a code comment or a web page can carry instructions, and the model may follow them. That’s prompt injection.
External communication is wider than “send email.” Loading an image from an attacker’s URL, calling a webhook, opening a public pull request or adding a reply to a ticket the attacker can read all move data out. If the agent can make any request whose content the attacker later sees, that’s the third leg.
Willison also calls out confident guardrail claims of catching “95% of attacks,” noting that in web application security 95% is a failing grade.1 An attacker needs one success.
Two public cases, both had all three
GitHub MCP, May 2025. Invariant Labs showed a malicious issue in a public repository hijacking an agent connected to the GitHub MCP server when the user asked it to look at open issues. The agent pulled data from the user’s private repositories into a pull request on the public repository. The authors described it as an architectural issue at the agent-system level, not a flaw in the server’s code.3 Private data: private repos. Untrusted content: the public issue. External communication: the public pull request.
Supabase MCP, July 2025. In a General Analysis demonstration with dummy data, a support ticket carrying instructions led an assistant using the Supabase MCP server with a service_role credential to read an integration_tokens table and post its contents back into the same ticket, where the attacker could see it.4 Private data: the tokens table. Untrusted content: the ticket. External communication: the ticket reply. Our write-up of the Supabase demo goes into the primary sources.
Notice that neither case needed a bug in the tools. Each tool did what it was built to do.
Map your own agent
List every tool an agent session can use, and mark which legs it brings. One tool can bring more than one.
| Tool | Private data | Untrusted content | Sends out |
|---|---|---|---|
| Postgres MCP server (production) | Yes | Rows written by users | Only if it can write somewhere the attacker reads |
| GitHub MCP server (all repos) | Private repos | Public issues and PRs | Comments, PRs, issues |
| Web fetch | Every page it loads | The URL it requests | |
| Email (read and send) | The inbox | Every incoming message | Sending |
| Shell | Files, .env |
Output of anything it runs | curl, git push |
| Slack or ticket tool | Private channels | Messages from others | Posting |
If any column is empty for the whole session, you’ve broken the trifecta for that session. If all three columns have a “yes,” you have work to do. GitHub alone covers all three when it can reach both public and private repos. A shell does too.
How to remove a leg
Pick whichever leg is cheapest to remove for the task.
- Remove private data. Run the agent against a development database, a branch, or a token scoped to public repos only. Invariant’s own advice for the GitHub case was to limit an agent to one repository per session.3
- Remove untrusted content. Don’t let the session read tickets, issues, email or web pages written by others. Hard for support and coding agents, whose job is reading such things, so it’s often not the cheapest leg.
- Remove external communication. Take away send, post, comment and fetch tools in that session, or limit them to destinations nobody else reads. If the agent must open a pull request, make it a draft on a private fork that a person promotes.
- Split the work across sessions. One session reads untrusted tickets and writes a summary with no data access. A second session with data access reads only the summary, written in a fixed format, and acts. Neither holds all three.
- Put a person on the leg that’s left. If all three are needed, require approval for each outbound action, with the full content shown. A human-in-the-loop approval on the send step is the check that holds when the model has been steered.
Meta’s Rule of Two
In October 2025 Meta published the “Agents Rule of Two,” inspired by a Chromium policy and by Willison’s trifecta. Its three properties are slightly different: an agent can process untrustworthy inputs, can access sensitive systems or private data, and can change state or communicate externally. Agents must satisfy no more than two of them in a session; if all three are needed, the agent shouldn’t run autonomously and at minimum needs supervision, such as human-in-the-loop approval.2
The difference worth noting: Meta’s third property includes changing state, not just communicating. An agent that reads an untrusted ticket and can DELETE rows in production is dangerous even if nothing leaves the building. For agents with write access, use Meta’s wording.
What the trifecta doesn’t cover
- Damage without exfiltration. Deleting data, sending money or changing permissions needs no outbound channel. The trifecta is about leaks; the blast radius of writes is a separate question.
- Mistakes with no attacker. An agent can run the wrong migration on its own. No untrusted content required.
- Tool descriptions themselves. An MCP server’s tool descriptions are text the model reads too; a hostile server brings untrusted content by itself. MCP server security basics covers that side.
To see which agents on your machine combine data access with write or send tools, paste an MCP config into the check page or run npx deedglass scan (how the grade works); the scan Action repeats it on pull requests. To try the checks in your browser without installing anything, open the playground.
Questions people ask
Who coined the lethal trifecta?
Simon Willison, in a post dated 16 June 2025.1
Can a guardrail model stop the lethal trifecta?
Willison’s view is that filters claiming to catch most attacks aren’t enough, and that the safe option is to avoid the combination.1
What is the difference between the lethal trifecta and the Rule of Two?
The Rule of Two, from Meta, counts changing state as well as communicating externally, and says an agent should hold no more than two of its three properties per session.2
Written with AI assistance and checked against our claims register.