Model output is untrusted input
Anything that reaches the model can reach your component, so agent output belongs in the same trust bucket as a form field a stranger typed — and unlike a form field, there is no known general fix.
The document-extraction review gate is the cleanest example you own. A stranger uploads a document. Optical character recognition turns it into text. That text goes into a model’s context along with your instructions about what to pull out. The model returns fields, and your React components render them: the per-field confidence panel, the source image crop, the raw OCR text next to it, and the approve-or-correct control that gates everything downstream.
Now trace the trust boundary rather than the data flow. The document came from outside. The OCR text is a faithful transcription of whatever was in it, including any sentence addressed to a machine. The model has no architectural way to tell your instructions apart from that sentence — and your components render whatever comes back. There is no point in that chain where the content stops being attacker-influenced. It just gets more structured.
The definition, from the people who number these things
OWASP’s GenAI Security Project publishes a top-ten list for LLM applications. Check which edition you are reading before you quote it: the current one is the OWASP GenAI LLM Top 10 2026, published in August 2026, and eight of the ten entries moved from the 2025 list. The site’s own /llm-top-10/ page still serves the 2025 version at the time of writing, and it is the page search engines return first — so a quote you find by searching is as likely to be last year’s wording as this year’s.
Prompt injection is LLM01 in both. The 2026 definition:
“A prompt-injection vulnerability occurs when input to a large language model (LLM), whether direct user input, retrieved content, tool output, image, audio, or video content, intermediate reasoning, or persistent memory, alters the model’s behavior in ways the application developer did not intend.” (OWASP, LLM01:2026)
Read the list of sources in that sentence again, because it is doing more work than the word “prompt” suggests. Retrieved content. Tool output. Image content. Persistent memory. The interesting case is not a user typing something clever into your chat box — that is the version everyone pictures, and it is the version where the attacker and the victim are the same person. The dangerous case is what OWASP calls indirect injection: the model ingests content from an external source that contains data acting as a prompt, and, in their words, the user did not supply or see those instructions.
Your review gate is an indirect-injection surface by construction. So is any product that summarises an email, reads a pull request, searches the web, or opens a customer’s uploaded file. So is roughly every agentic surface a portfolio company will hand you.
There is no fix, and saying otherwise is the mistake
This is where a security course usually pivots to mitigations. This one has to stop and be honest first, because the honesty is load-bearing for everything downstream.
OWASP’s 2026 wording:
“Prompt injection is intrinsic to current generative AI: LLMs make no architectural distinction between instructions and data, and their behavior is stochastic, so no reliable prevention mechanism exists today.” Consequently: “Defense is therefore architectural rather than interceptive.” (OWASP, LLM01:2026)
That is a stronger claim than the same project made a year earlier. The 2025 entry said it was “unclear if there are fool-proof methods of prevention for prompt injection.” The 2026 entry says none exists. The direction of that revision is itself worth carrying into a meeting: the field did not converge on a solution during the intervening year, it converged on admitting there isn’t one.
Simon Willison named the problem in 2022 and has documented it more closely than anyone since. His argument for why mitigation-by-filtering cannot work is the one to internalise, because it is the argument you will need when somebody proposes a classifier:
“a solution that works 99% of the time is no good” (Willison, “You can’t solve AI security problems with more AI”, 17 September 2022)
The comparison he draws is with SQL injection, and it is exact enough to be useful. Parameterised queries do not filter dangerous input; they remove the ambiguity that made the input dangerous, by putting data and instructions in structurally separate places. That is a guarantee. No equivalent exists for a language model, because a model has exactly one channel and everything arrives on it. A detector that catches 99% of injections is not 99% of a fix — an attacker who can retry has a working attack, and the same post demonstrates a payload that instructs the detector to report no injection.
Where people get burned
The sentence to never say in a portfolio-company meeting is “we sanitise the prompt.” It is the sentence that makes a security engineer stop listening to you, because it describes a control class that OWASP has now formally recorded as insufficient. If someone else says it, the useful reply is not “that’s wrong” but a question: what happens when it misses one? A control that has no answer to that question is not a control, it is a hope with a budget.
The lethal trifecta, and which leg is yours
If injection cannot be prevented, the practical question becomes what an injection can reach once it lands. Willison’s framing for this has stuck across the industry because it is small enough to hold in your head during a meeting. He calls it the lethal trifecta (16 June 2025), and it is three properties that are each individually fine and jointly catastrophic:
- Access to your private data.
- Exposure to untrusted content.
- The ability to externally communicate — any channel by which data can leave.
His prescription is not to defend the combination but to avoid it: cut one leg. He is also blunt about where help is not coming from — “The LLM vendors are not going to save us!” — and about the state of guardrails: “we still don’t know how to 100% reliably prevent this from happening.”
Now map your review gate onto it. Private data: yes, it is somebody else’s document. Untrusted content: yes, by definition. The third leg is the one this whole course exists for, and it is the one people assume they do not have. A surface that only “displays” model output feels like it cannot communicate anything. It can, and the rendering lesson is about exactly how — because a browser fetches things, and a fetch is a communication channel whether or not anyone clicked.
Check your recall
Answer from memory — no scrolling back.
Retrieval check
Why is “model output is untrusted input” a claim about your component and not about the model?
Check your answer
Because the model is not where the consequence lands. The model producing an unintended string is a curiosity until something acts on it. Your component is the thing that acts: it decides whether that string becomes text, markup, a link, a fetch, or a button. Every one of those is a different amount of authority handed to whoever influenced the string.
The practical consequence is that the trust decision is made in your code, on your side of an API boundary, by someone who was thinking about layout at the time. That is not a criticism of anyone. It is a description of where the decision actually gets made, which is the thing worth knowing before you go looking for it.
Hands on
Trace the untrusted path through one of your own surfaces
Done when: FLAG-LOG.md has one row where “What I saw” names the specific external source, the specific component that renders what comes back, and which of the three trifecta legs that surface has — with any leg you cannot confirm written as unconfirmed rather than assumed absent.
- Open
learning/agent-security/FLAG-LOG.mdand pick one row. The raw OCR text panel is the sharpest starting point; the chatbot’s rendered assistant message works too. - Write the path, concretely, in the What I saw cell: what external content enters the model’s context, which file and component renders what comes back, and what that component does with it. Name real files. “Some markdown gets rendered” is not a row.
- Go through the three trifecta legs for that surface and mark each one present, absent, or unconfirmed. Unconfirmed is the honest answer for “external communication” before you have read the rendering lesson — write that rather than guessing absent.
- In Why it matters here, write one sentence connecting the path to a consequence in this product with this data. If you cannot write that sentence, the row is not ready to raise, and saying so in the cell is a legitimate entry.
- Leave Owner and Pass condition blank. They belong to the module on raising things properly, and filling them in now would be inventing an owner.
- Bring the row into the chat. I’ll push back on any cell that describes a category of risk rather than this codebase, and on any leg marked absent that is really unconfirmed.
What this does not cover
This lesson establishes that the content is untrusted and that the problem has no general fix. It says nothing about what your renderer actually does with it. That is the next thing: markdown, raw HTML, links and images each hand over a different amount of the page, and the quietest of them is an image URL the browser fetches before anyone clicks anything. The rendering lesson works through each one and the documented incident that proves it.
Further out, the action-trigger lesson takes up the harder boundary — not what the surface shows but what it can start — and the module on data in the UI turns to the other direction of leak, where nothing is being attacked at all and personal data walks out through telemetry you added on purpose.
Read this next — primary source
LLM01:2026 Prompt InjectionOWASP GenAI Security Project, OWASP GenAI LLM Top 10 2026 — free, published August 2026. The 2026 per-entry text exists only as Markdown in the project repository; the website’s /llm-top-10/ page still serves the 2025 list.
This lesson takes the definition and the one-sentence verdict on prevention. The full entry adds the eleven defence-in-depth controls OWASP actually recommends, the explicit note that no single one is sufficient, and — most useful for you — the split between direct and indirect injection with worked scenarios for each. Read it once in full so you know which of those eleven controls are somebody else’s job before you are in a room being asked which one you would like.
Stuck, curious, or think this lesson is wrong? Ask your teaching agent. The lessons are the scaffold; the conversation is where the learning gets unstuck.