OWASP LLM Top 10: A Builder's Map

The OWASP LLM Top 10 (2025) maps where AI applications break. This post maps which categories static analysis can reach — and which ones it can't, and why.

Copper Sun Brass Team · · 8 min read
securityai-code-reviewengineering

The OWASP LLM Top 10 (2025) isn’t a checklist you scan your way out of. Seven of the ten categories are runtime risks — they produce no source pattern a static analyzer can flag. Knowing which three your scanner can reach, and which seven it can’t, is the difference between a security plan and a false sense of coverage.

The OWASP LLM Top 10: A Builder’s Map

BrassCoders maps the current OWASP LLM Top 10 (2025 edition, released March 12, 2025 by the OWASP GenAI Security Project) against the 12 scanners it runs at scan time, so you know exactly where your code review stops and where runtime controls must begin.

The 2025 list replaced v1.1 from 2023 and renumbered several categories. If you’ve seen references to “LLM02: Insecure Output Handling,” that was the v1.1 numbering. The current list calls the same category LLM05:2025 Improper Output Handling. The full 2025 list:

  • LLM01:2025 Prompt Injection
  • LLM02:2025 Sensitive Information Disclosure
  • LLM03:2025 Supply Chain
  • LLM04:2025 Data and Model Poisoning
  • LLM05:2025 Improper Output Handling
  • LLM06:2025 Excessive Agency
  • LLM07:2025 System Prompt Leakage
  • LLM08:2025 Vector and Embedding Weaknesses
  • LLM09:2025 Misinformation
  • LLM10:2025 Unbounded Consumption

The authoritative source is genai.owasp.org/llm-top-10/.

What Static Analysis Covers (and Why)

BrassCoders can reach any OWASP LLM Top 10 category that manifests as a source-code pattern: a detectable data flow, an unsafe API call, an unescaped interpolation. If the risk has no code signature — if it lives in a model’s training data, in runtime agent behavior, or in infrastructure configuration — no scanner sees it.

That’s not a gap to paper over. It’s the correct division of labor. A scanner that invents findings for runtime risks teaches teams to tune them out. BrassCoders reports what it can verify in the committed code; your deployment controls handle the rest.

Three categories from the 2025 list have meaningful source-code coverage. One has partial coverage at the application-code layer. Six have none.

LLM01:2025 Prompt Injection — What BrassCoders Can’t Catch

BrassCoders doesn’t flag prompt injection — and neither does any other static analyzer. Prompt injection is a runtime attack on the AI agent, not a pattern in the source code the agent produces. An attacker embeds adversarial instructions in content the agent reads — a file, a web page, a user message — and the agent follows those instructions instead of its legitimate system prompt. There is nothing in the committed codebase for a scanner to match.

The post The Attack BrassCoders Can’t Catch: Prompt Injection covers the attack surface in detail and explains where mitigations actually live: agent sandboxing, context isolation, restricted tool permissions, and egress control.

What BrassCoders does surface is the application-layer code that feeds user input into model calls without validation. That’s not a prompt injection finding, but it’s the code pattern that makes prompt injection easier to exploit. If your route handler passes request.body directly to an openai.chat.completions.create() call without any content filter or length check, that’s visible in the source — and BrassCoders’s Semgrep taint scanner will flag the input-flow path.

LLM05:2025 Improper Output Handling — What BrassCoders Can Catch

BrassCoders catches LLM05:2025 Improper Output Handling through taint analysis that follows user-controlled data from HTTP request sources to rendering sinks — the same pattern that makes AI-generated output dangerous when an application passes it to a template without escaping.

Specifically, BrassCoders ships bundled Semgrep taint rules (in mode: taint) that trace data flows through Flask, Django, and FastAPI request sources to six sink categories:

  • Python sinks: render_template_string(), Flask Markup(), Django mark_safe(), and HttpResponse(..., content_type="text/html") — all patterns where user input can reach HTML rendering without automatic escaping.
  • JavaScript/TypeScript sinks: React’s dangerouslySetInnerHTML, direct innerHTML and outerHTML assignments, document.write(), and jQuery’s .html() — all paths where server-fetched or user-supplied content reaches the DOM without sanitization.

The Pysa interprocedural taint scanner adds a second detection layer for Python codebases, tracing XSS flows across function boundaries that regex-based rules miss. The api_security_scanner adds a lightweight pattern match on dangerouslySetInnerHTML with a safe-wrapper filter — calls already wrapped in DOMPurify.sanitize() or identically named functions are excluded, which cuts false positives without widening the unchecked surface.

LLM05:2025 is exactly the category where “AI wrote this output and we rendered it” becomes a code-layer vulnerability. An AI assistant that generates an HTML snippet, which your application drops into a template string, is a taint path no different from a SQL injection. BrassCoders sees it the same way.

The Runtime Categories: Mitigations Outside the Scanner

BrassCoders has no findings to report for six of the ten 2025 categories — they carry no source-code signature a scanner can reach.

LLM04:2025 Data and Model Poisoning happens during training or fine-tuning — before any code you commit exists. The mitigation layer is data provenance, dataset validation, and supply-chain controls on model artifacts. No scanner sees training data.

LLM07:2025 System Prompt Leakage is a runtime information-exposure vulnerability. An attacker extracts the system prompt through model output; the risk lives in how the deployed system is configured and how the model responds to adversarial probing. Your scanner sees the system-prompt string in source, but it can’t determine whether the model will reveal it at runtime.

LLM08:2025 Vector and Embedding Weaknesses covers risks in retrieval-augmented generation (RAG) pipelines — index poisoning, adversarial retrieval manipulation, and embedding inversion. These are infrastructure and data-layer risks. Your vector store’s access controls and indexing pipeline are outside the scope of source-code analysis.

LLM09:2025 Misinformation is an output-quality risk. The model generates factually incorrect content; downstream users act on it. There is no code pattern that causes misinformation — it’s a property of the model’s weights and the context provided to it. Mitigations are grounding strategies, retrieval augmentation, and human review workflows.

LLM10:2025 Unbounded Consumption is a resource-governance risk — an attacker sends requests that cause the application to over-consume model tokens, API quota, or compute. The mitigation is rate limiting, token-budget enforcement, and timeout configuration in your gateway or orchestration layer. Scanners can flag missing rate-limit middleware, but they can’t enforce the limits.

LLM06:2025 Excessive Agency deserves a closer look because it touches application code. This category covers what happens when an AI agent is granted too many permissions — write access to databases, the ability to send emails, the power to execute arbitrary shell commands — and then acts on those permissions based on attacker-controlled input. The mitigation is principle of least privilege: grant agents only the specific tools they need.

BrassCoders doesn’t model agent capabilities or permission scopes. It’s not designed to. But it does catch the underlying dangerous patterns — subprocess.call() with user input in the flow, unrestricted database writes, hard-coded credentials that would expand an agent’s blast radius. Those are standard security findings that Pysa and Semgrep surface regardless of whether the code is called by a human or an agent.

A Practical Implementation Order

BrassCoders gives you the fastest starting point: surface the categories where a code fix exists first, then layer in runtime controls for the rest.

Step 1: Run BrassCoders. Surface the LLM05:2025 findings first — taint flows from HTTP input to template rendering or DOM injection. These have a concrete fix: replace render_template_string() with render_template(), remove mark_safe() on user-controlled strings, and route user-supplied content through a sanitizer before dangerouslySetInnerHTML. Each finding maps to a one-line change.

At the same time, review your LLM01:2025 exposure in the source: look for routes where request.body passes directly to model API calls without any content filter. This isn’t a prompt injection finding, but tightening those input paths reduces the attack surface.

Step 2: Address LLM06:2025 in your agent design. Audit what tools your agent can call. If it has write access to your database, email system, or file system, scope those permissions to the minimum the feature requires. This is a design decision, not a scan finding — but it’s the highest-impact control for excessive agency.

Step 3: Build runtime controls for the remaining categories. LLM10:2025 Unbounded Consumption requires a token budget and rate limiter in your gateway. LLM07:2025 System Prompt Leakage requires testing your deployed system with adversarial prompts — red-teaming, not code review. LLM08:2025 Vector and Embedding Weaknesses requires access controls on your retrieval index and provenance tracking on indexed documents.

Step 4: Treat LLM09:2025 Misinformation as a product quality risk. Grounding (retrieval-augmented generation with cited sources), confidence thresholds, and human review workflows are the tools here. No scanner contributes to this layer.

The 2025 OWASP LLM Top 10 is worth reading front to back — the mitigations section for each category is specific and actionable. The OWASP GenAI Security Project publishes the full list with guidance at genai.owasp.org/llm-top-10/.


Install BrassCoders and run it against your LLM application codebase to surface the categories where a code fix exists:

pip install brasscoders
brasscoders scan /path/to/your/project

The scan emits a YAML report your AI assistant can triage directly. LLM05:2025 findings include the taint path — source, sink, and the specific interpolation or render call — so the fix is clear before you open the file. The BrassCoders Paid plan ($12/month per developer) adds an AI-powered enrichment pass that reduces raw scan noise to the findings that need attention. Details at coppersun.dev/pricing.

Frequently Asked Questions

What is the OWASP LLM Top 10?

The OWASP LLM Top 10 is a community-maintained list of the ten highest-risk vulnerability categories in large language model applications. The current version is the 2025 edition (released March 12, 2025), which replaced v1.1 from 2023. It covers categories from prompt injection (LLM01:2025) through unbounded consumption (LLM10:2025). The authoritative source is genai.owasp.org/llm-top-10/.

Does BrassCoders implement OWASP LLM Top 10?

Partly. BrassCoders can detect the source-code patterns that underlie several OWASP LLM Top 10 categories — specifically LLM05:2025 Improper Output Handling, where user-controlled data flows into template rendering or DOM injection without escaping. Runtime categories — prompt injection, data poisoning, system prompt leakage, and unbounded consumption — are deployment-time risks with no source-code signature for a scanner to match.

Which OWASP LLM categories does static analysis cover?

Static analysis most directly addresses LLM05:2025 Improper Output Handling (XSS through template rendering and DOM injection), plus the underlying input-flow patterns relevant to LLM01:2025 (where application code fails to validate what it passes as model input). SQL injection, command injection, SSRF, and path traversal — all of which can be worsened when AI generates untrusted content — are covered by BrassCoders's Semgrep taint rules and Pysa interprocedural taint scanner.

Which OWASP LLM categories are outside static analysis reach?

LLM01:2025 Prompt Injection (runtime attack on the agent), LLM04:2025 Data and Model Poisoning (training-layer risk), LLM07:2025 System Prompt Leakage (runtime information exposure), LLM08:2025 Vector and Embedding Weaknesses (infrastructure-layer risk), LLM09:2025 Misinformation (output quality, not a code pattern), and LLM10:2025 Unbounded Consumption (rate-limiting and resource governance). None of these produce a detectable source pattern that a scanner can flag.

Where do I start if I want to implement OWASP LLM Top 10?

Start with the categories that static analysis reaches, because those are the ones where a code fix exists: run BrassCoders against your codebase to surface XSS/template-injection and taint-flow findings that map to LLM05:2025, then tackle the runtime categories in the deployment layer — sandboxing, rate limits, output validation middleware, and agent permissioning. The OWASP GenAI Security Project at genai.owasp.org publishes mitigations for each category.