Prompt Injection Isn't a Model Flaw. It's a Permission Combination You Gave Your Agent.
The flaw nobody can patch
In May 2025, researchers at Invariant Labs disclosed a prompt injection flaw in GitHub's official MCP server. The attack is almost insultingly simple: an attacker files an issue on a public repository, writing hidden instructions into the issue text. A developer then asks their AI coding agent — which also has access to their private repos — to look at that public issue. The agent reads the instructions as if they came from the user, follows them, and exfiltrates private repository data back out through the public issue thread.
Here's the part that should worry you more than the exploit itself: Invariant called this an architectural issue, not a code bug. GitHub's MCP server works exactly as designed. There is no patch, because nothing is broken. The agent did what agents do — it read content, treated instructions embedded in that content as legitimate, and acted on them with whatever permissions it had.
This is why OWASP's 2026 LLM Top 10 keeps prompt injection ranked first for a third consecutive year, with "excessive agency" climbing to third place as more systems hand models broader tool access and autonomy. If you're evaluating or building anything that lets an LLM call tools, read files, or take actions on your behalf, this is the risk that matters most — not whether the model occasionally hallucinates a fact.
The three ingredients, and why you probably already have all of them
Simon Willison named the underlying pattern the "lethal trifecta." An agent becomes exploitable when it combines three capabilities at once:
- Access to private data — your codebase, your CRM, your inbox, your internal docs.
- Exposure to untrusted content — a web page, a GitHub issue, an email, a retrieved document, the output of another tool.
- A way to communicate externally — posting a comment, sending an email, making an HTTP request, writing to a shared file.
Any one of these alone is fine. Most useful agents need all three to do their job — that's precisely what makes them useful, and precisely what makes them dangerous. A support-ticket agent that reads customer emails (untrusted content), looks up account data (private data), and replies to the customer (external channel) has the full trifecta by design. So does a research agent that browses the web, checks your internal wiki, and emails you a summary.
The mistake teams make is treating this as a model-alignment problem — assuming a smarter, better-trained model will stop "falling for" injected instructions. It won't, reliably. OWASP's own guidance is blunt about this: the stochastic nature of language models means no prompting technique or model update can guarantee the model will always distinguish your instructions from instructions smuggled in through the data it's reading. Prompt injection is not jailbreaking. You're not trying to get the model to say something forbidden — you're exploiting the fact that the model can't cleanly separate "instructions from my user" from "text that happens to appear in a document I was asked to read."
What defense actually looks like in production
Because this is an architecture problem, the fix is architectural, not a cleverer system prompt. The teams getting this right do a handful of concrete things:
Treat every piece of retrieved or tool-returned content as untrusted input, full stop. That includes RAG results, web pages, file contents, API responses, and output from other agents or MCP tools. None of it gets the trust level of a direct user instruction, no matter how it's formatted.
Never let one session hold all three legs of the trifecta. If an agent can read untrusted content, don't also give that same session unscoped write access to private systems or an unsupervised way to send data out. Split the task: one agent reads and summarizes untrusted content with no write permissions; a separate, scoped step decides what — if anything — gets acted on, ideally with a human or a rules engine in between.
Scope tool permissions to the task, not the integration. The GitHub MCP exploit is worse specifically because a single session held access to both the public repo being reviewed and the user's private repos simultaneously. Researchers' own recommended mitigation — one repository per session, least-privilege tokens — isn't a complete fix, but it shrinks the blast radius considerably. The same logic applies to any tool: an agent that only needs to read a calendar shouldn't also hold a token that can delete events.
Put a human or a hard gate in front of consequential actions. Sending money, sending external emails, deleting records, merging code, modifying permissions — these should require explicit approval, not run on an agent's "always allow" setting, which is exactly the shortcut that makes the GitHub-style exploit practical in real usage. The convenience of auto-approval is precisely what the attack is designed to exploit.
Monitor tool calls and data flows, not just final outputs. If you only check whether the agent's final response looks reasonable, you miss the exfiltration happening mid-task — a tool call that reads a private record and then immediately makes an outbound request is a pattern worth alerting on, independent of what the final message says.
Don't evaluate agent security by vibes. "We tried a few injection prompts and it didn't fall for them" tells you almost nothing about production exposure, because the attack surface is every document, email, and web page the agent might ever read — not the five test cases you thought of. If tool access is involved, adversarial testing against your actual tool permissions, not just the model, needs to be part of how you sign off on shipping it.
The takeaway
The question to ask before connecting an AI agent to anything isn't "is this model safe enough" — it's "does this specific agent session hold access to private data, exposure to content it didn't write, and a channel to send information out, all at the same time." If the answer is yes, that combination is the vulnerability, regardless of which model is running it. Scope the permissions, split the session, and gate the consequential actions — because no model update is going to do that work for you.
Have a system like this in mind?
Get a scoped plan ↗