From 2e2e1cbf7161e9cc7cf38161ccac8ed7a18af273 Mon Sep 17 00:00:00 2001 From: user <70670632+stuxf@users.noreply.github.com> Date: Sat, 25 Apr 2026 18:16:16 +0000 Subject: [PATCH] refactor(auth): hoist url_utils import; derive admin-config field list from CredentialLiteLLMParams MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /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) --- litellm/proxy/auth/auth_utils.py | 3 +- .../clientside_credential_handler.py | 66 +++++++++++-------- 2 files changed, 41 insertions(+), 28 deletions(-) diff --git a/litellm/proxy/auth/auth_utils.py b/litellm/proxy/auth/auth_utils.py index a57eb2be271..8918a1ceb16 100644 --- a/litellm/proxy/auth/auth_utils.py +++ b/litellm/proxy/auth/auth_utils.py @@ -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): diff --git a/litellm/router_utils/clientside_credential_handler.py b/litellm/router_utils/clientside_credential_handler.py index 3ce8e37f8a6..1f05efa842a 100644 --- a/litellm/router_utils/clientside_credential_handler.py +++ b/litellm/router_utils/clientside_credential_handler.py @@ -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: