mirror of
https://github.com/tiennm99/litellm.git
synced 2026-07-12 15:05:01 +00:00
dedaf74a5e
* chore(auth): validate clientside api_base against SSRF guard; clear admin secrets on base override Two related issues with how the proxy handles client-supplied ``api_base`` / ``base_url`` overrides on chat-completion requests: 1. **SSRF gate bypass** — ``check_complete_credentials()`` returned ``True`` for any non-empty ``api_key``, allowing the ``is_request_body_safe`` ``banned_params`` loop to admit ``api_base`` / ``base_url`` values that point at private (RFC 1918), loopback, link-local, or cloud-metadata addresses. Now: when the gate sees a client-supplied ``api_base`` / ``base_url``, it runs the URL through ``litellm_core_utils.url_utils.validate_url`` (DNS-resolves, blocks internal/IMDS/LL networks, defends against rebinding). Rejection raises with a clear message. 2. **Admin-config leak on base override** — ``get_dynamic_litellm_params`` only carried the three clientside keys (``api_key``, ``api_base``, ``base_url``) from request to upstream call. Other admin-configured fields on ``litellm_params`` — ``organization``, ``extra_body``, ``extra_headers``, ``api_version``, ``azure_ad_token``, AWS / Vertex creds, etc. — flowed through unchanged. With base redirected to a client-controlled server, those admin secrets were sent to the attacker. Now: when ``api_base`` / ``base_url`` is in ``request_kwargs``, drop those admin-config fields from ``litellm_params`` unless the caller re-supplied them. Tests cover the SSRF-target rejection per URL field, the admin-secret clearing on base override, the don't-clear case when only ``api_key`` is overridden (BYOK pattern), and the don't-overwrite case when the caller resupplies fields like ``organization`` themselves. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(vertex-batches): wrap api_base GET in safe_get for defense-in-depth The vertex batches status-poll fetches an attacker-influenceable ``api_base`` URL with a raw ``sync_handler.get()``. The proxy auth gate already validates clientside ``api_base`` before reaching this sink, so the proxy flow is covered. This adds the per-sink wrap so SDK callers and any future code path that bypasses the proxy gate pick up the same SSRF defense from ``url_utils.safe_get``. Operators with a legitimate private Vertex base can either allowlist the host via ``litellm.user_url_allowed_hosts`` or disable validation with ``litellm.user_url_validation = False``. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(auth): hoist url_utils import; derive admin-config field list from CredentialLiteLLMParams /simplify pass: - Move ``from litellm.litellm_core_utils.url_utils import SSRFError, validate_url`` to module top in ``proxy/auth/auth_utils.py``. CLAUDE.md prefers module-level imports unless avoiding a circular dependency, and there's no cycle here (``url_utils`` doesn't depend on ``proxy.auth``). - Replace the hardcoded ``_ADMIN_CONFIG_FIELDS_TO_CLEAR_ON_BASE_OVERRIDE`` literal with ``_admin_config_fields_to_clear_on_base_override()`` that derives the typed-field portion from ``CredentialLiteLLMParams.model_fields``. Adds three fields the hardcoded list missed (``aws_bedrock_runtime_endpoint``, ``watsonx_region_name``, ``region_name``) and stays in sync as new provider fields are declared on the model. The kwargs-only set (``organization``, ``extra_body``, ``azure_ad_token``, ``aws_session_token``, ``aws_sts_endpoint``, ``aws_web_identity_token``, ``aws_role_name``, …) remains explicit since those fields aren't on the typed model. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(auth): close field-echo bypass; gate URL check on toggle; cover async batch path Three issues from review: 1. ``get_dynamic_litellm_params`` used ``if field not in request_kwargs: pop`` to clear admin-set provider config when the caller redirected ``api_base``. A caller could *echo* any clear-list field name (with any value, including an empty string) to skip the pop, leaving the admin's value in ``litellm_params`` to be forwarded to the redirected upstream. Fix: always pop, then write the caller's value back if they resupplied the field. 2. ``check_complete_credentials`` called ``validate_url`` directly. That helper doesn't itself consult ``litellm.user_url_validation``; the toggle is honoured by ``safe_get`` / ``async_safe_get``. Mirror that here so admins who explicitly disabled URL validation aren't blocked at the proxy boundary. 3. ``VertexAIBatchesHandler._async_retrieve_batch`` still used a bare ``await client.get(api_base, ...)`` while the sync sibling was wrapped in ``safe_get``. Wrap the async call in ``async_safe_get`` so SDK callers on the async path get the same DNS-rebind / private / cloud-metadata defenses as the sync path. Tests: - ``TestCheckCompleteCredentialsBlocksSSRF`` is now mock-only; an autouse fixture flips the toggle on, ``validate_url`` is patched in the parametrized blocking tests, and the positive path no longer makes a real DNS call to api.openai.com. - ``test_skips_url_validation_when_toggle_is_off`` documents the new toggle-off behaviour and asserts ``validate_url`` is not called. - ``test_caller_resupplied_value_overrides_admin_value_on_base_override`` replaces the prior test that asserted the buggy preserve-admin-value-on-echo behaviour. - ``test_field_echo_does_not_preserve_admin_value`` is a focused regression test for the empty-string echo vector. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(auth): close provider-confusion credential exfil; expand banned-params; cover OCI Three additions on top of the entry-point URL gate so the cluster is fully closed against caller-supplied ``api_base`` redirection: 1. ``get_llm_provider_logic.py`` matched registered openai-compatible endpoints against ``api_base`` with an unanchored substring search (``if endpoint in api_base:``). A caller could pass an api_base like ``https://attacker.com/api.groq.com/openai/v1`` to coerce the proxy into reading ``GROQ_API_KEY`` from the environment and forwarding it as a Bearer credential to the attacker's host. Replaced with parsed- URL semantics (hostname exact-match plus segment-bounded path-prefix) in a new ``_endpoint_matches_api_base`` helper. 2. ``is_request_body_safe`` rejects ``api_base`` / ``base_url`` / ``user_config`` / a handful of AWS / vertex fields, but the list omitted three other endpoint-targeting fields: * ``aws_bedrock_runtime_endpoint`` — Bedrock endpoint redirect * ``langsmith_base_url`` / ``langfuse_host`` — observability callback hostnames; attacker-controlled values exfiltrate the entire request payload (incl. message content) via the logging hook. Added all three to the blocklist. 3. ``_admin_config_fields_to_clear_on_base_override`` derives its typed- field list from ``CredentialLiteLLMParams.model_fields``, which does not declare any of the OCI provider's auth fields. Added ``oci_signer``, ``oci_user``, ``oci_fingerprint``, ``oci_tenancy``, ``oci_key``, and ``oci_key_file`` to the kwargs-only fixed list so they are cleared on caller-redirected ``api_base`` like the AWS / Azure / Vertex equivalents. Tests: - ``TestEndpointMatchesApiBase`` — direct unit tests on the new matcher: legitimate provider URLs (5 shapes) match; attacker smuggling via path injection, suffix label, prefix label, userinfo ``@`` injection, and path-segment lookalikes (7 shapes) do not. - ``TestGetLlmProviderRejectsAttackerSmuggledApiBase`` — end-to-end invariant that ``GROQ_API_KEY`` is never read against an attacker- controlled host while the legitimate ``api.groq.com`` path still resolves the provider correctly. - ``TestIsRequestBodySafeBlocksEndpointTargetingFields`` — parametrized coverage that each of the three new banned-params raises a clear rejection naming the offending field. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(auth): remove implicit api-key bypass + add posthog/braintrust/slack to blocklist The historical ``check_complete_credentials`` clause inside ``is_request_body_safe`` was a third, *implicit*, *caller-controlled* BYOK path: any caller that supplied a non-empty ``api_key`` caused the entire banned-params blocklist to be skipped. That turned every missing entry on the blocklist into an exploitable SSRF / credential-exfil hole and is the root cause of the chain of api_base advisories that have been re-discovered with each new integration: * GHSA-jh89-88fc-qrfp (critical, triage) — env-var exfil via api_base * GHSA-3frq-6r6h-7j64 (high, triage) — admin org / extra_body leak * veria-admin Dv_m860l, b_yRJeQ5, stN90yjP, LBlyOAc8, U2TD78kg — variations on "list X is missing field Y" Two explicit, admin-controlled BYOK paths already exist and remain: ``general_settings.allow_client_side_credentials = true`` (proxy-wide) and ``configurable_clientside_auth_params: [...]`` per deployment. Removing the implicit bypass converts the failure mode of a missing blocklist entry from "live credential leak" to "predictable 400 with a clear remediation message," which is the structural fix. Also adds the three remaining endpoint-targeting fields the dynamic callback layer reads from request body: ``posthog_host``, ``braintrust_host``, ``slack_webhook_url``. ``slack_webhook_url`` in particular was a direct exfil channel (caller-set webhook → proxy mirrors every request to attacker's Slack). Tests: - ``test_api_key_does_not_bypass_blocklist`` — parametrized regression asserting api_key=anything no longer skips the gate for any of the five highest-risk fields. - ``test_admin_opt_in_proxy_wide_still_allows`` — confirms the documented BYOK opt-in still works. - Extends ``test_endpoint_targeting_field_in_request_body_is_rejected`` to cover posthog / braintrust / slack. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(auth): block sagemaker_base_url, s3_endpoint_url, deployment_url Provider-specific endpoint overrides surfaced by a wider audit of ``optional_params`` consumers in ``litellm/llms/``. Same threat as ``api_base``: a caller-supplied value redirects the outbound request to an attacker host. * ``s3_endpoint_url`` — read in ``litellm/llms/bedrock/files/transformation.py`` to build the S3 upload URL for Bedrock files. Caller redirects file uploads to attacker-controlled S3. * ``sagemaker_base_url`` — read in ``litellm/llms/sagemaker/{chat,completion}/*``. Caller redirects SageMaker traffic. This is the primary vector described in veria-admin mNqEBBtG. * ``deployment_url`` — popped in ``litellm/llms/sap/chat/transformation.py``. Caller redirects SAP deployment requests. Tests parametrize ``test_endpoint_targeting_field_in_request_body_is_rejected`` to cover the three new fields. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
In total litellm runs 1000+ tests
[02/20/2025] Update:
To make it easier to contribute and map what behavior is tested,
we've started mapping the litellm directory in tests/test_litellm
This folder can only run mock tests.