# Patch Analysis: LiteLLM combined-view token lookup SQL injection

## What the fix changes

Upstream commit `4dc416ee749122ca91e3bca095217478663419e7` (`fix(proxy): use parameterized query for combined_view token lookup`) changes `litellm/proxy/utils.py` in the `PrismaClient` database helper path used for virtual-key authentication.

The vulnerable `combined_view` lookup built a raw SQL string with the token value interpolated directly into the predicate:

```python
WHERE v.token = '{token}'
```

The fix changes this predicate to a positional parameter and forwards the hashed token as a query argument:

```python
WHERE v.token = $1
response = await self._query_first_with_cached_plan_fallback(sql_query, hashed_token)
```

The helper `_query_first_with_cached_plan_fallback()` was also changed from accepting only `sql_query: str` and calling `self.db.query_first(query=sql_query)` to accepting `*args` and calling `self.db.query_first(sql_query, *args)`, including in the cached-plan retry path.

## Fix assumptions

The patch assumes the SQL injection root cause is the direct string interpolation of the attacker-controlled token in the single `PrismaClient.get_data(table_name="combined_view", query_type="find_unique")` sink. It does not attempt to enumerate all HTTP endpoints or all auth header names. Instead, it fixes the shared database sink that all normal LiteLLM API-key authentication flows use after they resolve a submitted key value.

This is a strong assumption for the parent vulnerability because the network entry points (`/model/info`, `/v1/chat/completions`, spend/admin endpoints, and other protected routes) call `user_api_key_auth`, which ultimately calls `_fetch_key_object_from_db_with_reconnect()` in `litellm/proxy/auth/auth_checks.py`, then `prisma_client.get_data(..., table_name="combined_view")`.

## Code paths and inputs covered

The fix covers any attacker-controlled string that reaches the combined-view token lookup as `hashed_token`, including at least these input sources inspected/tested in this stage:

- `Authorization: Bearer <token>` (`SpecialHeaders.openai_authorization`) — parent reproduction.
- `API-Key: <token>` (`SpecialHeaders.azure_authorization`) — tested alternate header source.
- `x-litellm-api-key: <token>` (`SpecialHeaders.custom_litellm_api_key`) — tested alternate header source.
- Other provider-compatible API-key headers parsed by `get_api_key()` before the same DB lookup (e.g. `x-api-key`, Google/Anthropic-style header names, and Azure APIM subscription-key header) appear to share the same sink and should receive the same protection, although only two materially distinct alternate headers were runtime-tested.

## Code paths and inputs not covered by this specific patch

The patch does not parameterize unrelated raw SQL queries elsewhere in the proxy. During source inspection, additional raw SQL call sites were noted, but they are not the same unauthenticated virtual-key lookup sink. For example:

- `litellm/proxy/utils.py::check_view_exists()` interpolates `DATABASE_SCHEMA` and a constant list of expected view names into a startup/admin database-maintenance query. `DATABASE_SCHEMA` is environment/configuration, not an unauthenticated network input.
- `litellm/proxy/spend_tracking/spend_management_endpoints.py::ui_view_spend_logs()` builds a raw `ORDER BY` clause from `sort_by`, but the package tested (`litellm==1.83.7`) validates `sort_by` against a fixed allow-list and validates `sort_order` against `asc|desc` before constructing the SQL. This appears to block a distinct authenticated/admin raw-SQL candidate and is not a bypass of the combined-view token fix.
- Raw spend/user-agent analytics queries interpolate constants and dynamically generated positional placeholders, with user-supplied values passed as query arguments in the inspected paths.

## Target security/threat-model scope

`security.md` classifies in-scope vulnerabilities as application-level attacks, including:

- **P1: Unauthenticated Proxy Access** — unauthenticated access to protected proxy data such as API keys.
- **P2: Authenticated Malicious Actions** — authenticated privilege escalation or unauthorized data access.

The parent issue and this variant search fall within P1/P2 scope because the malicious input is delivered over the network to a protected LiteLLM proxy endpoint. The security policy excludes setup misconfiguration such as omitting a `master_key`; this analysis uses a configured `LITELLM_MASTER_KEY` and therefore does not rely on that excluded condition.

## Behavior before and after the fix

Runtime testing of alternate header sources showed:

- Vulnerable `litellm==1.83.6` accepted SQL payloads supplied through both `API-Key` and `x-litellm-api-key` to `GET /model/info`; both produced HTTP 200 protected model information and delayed for approximately two seconds due to injected `pg_sleep(2)`.
- Fixed `litellm==1.83.7` rejected the same header payloads quickly with HTTP 401 and PostgreSQL logs showed `WHERE v.token = $1` with the payload only in bound parameters.

Therefore, the alternate headers are real alternate triggers on the vulnerable version but not fixed-version bypasses. The fix appears complete for the shared combined-view token lookup sink.
