OWASP LLM Top 10 in Production: How I Audited My TypeScript Agent Pipeline Against All 10 Risks — and What I Found A developer audited their TypeScript agent pipeline against the OWASP LLM Top 10 and found critical vulnerabilities, including unsanitized external tool output injected into the model context and unescaped agent output reaching the UI. The developer fixed these by adding structural validation with Zod and explicit sanitization before rendering, and documented dependency risks from the base model. I was reviewing a system prompt for an MCP agent I'd written three weeks earlier when something hit me hard: the prompt was accepting instructions from the output of an external tool. No sanitization. No validation. No limits whatsoever on what it could do with that output. The tool called a public API, got back JSON, and that JSON landed directly in the model's context. That's when I opened the OWASP LLM Top 10 https://owasp.org/www-project-top-10-for-large-language-model-applications/ and stopped reading it like a list of best practices — and started using it for what it actually is: an audit framework. My thesis is simple: most posts about the OWASP LLM Top 10 explain the ten risks to you. None of them show you how to run them against your own stack and what you actually find when you do it seriously. That's the difference between "reading the checklist" and "auditing the pipeline." This post is the second thing. Before getting into the checklist, some context: I have a TypeScript agent pipeline with three layers that interact with each other: Each layer has a different attack surface. That's exactly what the OWASP LLM Top 10 let me see with surgical precision. This was the biggest finding. My MCP agent was receiving output from external tools and injecting it directly into context with zero sanitization layer. In an adversarial scenario, any API the agent queried could return text specifically crafted to overwrite the system prompt instructions. The broken pattern looked like this: // ❌ Insecure pattern: external output goes straight into context async function fetchContextAndInject url: string : Promise