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>
This commit is contained in:
user 2026-04-25 18:16:16 +00:00
parent 72f8a68680
commit 2e2e1cbf71
No known key found for this signature in database
2 changed files with 41 additions and 28 deletions

View file

@ -9,6 +9,7 @@ from fastapi import HTTPException, Request, status
from litellm import Router, provider_list
from litellm._logging import verbose_proxy_logger
from litellm.constants import STANDARD_CUSTOMER_ID_HEADERS
from litellm.litellm_core_utils.url_utils import SSRFError, validate_url
from litellm.proxy._types import *
from litellm.types.router import CONFIGURABLE_CLIENTSIDE_AUTH_PARAMS
@ -79,8 +80,6 @@ def check_complete_credentials(request_body: dict) -> bool:
if not (api_key_value and isinstance(api_key_value, str) and api_key_value.strip()):
return False
from litellm.litellm_core_utils.url_utils import SSRFError, validate_url
for url_field in ("api_base", "base_url"):
url_value = request_body.get(url_field)
if not url_value or not isinstance(url_value, str):

View file

@ -11,34 +11,48 @@ If given, generate a unique model_id for the deployment.
Ensures cooldowns are applied correctly.
"""
from typing import List
clientside_credential_keys = ["api_key", "api_base", "base_url"]
# Admin-configured fields that carry secrets or environment-specific config
# meant for the *original* upstream. When the caller redirects ``api_base`` /
# ``base_url`` to their own server, these MUST NOT flow through unchanged or
# the admin's ``OpenAI-Organization`` header, ``extra_body`` payloads, AWS /
# Vertex / Azure credentials, etc. would be sent to the attacker. Only carry
# them through when the caller explicitly re-supplies the field.
_ADMIN_CONFIG_FIELDS_TO_CLEAR_ON_BASE_OVERRIDE = [
"organization",
"extra_body",
"extra_headers",
"default_headers",
"api_version",
"api_type",
"azure_ad_token",
"azure_ad_token_provider",
"aws_access_key_id",
"aws_secret_access_key",
"aws_session_token",
"aws_region_name",
"aws_sts_endpoint",
"aws_web_identity_token",
"aws_role_name",
"vertex_credentials",
"vertex_project",
"vertex_location",
]
def _admin_config_fields_to_clear_on_base_override() -> List[str]:
"""
Provider-specific credential / endpoint-targeting fields that must NOT
flow through to a client-redirected upstream.
Built dynamically from ``CredentialLiteLLMParams.model_fields`` so any
new provider field added there (Bedrock endpoint, Watsonx region, etc.)
is gated automatically plus a fixed list of kwargs-only fields that
aren't declared on the typed model.
"""
from litellm.types.router import CredentialLiteLLMParams
typed_fields = [
f
for f in CredentialLiteLLMParams.model_fields
if f not in clientside_credential_keys
]
kwargs_only_fields = [
# Caller-supplied via **kwargs, not declared on CredentialLiteLLMParams.
"organization",
"extra_body",
"extra_headers",
"default_headers",
"api_type",
"azure_ad_token",
"azure_ad_token_provider",
"aws_session_token",
"aws_sts_endpoint",
"aws_web_identity_token",
"aws_role_name",
]
return typed_fields + kwargs_only_fields
_ADMIN_CONFIG_FIELDS_TO_CLEAR_ON_BASE_OVERRIDE = (
_admin_config_fields_to_clear_on_base_override()
)
def is_clientside_credential(request_kwargs: dict) -> bool: