Commit graph

38301 commits

Author SHA1 Message Date
yuneng-jiang
37845acb57
fix(tests): migrate realtime + rerank tests off shut-down upstream models (#28191)
* fix(tests): use gpt-realtime in realtime guardrails test

OpenAI shut down gpt-4o-realtime-preview-2024-12-17 on 2026-05-07, so
the live OpenAI realtime guardrails integration test now fails with
model_not_found (session.created never arrives, _wait_for_event times
out). Point OPENAI_REALTIME_URL at the current GA model, gpt-realtime.

Scope limited to this test: the pricing-catalog JSON keeps the retired
entries intentionally (historical cost calc + separate Azure timeline),
and the Azure realtime cost-calc test is unaffected.

* fix(tests): mock nvidia_nim rerank instead of hitting EOL'd endpoint

NVIDIA reached end-of-life for the hosted nvidia/llama-3.2-nv-rerankqa-1b-v2
rerank API on 2026-05-18 with no published replacement, so the live
BaseLLMRerankTest.test_basic_rerank for nvidia_nim now returns HTTP 410
("Gone"). NVIDIA's hosted catalog rotates on a schedule, so swapping in
another live model would only defer the failure.

Override test_basic_rerank in TestNvidiaNim to mock the sync/async HTTP
transport (same pattern as test_nvidia_nim_rerank_ranking_endpoint in this
file) and inject a fake NVIDIA_NIM_API_KEY via monkeypatch. The
request/response transformation and cost calculation stay covered offline.
Scope limited to nvidia_nim; other BaseLLMRerankTest providers untouched.

* fix(tests): migrate remaining realtime tests off shut-down gpt-4o-realtime-preview

OpenAI's 2026-05-07 shutdown removed the entire gpt-4o-realtime-preview
family, including the undated 'gpt-4o-realtime-preview' alias (not just the
dated snapshot fixed earlier). Three live tests still connected with the
dead alias and failed with messages_received=1 (an error event instead of
session.created):

- test_openai_realtime_simple.py: get_model() -> gpt-realtime (drives
  TestOpenAIRealtime.test_realtime_connection / test_realtime_with_query_params)
- test_openai_realtime.py: test_openai_realtime_direct_call_no_intent and
  test_openai_realtime_direct_call_with_intent -> openai/gpt-realtime
  (the with_intent test shares the same dead alias even though it was not
  in the failing set this run)

Mocked unit tests (test_realtime_query_params_construction,
test_realtime_query_params_use_normalized_model_name) are left as-is: they
never hit the network and assert string plumbing only.

Also fixes test_text_message_blocked_by_guardrail_no_ai_response, which now
connects (the earlier URL swap worked) but tripped a model-wording-brittle
assertion. The guardrail flow asks the model to voice the block message
verbatim; gpt-4o-realtime-preview complied (output contained 'blocked'),
gpt-realtime refuses verbatim-repeat instructions ('I'm sorry, but I can't
repeat that message.'). Since the original user message is blocked before
it reaches OpenAI, the refusal is still a safe outcome. Assertion #3 now
accepts both voicing and refusal, and adds a hard check that the blocked
phrase never leaks into AI output.

(cherry picked from commit ce87c411bf)
2026-05-20 15:25:30 +00:00
yuneng-jiang
c65e598402
fix(tests): replace shut-down gpt-4o-audio-preview with gpt-audio-1.5 (#28281)
* fix(tests): replace shut-down gpt-4o-audio-preview with gpt-audio-1.5

OpenAI shut down gpt-4o-audio-preview on 2026-05-07, so the live audio
calls in test_stream_chunk_builder_openai_audio_output_usage and
test_standard_logging_payload_audio now hard-fail with a model-not-found
error on every PR. The error was not "openai-internal", so the except
block swallowed it and execution fell through to an unbound
completion/response (UnboundLocalError).

Switch both tests to gpt-audio-1.5, OpenAI's recommended successor
(GA, not deprecated, already present in the litellm cost map so the
response_cost assertion still resolves). Also broaden the except to
skip with the real error in the reason instead of crashing, so a
transient upstream blip can't reintroduce the UnboundLocalError.

* fix(tests): narrow audio-test skip to model-not-found, re-raise the rest

Address review feedback: an unconditional skip on any exception would
silently mask a litellm-internal regression in the audio path (broken
param transformation, serialization, bad header) instead of failing CI.

Skip only on the upstream-unavailable class (model_not_found / "does not
exist" / openai-internal) and re-raise everything else, so genuine
regressions still fail loudly. The UnboundLocalError is still fixed
because the handler either skips or raises - it never falls through.

* fix(tests): add budget_exceeded to expected Interaction status enum

Staging added budget_exceeded to the Interaction OpenAPI status enum; the staging merge into this branch picked up the spec change but not the matching test update, so test_status_enum_values failed in CI. Align the test's expected list (exact-match by design) with the live spec.

* fix(tests): mock HTTP fetch in test_img_url_token_counter

The test parameterized a live third-party image URL (blog.purpureus.net) which now 404s, causing get_image_dimensions to fall through to its base64 decode path and crash with 'not enough values to unpack' on every PR run. Mock safe_get with a tiny 1x1 PNG so the URL branch is still exercised without any network dependency.

* fix(tests): swap gpt-4o-audio-preview to gpt-audio-1.5 in test_gpt4o_audio

OpenAI shut down gpt-4o-audio-preview on 2026-05-07, so both live tests in test_gpt4o_audio.py (test_audio_output_from_model and test_audio_input_to_model) hard-fail model_not_found on every PR. Swap the hardcoded model to OpenAI's successor gpt-audio-1.5 (same chat-completions audio surface; already in the litellm cost map). Mirror the narrowed-skip pattern from the prior audio fixes: skip on model_not_found / does-not-exist / openai-internal, re-raise everything else so genuine litellm regressions still fail CI loudly.

(cherry picked from commit 92de7423ef)
2026-05-20 15:24:55 +00:00
Yuneng Jiang
76700e7bb3
test(fireworks): mock remaining live smoke tests
test_completion_fireworks_ai and test_completion_cost_fireworks_ai
made real Fireworks calls and broke whenever Fireworks rotated its
serverless catalog (no externally-verifiable model list exists).
They also asserted nothing — just printed.

Mock the HTTP post and assert real behavior instead: the request is
built with the right model/messages and the OpenAI-compatible
response parses back; the cost path yields a non-zero cost against
the local cost map. No network, no model dependency, stronger than
the old smoke checks.

(cherry picked from commit b5db7ed37d)
2026-05-20 15:24:49 +00:00
Yuneng Jiang
a0b61f6dcb
test(fireworks): replace deprecated llama-v3p3-70b-instruct model
Fireworks removed llama-v3p3-70b-instruct from serverless, so every
live test using it now fails with NotFoundError ("Model not found,
inaccessible, and/or not deployed").

Swap the 6 references (3 files) to the currently-served
accounts/fireworks/models/deepseek-v3p1 — the canonical model in
Fireworks' current docs examples and present in LiteLLM's cost map.
test_get_model_params_fireworks_ai is a pure pricing-heuristic test
(no network) asserting the >16b branch, so it uses llama-v3p1-70b-
instruct instead to keep the "fireworks-ai-above-16b" assertion and
branch coverage intact.

(cherry picked from commit 39a1d438f2)
2026-05-20 15:24:45 +00:00
Yuneng Jiang
93d3ed7ee5
fix(tests): drop dall-e-only test classes; route live image tests via gpt-image-1
Second wave of failures from the 2026-05-12 DALL-E shutdown:
- tests/image_gen_tests/test_image_edits.py::TestOpenAIImageEditDallE2
  and tests/image_gen_tests/test_image_generation.py::TestOpenAIDalle3
  are explicitly named for the deprecated models and can't pass; remove.
  gpt-image-1 coverage already exists in sibling classes.
- tests/local_testing/test_router.py image gen tests use dall-e-3 only
  as a routing example; swap to gpt-image-1.
- tests/local_testing/test_custom_callback_input.py image_generation
  success/failure paths swapped to gpt-image-1.

(cherry picked from commit 945b10ded4)
2026-05-20 15:24:41 +00:00
yuneng-jiang
a119324127
test: replace dall-e-3 with gpt-image-1 in health check and router tests (#27813)
OpenAI returns 'The model dall-e-3 does not exist' for the test account,
breaking test_openai_img_gen_health_check and test_image_generation.
Switch to gpt-image-1, matching the existing TestOpenAIGPTImage1 pattern.

(cherry picked from commit aee58db880)
2026-05-20 15:24:38 +00:00
Claude
806b8be451
fix(mcp,jwt): address greptile review concerns
- Cache _get_agent_object_permission via user_api_key_cache (sentinel for
  no-permission rows) so MCP requests from agent keys don't hit the DB on
  every tool-list / tool-call.
- Re-raise HTTPException in handle_sse_mcp so 401 + WWW-Authenticate
  challenges (and other HTTP errors) propagate to SSE clients instead of
  being swallowed as 500.
- Normalise booleans in _validate_token_response so admin rules written as
  JSON-style "true" / "false" match upstream responses that return
  Python True / False.
- Treat configured JWT issuer claim mappings as advisory: when a mapped
  field is absent or empty, leave the normalised claim unset instead of
  raising, matching the global litellm_jwtauth path.

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-20 15:08:54 +00:00
gym-cmd
216e055910 fix(interactions): align status enum with openapi spec 2026-05-20 12:35:45 +01:00
Artem Dudarev
f6359cbf04 fix(mcp): forward Authorization in pass-through when x-litellm-api-key is admission
Commit 3753970cc9 widened the Authorization strip to cover all
is_oauth_passthrough servers — protecting against the LiteLLM admission
key leaking upstream when the caller used Authorization for admission,
but also silently stripping legitimate upstream OAuth bearers when the
caller used x-litellm-api-key for admission.

That broke transparent OAuth pass-through (EAI-506 V5/V6): standards-
compliant MCP clients (OpenCode, Claude Code, mcp-inspector) complete
PKCE against the upstream IdP and send the resulting token as plain
Authorization: Bearer per the MCP spec — with the wider strip in place,
that token never reaches the upstream and tools/list returns empty.

Narrow the strip: skip Authorization for pass-through servers only when
the caller did NOT supply x-litellm-api-key. When x-litellm-api-key is
present, admission is unambiguous and Authorization is free to carry
the upstream OAuth bearer.

The original security guarantee is preserved — a client that sends only
Authorization (no x-litellm-api-key) still has it stripped, so the
LiteLLM key cannot leak upstream via that path.

Tests:
- new: forwards Authorization when x-litellm-api-key is present
- new: still strips Authorization when only Authorization is present
- existing pass-through + M2M tests unchanged

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 11:41:37 +01:00
gym-cmd
3990165b26 fix(interactions): remove steps field not in google openapi spec
The steps field was added but is not present in the current Google
Interactions OpenAPI specification. Revert to using only the fields
that are actually defined in the spec.
2026-05-16 19:30:19 +01:00
gym-cmd
3753970cc9 security: prevent forwarding litellm api keys to upstream mcp servers
- Strip Authorization header from extra_headers for pass-through servers
- Pass-through servers (auth_type=None with extra_headers: [Authorization])
  must not receive the user's LiteLLM API key
- Only OAuth2 M2M and pass-through servers skip Authorization header
- Other headers (x-request-id, x-trace-id) are still forwarded normally
- Fixes credential leakage / authentication bypass in MCP pass-through mode
2026-05-16 19:23:03 +01:00
gym-cmd
409a0058ed fix(interactions): align openapi response fields 2026-05-16 00:33:49 +01:00
gym-cmd
6079a5f55b test(mcp): split oauth passthrough regressions 2026-05-15 23:17:10 +01:00
gym-cmd
8ca2f14f81 fix(mcp): address oauth passthrough review findings 2026-05-15 21:05:40 +01:00
gym-cmd
b290416b30 chore(ui): bump next to 16.2.6 2026-05-15 17:58:50 +01:00
gym-cmd
dfb1e33483 style(mcp): satisfy black formatting 2026-05-15 17:49:29 +01:00
gym-cmd
dee5d55a0b test(mcp): align trusted proxy fixtures 2026-05-15 17:49:29 +01:00
gym-cmd
1d731af5df feat(proxy): support issuer-scoped JWT auth 2026-05-15 17:49:29 +01:00
gym-cmd
c279f4e3ef fix(mcp): validate oauth callback redirect sink 2026-05-15 17:49:29 +01:00
gym-cmd
da24c8983a feat(proxy): support issuer-scoped JWT auth 2026-05-15 17:49:29 +01:00
gym-cmd
8e864f33ee fix(mcp): refine upstream OAuth metadata fallback 2026-05-15 17:49:29 +01:00
gym-cmd
0daeeab884 fix(mcp): support OAuth browser auth 2026-05-15 17:49:28 +01:00
gym-cmd
d0cc1a49f5 feat(mcp): support OAuth passthrough discovery 2026-05-15 17:49:28 +01:00
yuneng-jiang
e1fc955464
Merge pull request #27909 from BerriAI/backport/27908-litellm_1.84.0rc2
Some checks failed
Unit Tests: Caching (Redis) / caching-redis (push) Has been cancelled
Unit Tests: Proxy DB Operations / assert-shard-coverage (push) Has been cancelled
Unit Tests: Security / security (push) Has been cancelled
Unit Tests: Proxy DB Operations / custom-logging (push) Has been cancelled
Unit Tests: Proxy DB Operations / logging-misc (push) Has been cancelled
Unit Tests: Proxy DB Operations / auth-checks (push) Has been cancelled
Unit Tests: Proxy DB Operations / budgets (push) Has been cancelled
Unit Tests: Proxy DB Operations / db-and-spend (push) Has been cancelled
Unit Tests: Proxy DB Operations / endpoints-and-responses (push) Has been cancelled
Unit Tests: Proxy DB Operations / guardrails-hooks (push) Has been cancelled
Unit Tests: Proxy DB Operations / jwt-and-keys (push) Has been cancelled
Unit Tests: Proxy DB Operations / key-generation (push) Has been cancelled
Unit Tests: Proxy DB Operations / proxy-runtime (push) Has been cancelled
Unit Tests: Proxy DB Operations / proxy-server-core (push) Has been cancelled
Unit Tests: Proxy DB Operations / schema-migration (push) Has been cancelled
Unit Tests: Proxy DB Operations / proxy-utils (push) Has been cancelled
chore: backport #27908 to litellm_1.84.0rc2
2026-05-13 22:12:10 -07:00
Yuneng Jiang
fcd63b0137
uv lock 2026-05-13 22:10:15 -07:00
Yuneng Jiang
0f741fc12d
bump: version 0.4.71 → 0.4.72 2026-05-13 22:10:15 -07:00
yuneng-jiang
321d5763e4
Merge pull request #27904 from BerriAI/backport/27878-litellm_1.84.0rc2
fix: backport #27878 to litellm_1.84.0rc2
2026-05-13 21:43:12 -07:00
Yuneng Jiang
fe186651fa Merge remote-tracking branch 'origin/litellm_1.84.0rc2' into backport/27878-litellm_1.84.0rc2
# Conflicts:
#	tests/test_litellm/proxy/rag_endpoints/test_rag_endpoints.py
2026-05-13 21:39:17 -07:00
yuneng-jiang
087003ea04
Merge pull request #27903 from BerriAI/backport/27892-litellm_1.84.0rc2
fix: backport #27892 to litellm_1.84.0rc2
2026-05-13 21:36:33 -07:00
Krrish Dholakia
d35d2a70f4 fix: harden /key/update authorization checks (#27878)
* fix: patch Host-header auth bypass in get_request_route

Starlette reconstructs request.url from the Host header. A malformed
Host like `localhost/?x=1` causes Starlette to build the full URL as
`http://localhost/?x=1/health`, which url-parses to path="/". Since "/"
is in LiteLLMRoutes.public_routes, all protected routes became reachable
without authentication.

Fix: read scope["path"] (set by uvicorn from the HTTP request line,
not derivable from headers) instead of request.url.path. Sub-path
deployments are handled via scope["app_root_path"] / scope["root_path"],
mirroring Starlette's own base_url construction logic.

Affected variants confirmed fixed:
  Host: localhost/?x=1
  Host: localhost:4000/?x=1
  Host: localhost/#test
  Host: localhost:4000/#test

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* style: reduce comments in route fix

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* fix: block credential fields in RAG ingest vector_store options

Credential fields (vertex_credentials, aws_access_key_id, api_key, etc.)
in ingest_options.vector_store are now rejected at the API boundary with
a 400 error. Credentials must be configured server-side.

Previously any authenticated user could supply a vertex_credentials dict
with type=external_account pointing credential_source.file at an
arbitrary path (e.g. /proc/1/environ) and token_url at an
attacker-controlled server. google-auth's identity_pool.Credentials
refresh() would read the file and POST its contents to the attacker.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* fix: block /key/update self-escalation by assigned users

Non-admin users who were assigned a key (created_by != caller) could
update any non-budget field — models, rpm_limit, guardrails, etc. —
without admin authorization, allowing privilege self-escalation.

Gate: only the key creator (created_by == caller) may edit their own
key without admin check; budget changes always require admin regardless
of creator status. All other callers must pass _check_key_admin_access.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* fix: block user-controlled api_base in RAG ingest vector_store options

A user-supplied api_base in ingest_options.vector_store caused the server
to forward its configured provider credentials (Gemini, OpenAI) to an
attacker-controlled endpoint via SSRF.

Add api_base to the blocked credential params set alongside api_key and
the existing credential fields.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* fix: restrict /utils/transform_request to PROXY_ADMIN and apply body safety check

Any authenticated internal_user could POST arbitrary provider config
(aws_sts_endpoint, api_base, etc.) to /utils/transform_request and have
the server forward its credentials to an attacker-controlled endpoint.

- Gate the endpoint on PROXY_ADMIN role (403 for all other roles)
- Call is_request_body_safe() to reject banned params even for admins
- Convert ValueError from safety check to HTTP 400

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* fix: apply banned-param check to /utils/transform_request

Without is_request_body_safe(), any authenticated user could pass
aws_sts_endpoint, api_base, or aws_web_identity_token to
/utils/transform_request and have the server forward its configured
provider credentials to an attacker-controlled endpoint during SDK
credential resolution.

Applies the same banned-param blocklist already used by LLM endpoints.
Endpoint remains accessible to all authenticated users.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* fix: block SSRF via api_base in /prompts/test dotprompt YAML frontmatter

Any frontmatter key not in ["model","input","output"] flowed into
optional_params and was merged into the LLM call data dict, bypassing
is_request_body_safe. An attacker with any bearer key could set
api_base in YAML to redirect the outbound LLM request — including the
provider API key — to an attacker-controlled host.

Fix: call is_request_body_safe on the constructed data dict after
optional_params are merged, before invoking ProxyBaseLLMRequestProcessing.
ValueError from the banned-param check is surfaced as HTTP 400.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* Update litellm/proxy/rag_endpoints/endpoints.py

Co-authored-by: veria-ai[bot] <224490171+veria-ai[bot]@users.noreply.github.com>

* fix: coerce nested config strings before banned-param check

_NESTED_CONFIG_KEYS descent used isinstance(nested, dict) which silently
skipped litellm_embedding_config when delivered as a JSON string via
multipart/form-data. Banned params (api_base, aws_sts_endpoint, etc.)
nested inside the stringified value were invisible to is_request_body_safe.

_NESTED_METADATA_KEYS already used _coerce_metadata_to_dict which parses
JSON strings before checking. Apply the same coercion to _NESTED_CONFIG_KEYS.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* fix: replace substring match with prefix match in is_llm_api_route

mapped_pass_through_routes used `_llm_passthrough_route in route` (substring)
so any admin-only path whose URL contained a provider name (openai, anthropic,
azure, bedrock, etc.) was misclassified as an LLM API route and bypassed the
admin gate in non_proxy_admin_allowed_routes_check.

Confirmed live: non-admin key could GET /credentials/by_name/openai (read
masked provider API key) and DELETE /credentials/openai (delete credential).

Fix: use exact match or startswith(prefix + "/") — the same pattern used
everywhere else in RouteChecks — so only routes that actually start with a
passthrough prefix are allowed through.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* fix: stabilize PR #27878 test failures

- key_management_endpoints: extend can_skip_admin_check to team keys so
  team members with /key/update permission can update non-budget fields.
  can_team_member_execute_key_management_endpoint already validates team
  membership + permission and raises if unauthorized; reaching the admin
  check on a team key means the caller was authorized.

- test: set created_by on mock key in
  test_update_key_non_budget_fields_allowed_for_internal_user so
  caller_is_creator resolves correctly (MagicMock default ≠ user_id).

- auth_utils.get_request_route: guard against non-dict request.scope
  (e.g. MagicMock in unit tests) to prevent a MagicMock leaking into
  UserAPIKeyAuth.request_route and failing Pydantic validation.

- ci: assign test_multipart_bypass_repro.py to the proxy-runtime shard
  in test-unit-proxy-db.yml to satisfy the shard-coverage check.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* fix(lint): add explicit str() cast in get_request_route for MyPy

scope.get() returns Any|None which MyPy cannot coerce to str implicitly.
Wrap both scope.get() calls in str() to satisfy the type checker.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* fix: guard bare-/ root_path strip + make total_spend migration idempotent

auth_utils.get_request_route: when Starlette sets scope["app_root_path"]
to "/" (e.g. behind some middleware), the old stripping logic would
remove the leading slash from every path ("/team/new" → "team/new"),
breaking route matching and causing auth to misclassify protected routes.
Skip stripping when root_path is bare "/".

migration: add IF NOT EXISTS to total_spend ALTER TABLE so the migration
is safe to replay when a prior partial run already created the column.
Without this guard, prisma migrate deploy fails on CI DBs that were
partially migrated, causing all subsequent DB operations (including
/team/new) to 500.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* fix: require creator still owns key for personal-key bypass in /key/update

caller_is_creator now requires both created_by == caller AND user_id ==
caller. Previously checking only created_by let a demoted admin who
originally created a key for another user continue editing non-budget
fields on it after reassignment, bypassing _check_key_admin_access.

Adds regression test: creator whose key was reassigned is blocked (403).

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* fix: extract auth checks to fix PLR0915 + broaden max_budget assertion

internal_user_endpoints._update_single_user_helper exceeded 50 statements
(PLR0915). Extract authorization checks into _check_user_update_authz helper
to bring statement count under the limit.

test_validate_max_budget: assert "negative" (substring of both the local
"cannot be negative" and the CI "non-negative finite number" messages) so
the test is stable regardless of which exact wording the function uses.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: veria-ai[bot] <224490171+veria-ai[bot]@users.noreply.github.com>
2026-05-13 21:33:14 -07:00
Krrish Dholakia
b2c93b15e7
fix: block SSRF fields in RAG ingest vector_store config
aws_sts_endpoint, aws_web_identity_token, and aws_bedrock_runtime_endpoint
in ingest_options.vector_store were passed directly to the Bedrock ingestion
class, which reads them into boto3 STS client construction. Any authenticated
caller could redirect AssumeRole calls to an attacker-controlled server,
leaking the proxy's instance profile credentials.

Calls is_request_body_safe() on ingest_options["vector_store"] before
forwarding to litellm.aingest(). Same banned-params list and admin opt-in
escape hatch (allow_client_side_credentials) as the /chat/completions path.
ValueError from the safety check is caught and re-raised as HTTP 400.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-13 21:29:26 -07:00
Krrish Dholakia
fdb55ab65b
fix: block client-side pricing injection via request body
Authenticated clients could supply CustomPricingLiteLLMParams fields
(input_cost_per_token, output_cost_per_token, etc.) in the request body.
These were forwarded to register_model() in main.py, permanently mutating
the shared global litellm.model_cost dict for all users on the instance.

Adds all CustomPricingLiteLLMParams fields to _BANNED_REQUEST_BODY_PARAMS
so is_request_body_safe() rejects them before they reach completion().
New pricing fields added to CustomPricingLiteLLMParams are auto-covered.

Admin opt-in via allow_client_side_credentials or
configurable_clientside_auth_params still works as before.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-13 21:29:26 -07:00
yuneng-jiang
08ea016d8f
Merge pull request #27902 from BerriAI/litellm_/eager-euler-fd3639
chore(proxy): backport #27898 + #27801 to 1.84.0rc2
2026-05-13 21:14:25 -07:00
yuneng-jiang
3dd763d710
Merge pull request #27801 from stuxf/chore/get-instance-fn-runtime-s3-gate
chore(proxy): refuse remote-URL instance-fn loads outside config-file path

(cherry picked from commit e3e5209f51)
2026-05-13 20:57:24 -07:00
yuneng-jiang
974f5cd04f
Merge pull request #27898 from stuxf/chore/banned-params-extra-body-cover
chore(proxy): cover extra_body + azure_ad_token in banned-params check

(cherry picked from commit a6a9d8edf0)
2026-05-13 20:57:19 -07:00
yuneng-jiang
50030e3ab7
Merge pull request #27901 from BerriAI/yj_build_may13
[Infra] Build UI
2026-05-13 20:53:27 -07:00
Yuneng Jiang
02c5e1c0ec
chore: update Next.js build artifacts (2026-05-14 03:52 UTC, node v20.20.2) 2026-05-13 20:52:07 -07:00
yuneng-jiang
305875f97d
Merge pull request #27899 from BerriAI/backport-pr-27896-into-1.84.0rc2
fix(ui): fetch version + debug flag from /health/readiness/details (backport #27896)
2026-05-13 20:50:27 -07:00
Yuneng Jiang
51ed234e36
fix(ui): disable retries on readiness/details + cover token forwarding
Two small follow-ups on the readiness/details migration:

- Set `retry: false` on the query. The payload feeds a passive
  navbar tag and a debug banner; a 401 from an expired token
  shouldn't fan out into three retries against the proxy.
- Add navbar specs that assert the `accessToken` prop is forwarded
  into the hook (matches the DebugWarningBanner spec). Without
  this, the navbar could silently regress to passing `undefined`
  and the existing tests wouldn't catch it.
2026-05-13 20:36:38 -07:00
Yuneng Jiang
ab0267d9fd
fix(ui): fetch version + debug flag from /health/readiness/details
The proxy moved `litellm_version`, `is_detailed_debug`, and other
diagnostic fields off the public `/health/readiness` payload behind
an auth-gated `/health/readiness/details` endpoint. The navbar
version tag and the detailed-debug-mode banner stopped working
because they were still reading those fields from the unauthed
response, which no longer contains them.

Replace `useHealthReadiness` with a `useHealthReadinessDetails`
hook that takes an `accessToken` argument and sends a Bearer header
to the auth-gated endpoint. The hook stays disabled while
`accessToken` is falsy, so the navbar can keep rendering on the
public model hub (where the token is null) without triggering an
auth redirect or a 401-loop.
2026-05-13 20:36:37 -07:00
yuneng-jiang
be0526bac7
Merge pull request #27868 from BerriAI/backport-pr-27866-into-1.84.0rc2
fix(proxy): expose db status on public /health/readiness (backport #27866)
2026-05-13 13:41:24 -07:00
yuneng-jiang
be37cf782c fix(proxy): expose db status on public /health/readiness
Backport of #27866 onto litellm_1.84.0rc2.

External readiness probes consumed the legacy detailed payload's `db`
field to drive alerting and pod-rotation decisions. Stripping the body
to {"status": "healthy"} broke those probes silently — the HTTP code
still flipped to 503, but probes checking body.db == "connected"
treated the response as healthy.

Add `db` back to the unauthenticated payload. The rest of the diagnostic
fields (litellm_version, callbacks, cache, log_level) stay behind
/health/readiness/details so the recon-leak gate from #26912 holds.
Values match the legacy contract: "connected", "disconnected",
"Not connected". The 503-on-DB-disconnect behavior from LIT-2607 is
preserved.
2026-05-13 13:18:54 -07:00
yuneng-jiang
2b189be7f1
Merge pull request #27819 from BerriAI/litellm_backport_regenerate_guard_84rc2
[Fix] Backport /key/regenerate ownership-rebind + premium-gate guards (#27793)
2026-05-12 23:36:57 -07:00
yuneng-jiang
8b90a547ff
Merge pull request #27818 from BerriAI/litellm_backport_27812_to_1840rc2
[Fix] Lazy feature loading under SERVER_ROOT_PATH returns 404 (backport of #27812)
2026-05-12 23:35:59 -07:00
Yuneng Jiang
069469c7c0
chore(proxy): backport /key/regenerate ownership-rebind + premium-gate guards (#27793)
Backport of #27793 onto litellm_1.84.0rc2.

A non-admin caller could rebind their own key's user_id via /key/regenerate.
_execute_virtual_key_regeneration had org/team guards but no user_id guard,
and prepare_key_update_data did not strip the field — it survived
model_dump(exclude_unset=True) into the Prisma update. On the next request,
_return_user_api_key_auth_obj resolved the rebound user_id against
litellm_usertable and returned PROXY_ADMIN whenever the target row's
user_role was admin.

/key/update had the equivalent guard inline at _validate_update_key_data;
extract it to a shared helper _validate_caller_can_change_key_ownership and
call from both /key/update and _execute_virtual_key_regeneration.

Also tighten the premium gate that allowed the master-key rotation branch to
skip the enterprise check. The previous predicate was a field-presence test,
not an identity check. Verify the caller actually holds the master key via
_is_master_key before allowing the non-premium path.

Block explicit-null user_id and empty-string user_id as removal attempts;
both 403-reject for non-admin callers.
2026-05-12 23:18:36 -07:00
Yuneng Jiang
c14cab9082
Cache normalized SERVER_ROOT_PATH at middleware init
SERVER_ROOT_PATH is a process-startup env var. Read it once in
__init__ instead of calling get_server_root_path() + rstrip on every
request that arrives before all lazy features have loaded.
2026-05-12 23:15:19 -07:00
Yuneng Jiang
2675b507c6
Strip SERVER_ROOT_PATH before lazy-feature prefix match
LazyFeatureMiddleware compared the raw scope path against registered
prefixes (e.g. /policies), so requests under a server root path like
/api/v1/policies/... never matched, the feature never loaded, and the
endpoint returned 404. Strip the configured root path before matching,
normalizing trailing slashes and enforcing a component boundary so
/api does not falsely match /apiv2.
2026-05-12 23:15:19 -07:00
yuneng-jiang
037d4c505b
Merge pull request #27794 from BerriAI/litellm_/fervent-archimedes-7e86be
Some checks are pending
Unit Tests: Caching (Redis) / caching-redis (push) Waiting to run
Unit Tests: Proxy DB Operations / assert-shard-coverage (push) Waiting to run
Unit Tests: Proxy DB Operations / auth-checks (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / budgets (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / custom-logging (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / db-and-spend (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / endpoints-and-responses (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / guardrails-hooks (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / jwt-and-keys (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / key-generation (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / logging-misc (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / proxy-runtime (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / proxy-server-core (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / schema-migration (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / proxy-utils (push) Blocked by required conditions
Unit Tests: Security / security (push) Waiting to run
cherry-pick: reject bare str at file-input sinks (#27762) onto litellm_1.84.0rc2
2026-05-12 17:45:17 -07:00
Krrish Dholakia
590bc834c5
chore: reject bare str at file-input sinks to prevent local-file read (#27762)
Cherry-pick of #27762 onto litellm_1.84.0rc2.

* chore: reject bare str at file-input sinks to prevent local-file read (#27667)
* fix: use os.PathLike in ocr sink and check truthy reasoningSummary for bridge
  - ocr/main.py: widen Path check to os.PathLike for consistency with other sinks
  - main.py: bridge condition checks truthiness of reasoning_summary, not just None
* fix: remove unused pathlib.Path import in ocr/main.py

Co-authored-by: yuneng-jiang <yuneng@berri.ai>
Co-authored-by: ryan-crabbe-berri <ryan@berri.ai>
Co-authored-by: stuxf <70670632+stuxf@users.noreply.github.com>
2026-05-12 17:30:03 -07:00
yuneng-jiang
74bb5253dc
Merge pull request #27789 from BerriAI/litellm_/wizardly-sammet-94baac
fix(proxy): always merge caller-supplied tags into request metadata
2026-05-12 16:47:25 -07:00