---
title: "OWASP LLM Top 10 (2026) mapping"
url: "/docs/proxy/security_owasp_llm_top10"
canonical_url: "https://docs.litellm.ai/docs/proxy/security_owasp_llm_top10"
type: "docs"
last_updated: "2026-10-03"
summary: "This page maps every item of the OWASP Top 10 for LLM Applications 2026 to the LiteLLM gateway controls that address it, says how each control is turned on, and states where each one stops. It is written for a security reviewer filling in..."
related:
  - "/docs/proxy/security_best_practices"
  - "/docs/proxy/rust_gateway"
---
# OWASP LLM Top 10 (2026) mapping

> Index of all LiteLLM docs: https://docs.litellm.ai/llms.txt


This page maps every item of the [OWASP Top 10 for LLM Applications 2026](https://genai.owasp.org/resource/owasp-genai-llm-top-10-2026/) to the LiteLLM gateway controls that address it, says how each control is turned on, and states where each one stops. It is written for a security reviewer filling in a questionnaire and for the platform team deciding which controls to enable. Every claim on it was checked against the LiteLLM `main` branch at version `1.104.0` (September 2026); the last section says how to re-check it against the version you run

LiteLLM sits between your callers and the model providers, so its controls cover what the gateway can see: the request, the response, the tools and MCP servers it brokers, the files and vector stores it manages, and its own configuration, container image, and dependencies. What a provider's model does internally, and what your application does with a response after the gateway returns it, are out of its reach. Every item below therefore keeps a share that stays with the application or the provider, and each section names it

Each section has three parts: what ships, how to turn it on, and known limits. Nearly every control is off until you configure it, and the defaults quoted here are the code defaults on the version above. The table is the short version

| Item | Main gateway controls | Default state |
|------|----------------------|---------------|
| [LLM01 Prompt Injection](#llm01-prompt-injection) | Input, tool, and MCP guardrails | Off |
| [LLM02 Sensitive Information Disclosure](#llm02-sensitive-information-disclosure) | PII masking, secret detection, log redaction, encryption at rest | Off, except key hashing |
| [LLM03 Excessive Agency](#llm03-excessive-agency) | MCP, A2A, and tool permissions, route allowlists | Open until granted |
| [LLM04 Supply Chain](#llm04-supply-chain) | Signed images, frozen lockfile, scanning CI, client `api_base` block | On |
| [LLM05 Data and Model Poisoning](#llm05-data-and-model-poisoning) | Upload limits, batch guardrails, admin-only writes | Mixed |
| [LLM06 Unbounded Consumption](#llm06-unbounded-consumption) | Budgets, rate limits, admission control, size caps | Off |
| [LLM07 Misinformation](#llm07-misinformation) | Grounding checks, judge guardrail, deprecation warnings | Off |
| [LLM08 Hidden Context Exposure](#llm08-hidden-context-exposure) | Log redaction, server-side prompts, leak patterns | Off |
| [LLM09 Vector and Embedding Weaknesses](#llm09-vector-and-embedding-weaknesses) | Store access control, ingest checks | Mixed |
| [LLM10 Improper Output Handling](#llm10-improper-output-handling) | Output guardrails, stream buffering, tool permission | Off |

## LLM01 Prompt Injection

The 2026 entry covers direct and indirect injection, including payloads carried in images and audio and instructions smuggled through tool results and MCP servers

**What ships.** The [guardrails framework](./guardrails/quick_start) runs a guardrail before the provider call (`pre_call`), alongside it (`during_call`), or on the response (`post_call`). One guardrail definition covers the chat, text completion, Responses API, Anthropic Messages, Google GenAI, embeddings, image generation, speech, transcription, video, rerank, OCR, Bedrock pass-through, A2A, and MCP routes, because the framework translates each of those shapes into the text a guardrail scans. Over fifty guardrail integrations ship in [the guardrail hooks directory](https://github.com/BerriAI/litellm/tree/main/litellm/proxy/guardrails/guardrail_hooks), from the [LiteLLM Content Filter](./guardrails/litellm_content_filter), whose [prebuilt patterns](./guardrails/litellm_content_filter#available-patterns) include `prompt_injection_jailbreak`, `prompt_injection_system_prompt`, `prompt_injection_data_exfiltration`, `prompt_injection_malicious_code`, and `prompt_injection_sql`, to the hosted detectors from Lakera, Prompt Security, Azure, Bedrock, and others. The older [prompt injection detection callback](./guardrails/prompt_injection) adds a heuristic and an LLM check. System and tool messages are scanned along with user turns unless you set the [skip flags](./guardrails/quick_start#skip-system-messages-in-guardrail-evaluation). On the MCP side, [MCP guardrails](../mcp_guardrail) run on every tool call, [tool filtering](../mcp_control#allowdisallow-mcp-tools) limits which tools a key can reach, and the [tool permission guardrail](./guardrails/tool_permission) and [tool policies](./tool_policies) decide which function calls the model may make. For voice, [realtime guardrails](./guardrails/realtime_guardrails) scan the input transcription of a Realtime session. For images, the Content Filter can [describe an image with a vision model and filter the description](./guardrails/litellm_content_filter#image-content-filtering)

**How to turn it on.** Add the guardrail under `guardrails:` in `config.yaml` with `mode: pre_call` (or `during_call` when latency matters more than blocking), then either set `default_on: true` so it runs on every request or attach it to specific keys, teams, or requests. Nothing is scanned until a guardrail is defined; `default_on` is false

**Known limits.** Guardrails are classifiers, so a crafted injection can pass any of them. Treat them as one layer and pair them with the LLM03 controls, so a successful injection has little it can act on. The built-in detection callback runs only on completion, text completion, embeddings, image generation, moderation, and audio transcription calls, so `/v1/messages` and `/v1/responses` traffic skips it; use a guardrail with `mode: pre_call` for those routes. That callback also fails open on a non-HTTP error, logs the rejected input at INFO, and its vector similarity option is not implemented, so only its heuristic and LLM checks run. Cross-modal payloads are scanned only when you configure a guardrail that looks at them: the Content Filter's image description path is opt-in and costs a vision call per request, audio and file parts on chat requests and image parts on MCP calls go to the provider unscanned, and the Azure Prompt Shield integration sends the messages without attached documents. The tool descriptions an upstream MCP server returns are not scanned or pinned, so a server can change what a tool claims to do after you approved it ([PR #43283](https://github.com/BerriAI/litellm/pull/43283) is in review for this). Only the [Content Filter](./guardrails/realtime_guardrails#supported-guardrail-mode) supports the realtime input transcription mode

## LLM02 Sensitive Information Disclosure

The entry covers PII, credentials, and proprietary data leaving through prompts, responses, logs, and caches

**What ships.** [PII masking with Presidio](./guardrails/pii_masking_v2) masks or blocks entities on the request, with a [logging only](./guardrails/pii_masking_v2#logging-only) mode that masks what reaches your logging callbacks while the provider sees the original text. [Secret detection](./guardrails/secret_detection) strips API keys and tokens from the request before the provider sees them. Logging has three redaction switches: `turn_off_message_logging` ([redact messages and response content](./logging#redact-messages-response-content)), `redact_user_api_key_info` ([redacting UserAPIKeyInfo](./logging#redacting-userapikeyinfo)), and `redact_messages_in_exceptions` ([redacting messages from alerts](./alerting#redacting-messages-from-alerts)). Spend logs store no prompt or response text unless `store_prompts_in_spend_logs` is set ([config settings](./config_settings)). Virtual keys are stored as SHA-256 hashes ([virtual keys](./virtual_keys)), `/model/info` masks provider credentials ([model management](./model_management)), and `LITELLM_SALT_KEY` encrypts model `litellm_params`, stored environment variables, and MCP credentials in the database ([data at rest encryption](./security_encryption_faq#data-at-rest-encryption)). Client-facing errors run through an internal-detail scrub that removes secrets, system paths, private IPs, and tracebacks, and request validation errors (`422`) leave the submitted body out of the response ([error responses and headers](./security_best_practices#7-avoid-disclosing-internals-through-error-responses-and-headers))

**How to turn it on.** Key hashing, credential masking, and error scrubbing are always on. Everything else is opt-in: define the PII and secret guardrails, set the three redaction flags under `litellm_settings`, leave `store_prompts_in_spend_logs` unset, and set `LITELLM_SALT_KEY` before the first model is stored

**Known limits.** Secret detection is an enterprise feature and scans the request only, so a provider that echoes a secret back is not redacted on the response. All three redaction flags default to off, and with them off every logging callback receives full prompts and responses. Encryption at rest covers model `litellm_params`, environment variables, and MCP credentials; guardrail `litellm_params` (which can hold a provider API key), non-string values, and the `router_settings`, `general_settings`, and `litellm_settings` sections are stored as plaintext ([PR #43255](https://github.com/BerriAI/litellm/pull/43255) is in review to extend the coverage). The exact-match response cache is keyed on the request alone, so two keys or teams sending an identical request share one cached response unless you set `cache.namespace` in the config or `metadata.redis_namespace` on the request; only the semantic cache is scoped per tenant ([caching](./caching))

## LLM03 Excessive Agency

The entry moved up three places in 2026 on the back of agentic deployments: agents with more capability, permission, or autonomy than the task needs

**What ships.** MCP access follows a [permission hierarchy](../mcp_control#permission-hierarchy) across organization, team, and key, with `require_key_mcp_access_defined` to refuse keys that carry no MCP list and a `no-mcp-servers` opt-out. Below the server level, [tool allow and deny lists](../mcp_control#allowdisallow-mcp-tools), [per-entity tool permissions](../mcp_control#per-entity-tool-level-permissions), and [toolsets](../mcp_toolsets) narrow which tools a key or team can call, [grants](../mcp_grant_access) attach servers to keys and teams, the [client allowlist](../mcp_client_allowlist) limits which MCP client applications may connect, and [zero-trust MCP](../mcp_zero_trust) signs a JWT with the caller's identity so the MCP server can enforce its own policy. For function tools the model asks your application to run, the [tool permission guardrail](./guardrails/tool_permission) and [tool policies](./tool_policies) allow or deny by tool name and argument. For agent-to-agent traffic, [A2A agent permissions](../a2a_agent_permissions), the [A2A kill switch](../a2a_kill_switch), and [A2A iteration budgets](../a2a_iteration_budgets) (`max_iterations` and `max_budget_per_session`) bound what an agent can do and for how long. A key's `models` and `allowed_routes` ([virtual keys](./virtual_keys)) limit which models and which proxy routes it can reach at all

**How to turn it on.** Set `mcp_servers` or an access group on the key, team, or organization, and set `require_key_mcp_access_defined: true` to close the default. Create tool policies through the management API or the Admin UI. Put `agent_permissions` on the key or team, and send `metadata.session_id` on every call so iteration budgets can count

**Known limits.** MCP and A2A access is open until some level defines a list: a key with no MCP list on it, its team, or its organization reaches every server, and A2A agents behave the same way. When the permission lookup fails because the database is unreachable, MCP falls back to the global list plus the key's own submitted servers and A2A returns unrestricted access. There is no server-side approval step per tool call; `require_approval` on Responses API MCP tools is honored by the client, so a human-in-the-loop step has to live in your agent. Tool policies see only tool calls that pass through the gateway; a tool your application runs on its own is invisible to them. A pass-through endpoint defined in YAML is unauthenticated unless its entry sets `auth: true` ([PR #43250](https://github.com/BerriAI/litellm/pull/43250) is in review to apply policy checks to entries that omit it)

## LLM04 Supply Chain

The 2026 entry adds a demand to verify that model artifacts match their claimed identity, alongside the classic dependency, image, and plugin risks

**What ships.** Every published image is signed with cosign, built from a Chainguard Wolfi base pinned by digest, and available in a non-root variant ([Docker image security](./docker_image_security), with a [pin by digest](./docker_image_security#pin-by-digest) recipe). Python dependencies install frozen from `uv.lock`, and the resolver refuses any package version published in the last three days, so a freshly compromised release cannot enter a build. In CI, [Grype](https://github.com/BerriAI/litellm/blob/main/.github/workflows/image-scan.yml) scans every Dockerfile's image and [OSV](https://github.com/BerriAI/litellm/blob/main/.github/workflows/osv-scan.yml) scans the lockfile nightly and on pull requests, every third-party GitHub Action is pinned to a commit SHA, [zizmor](https://github.com/BerriAI/litellm/blob/main/.github/workflows/zizmor.yml) lints the workflows, and [OpenSSF Scorecard](https://github.com/BerriAI/litellm/blob/main/.github/workflows/scorecard.yml) runs weekly. At runtime, request bodies that carry `api_base`, `base_url`, or similar fields are rejected so a caller cannot point the gateway at another upstream ([client-side auth](./clientside_auth#pass-user-llm-api-keys--api-base)), [model access groups](./model_access_groups) grant named sets of models instead of raw model names, and MCP servers proposed by a team stay inactive until a proxy admin approves them, with stdio servers limited to an allowlisted command (`npx`, `uvx`, `python`, `python3`, `node`, `docker`, `deno`, plus `LITELLM_MCP_STDIO_EXTRA_COMMANDS`) ([MCP server submissions](../mcp_server_submissions))

**How to turn it on.** The image, lockfile, CI, `api_base` block, and stdio allowlist controls are on with no setting. Verify signatures with the steps on the image security page and pin by digest in your deployment. Access groups need `model_info.access_groups` on the deployment and the group name on the key or team

**Known limits.** The image publish pipeline is not in the public repository, so you can verify a signature with the public key but there is no SBOM or provenance attestation shipped with the image. The default image runs as root; use the `-non_root` variant. Scans skip pull requests from forks and run on merge and nightly instead, CI jobs have no egress lockdown, and `CODEOWNERS` covers the UI, cost maps, and migrations but not the workflows, `pyproject.toml`, `uv.lock`, or the Dockerfiles. The model cost map is downloaded at startup from the GitHub `main` branch over HTTPS without a signature; its integrity check is a minimum model count and a shrink ratio, with a fallback to the bundled copy on failure. Set `LITELLM_LOCAL_MODEL_COST_MAP=True` to use the bundled copy ([disable pulling live model prices](./server_tuning#disable-pulling-live-model-prices)) or [serve your own](./custom_model_cost_map#option-2-serve-your-own-cost-map). A config file or custom callback module loaded from S3 or GCS at boot ([load config from S3 or GCS](./server_tuning#load-configyaml-from-s3-or-gcs)) is written to disk without a checksum, so bucket write access is config write access. A [custom code guardrail](./guardrails/custom_code_guardrail) runs admin-supplied Python under RestrictedPython with no CPU or wall-clock limit, and its `http_request` primitive can reach private addresses ([PR #43280](https://github.com/BerriAI/litellm/pull/43280) is in review for both). LiteLLM never downloads or runs model weights; it calls hosted or self-hosted model APIs, so verifying that a model artifact matches its claimed identity stays with whoever serves the model

## LLM05 Data and Model Poisoning

The 2026 entry widens from training data to RAG knowledge bases, agent memory, and fine-tune backdoors

**What ships.** Uploads through `/v1/files` accept `max_file_size_mb`, `max_batch_file_size_mb`, and `allowed_file_extensions` ([restrict file uploads](./security_best_practices#10-restrict-file-uploads)), and every upload rejects a filename with a path traversal or NUL byte. [Batch guardrails](./guardrails/batch_guardrails) run your pre-call guardrails over every record of a `purpose=batch` JSONL file before it reaches the provider. Inline uploads to [`/v1/rag/ingest`](../rag_ingest) are typed by magic bytes, capped at 512 MiB, and refused when they are archives or executables. Managed prompts can be created, changed, or deleted only by a proxy admin ([prompt management](./prompt_management)), and a model without a team can be registered only by a proxy admin ([model management](./model_management)). Fine-tuning jobs are an enterprise feature whose training and validation file ids are checked against the calling key's ownership when they are LiteLLM-managed ids ([fine-tuning](../fine_tuning))

**How to turn it on.** The filename check, the RAG ingest checks, and the admin-only writes are always on. The `/v1/files` limits are set under `general_settings`. Batch guardrails need nothing beyond a pre-call guardrail on the key, team, or request

**Known limits.** The `/v1/files` size and extension limits are all unset by default. The RAG ingest malware scanner is a placeholder that recognizes only the EICAR test string, with no configuration hook for a real engine, and uploads referenced by `file_url` or `file_id`, or attached to a vector store through `/v1/vector_stores/{id}/files`, skip the ingest checks ([PR #43244](https://github.com/BerriAI/litellm/pull/43244) is in review for a configurable scanner and upload controls on every vector store upload route). Fine-tuning files are not scanned and any key can start a job once the enterprise gate is passed. On enterprise, a team admin can register a team-scoped model with its own `api_base` and pricing. Batch guardrails run pre-call guardrails only, not post-call or deployment-level ones ([limits](./guardrails/batch_guardrails#limits)). Poisoning inside a provider's training data, a third-party model, or a knowledge base you fill outside the gateway is out of its view

## LLM06 Unbounded Consumption

The entry moved up four places in 2026 and now names denial of wallet explicitly

**What ships.** Budgets apply at every level: the whole proxy, organizations, teams, team members, internal users, virtual keys, end users, tags, and model access groups ([set budgets](./users#set-budgets), [team budgets](./team_budgets), [tag budgets](./tag_budgets), [access group budgets](./model_access_group_budgets)). Requests per minute, tokens per minute, and parallel request limits apply per key, team, user, model, and tag, with counters shared across instances through Redis ([set rate limits](./users#set-rate-limits), [multi-instance rate limiting](./users#multi-instance-rate-limiting)). When a counter cannot be verified, `fail_closed_budget_enforcement` ([hard budget enforcement](./users#hard-budget-enforcement-fail-closed)) and `fail_closed_rate_limit_enforcement` ([hard rate limit enforcement](./users#hard-rate-limit-enforcement-fail-closed)) reject with `503` instead of admitting. [Per-worker admission control](./server_tuning#per-worker-admission-control) caps in-flight requests with a bounded queue, `max_request_size_mb` and `max_response_size_mb` cap body sizes, [upperbound key params](./virtual_keys#upperbound-keygenerate-params) put a ceiling on what `/key/generate` may ask for, pass-through routes time out after 600 seconds by default ([request timeouts](./pass_through#request-timeouts)), and `litellm_settings.block_requests_for_models_without_pricing: true` refuses a model the cost map cannot price with a `403` of type `model_cost_map_missing`, so no spend goes unmetered

**How to turn it on.** Set `max_budget` and the `rpm_limit`, `tpm_limit`, and `max_parallel_requests` fields on the key, team, or user; set the two fail-closed flags, `max_in_flight_requests_per_worker`, and the size caps under `general_settings`; set `upperbound_key_generate_params` under `litellm_settings`

**Known limits.** Every limit above is unset by default; the pass-through timeout is the only one that is on. A proxy admin key passes authentication before the budget and rate limit checks run, so it is exempt from both. A team-scoped key skips the personal user budget unless `general_settings.apply_user_budget_to_team_keys: true`. Budgets are checked on LLM API routes only, not on management or utility routes. The global `max_budget` needs a database and is not enforced without one. With the fail-closed flags off, a Redis outage degrades to per-instance counters; with `fail_closed_budget_enforcement` on, tag budgets still admit because their counters live in Redis only, and access group budgets are a plain read with no reservation. The body size caps are enterprise-only and are logged and skipped otherwise. There is no per-key ceiling on `max_tokens`, so output volume is bounded by tokens per minute and budget rather than per request

## LLM07 Misinformation

The entry moved up two places on incident data, with the new emphasis on a wrong answer that drives a tool call or a decision

**What ships.** Three guardrails check a response against source material: [Bedrock Guardrails contextual grounding](./guardrails/bedrock#contextual-grounding) with `contextual_grounding_from_messages`, the [Qualifire hallucination and grounding checks](./guardrails/qualifire#available-checks), and [XecGuard context grounding](./guardrails/xecguard#context-grounding-rag), which reads the documents from `metadata.xecguard_grounding_documents`. The [LLM-as-a-judge guardrail](./guardrails/llm_as_a_judge) has a second model score the response against your own rubric and blocks below `overall_threshold` (80 by default). For models that are about to be retired, `GET /model/deprecations` lists deployments whose provider deprecation date falls within `warn_within_days` (30 by default), and the Slack `model_deprecation_warnings` alert fires once alerting is configured

**How to turn it on.** Define the grounding or judge guardrail with `mode: post_call`, attach it to the keys or teams that need it, and pass the source documents where the provider expects them. Deprecation warnings need only the alerting setup

**Known limits.** Every check here is opt-in, adds a provider or model call to the request, and is probabilistic. A grounding check needs the source documents on the request or in metadata, so a RAG flow that retrieves outside LiteLLM has to pass them along. Nothing verifies a factual claim without a source document, and a wrong answer that turns into a tool call is checked against tool policy (LLM03), not against truth. The gateway cannot tell an overconfident answer from a correct one on its own

## LLM08 Hidden Context Exposure

Renamed from System Prompt Leakage in 2026 and widened to developer instructions, RAG policy text, user profiles, and tool schemas, with the advice to assume all of it can be disclosed

**What ships.** Managed prompts keep the developer message server-side so callers send a `prompt_id` and variables instead of the prompt itself ([prompt management](./litellm_prompt_management)), and only a proxy admin can write them. The [Content Filter](./guardrails/litellm_content_filter) `prompt_injection_system_prompt` pattern blocks requests that ask for the system prompt, and the Bedrock [`PROMPT_LEAKAGE` check](./guardrails/bedrock#resource-less-checks-invokeguardrailchecks) does the same through Bedrock. The LLM02 redaction switches keep prompts out of logs, alerts, and spend logs, and [logging only PII masking](./guardrails/pii_masking_v2#logging-only) masks what your logging callbacks receive. Client headers are not forwarded to providers unless [forward client headers](./forward_client_headers) is configured, internal `_hidden_params` are stripped from responses, and error messages run through the internal-detail scrub described under LLM02

**How to turn it on.** Move secrets and policy text out of prompts first; nothing on this page makes a prompt unreadable to the model that receives it. Then define the Content Filter or Bedrock guardrail with `mode: pre_call` and set the redaction flags under `litellm_settings`

**Known limits.** A model can be talked into repeating its context, and the gateway's checks catch the direct asks by pattern; the Bedrock check runs on the request side only. Redaction is off by default, and the Responses API `instructions` and `tools` fields are not covered by message redaction, so they reach callbacks in full even with `turn_off_message_logging` set. The prompt list and info endpoints honor a `prompts` allowlist in the key's metadata, but resolving a `prompt_id` at request time has no access check, so any key that can call the model can use any prompt id it knows. The `x-litellm-model-api-base` response header always names the upstream host (the query string is removed), and a public provider hostname can appear in the `API Base:` line of a client-facing error. Any authenticated key can list the configured guardrails' names and masked parameters through the v1 `/guardrails/list` route

## LLM09 Vector and Embedding Weaknesses

The entry covers poisoned chunks, weak access control on the store, and embedding inversion

**What ships.** Keys and teams can be [restricted to specific vector stores](../vector_stores/managed_vector_stores#restricting-keys-and-teams-to-specific-stores) through `object_permission.vector_stores`, stores can be scoped to a team, and `disable_vector_stores_for_internal_users` keeps internal users out entirely. The ingest checks under LLM05 apply to inline uploads. The vector store page describes [retrieved context going through your pre-call guardrails](../vector_stores/managed_vector_stores#retrieved-context-goes-through-your-pre-call-guardrails), which is the intended place to catch a poisoned chunk before the model sees it

**How to turn it on.** Set `object_permission.vector_stores` on the key or team, set `team_id` on the store, and set `disable_vector_stores_for_internal_users: true` under `general_settings` when internal users should not query stores

**Known limits.** Access is per store, not per document. An empty `vector_stores` list means every store is allowed, and a store with no `team_id` is usable by any key that can use vector stores at all. At version `1.104.0` the retrieved chunks are added to the request after the pre-call guardrails have already run, so the scan the vector store page describes does not happen until [PR #43271](https://github.com/BerriAI/litellm/pull/43271) lands; until then a `post_call` guardrail sees the model's use of the chunks, not the chunks themselves. The malware scanner on ingest is the placeholder described under LLM05. Embedding inversion and the store's own encryption and access controls belong to the vector database; the gateway holds its credentials and forwards queries

## LLM10 Improper Output Handling

The entry dropped five places in 2026 but still covers unvalidated output reaching browsers, databases, shells, and APIs, and insecure generated code

**What ships.** Any guardrail with `mode: post_call` inspects or rewrites the response before the client sees it, and the [tool permission guardrail](./guardrails/tool_permission) checks the tool calls a response asks for. On streams, `streaming_buffer_until_moderated`, `streaming_sampling_rate`, `streaming_end_of_stream_only`, and `streaming_transform_mode` decide whether the stream is held until it passes or sampled as it flows ([streaming and post_call guardrails](./guardrails/custom_guardrail)), with [Bedrock](./guardrails/bedrock#streaming) and the [Content Filter](./guardrails/litellm_content_filter#streaming-support) documenting their own stream behavior. [Output parsing](./guardrails/pii_masking_v2#output-parsing) in the PII guardrail masks entities on the response. The Admin UI renders model output as Markdown without raw HTML

**How to turn it on.** Define the guardrail with `mode: post_call`, attach it, and set `streaming_buffer_until_moderated: true` on it when a block must land before any chunk reaches the client

**Known limits.** Every output guardrail is opt-in. On a stream, a `post_call` guardrail that is not buffering runs on the assembled response after the chunks are already delivered, so it can audit but not block; only Bedrock, Straiker, and Rubrik buffer by default, the rest sample every fifth chunk, and the OpenAI moderation guardrail samples rather than buffers. PII output parsing does not cover raw byte streams from providers other than Anthropic or `/v1/responses` stream events, and logs a warning when it skips them. The gateway does not validate generated code, SQL, HTML, or shell commands against what your application will do with them; treat model output as untrusted input in every consumer. The Admin UI loads remote images referenced in model output automatically ([PR #43276](https://github.com/BerriAI/litellm/pull/43276) is in review to make that click-to-load)

## Keeping this page current

The OWASP list is at [genai.owasp.org](https://genai.owasp.org/resource/owasp-genai-llm-top-10-2026/) and this page tracks its 2026 edition. The controls and limits above were checked against LiteLLM `main` at version `1.104.0`; the pull requests linked as in review change a limit when they merge, so check their state against the version you run. If a control on your version behaves differently from what this page says, open an issue on [BerriAI/litellm](https://github.com/BerriAI/litellm/issues) with the version and the section name

## Related pages

- [Security Best Practices](https://docs.litellm.ai/docs/proxy/security_best_practices.md)
- [[Beta] Rust AI Gateway](https://docs.litellm.ai/docs/proxy/rust_gateway.md)
