From 47e41deab60027c26c6b6b8a6ba4d494f43f3994 Mon Sep 17 00:00:00 2001 From: joereyna Date: Wed, 11 Mar 2026 20:00:25 -0700 Subject: [PATCH] fix: enforce SERVER_ROOT_PATH prefix guard in mapped pass-through route checks Routes lacking the root prefix can no longer spuriously match mapped pass-through routes (vertex_ai, bedrock, etc.) when SERVER_ROOT_PATH is set. Also applies the same fix to the identical check in user_api_key_auth.py (litellm_user_api_key header extraction). --- litellm/proxy/auth/user_api_key_auth.py | 16 ++++++++++---- .../pass_through_endpoints.py | 21 ++++++++++++------- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/litellm/proxy/auth/user_api_key_auth.py b/litellm/proxy/auth/user_api_key_auth.py index c992cfb53e8..40a1e250689 100644 --- a/litellm/proxy/auth/user_api_key_auth.py +++ b/litellm/proxy/auth/user_api_key_auth.py @@ -50,7 +50,7 @@ from litellm.proxy.common_utils.http_parsing_utils import ( _read_request_body, _safe_get_request_headers, populate_request_with_path_params) from litellm.proxy.common_utils.realtime_utils import _realtime_request_body -from litellm.proxy.utils import PrismaClient, ProxyLogging +from litellm.proxy.utils import PrismaClient, ProxyLogging, get_server_root_path from litellm.secret_managers.main import get_secret_bool from litellm.types.services import ServiceTypes @@ -386,9 +386,17 @@ async def check_api_key_for_custom_headers_or_pass_through_endpoints( api_key: str, ) -> Union[UserAPIKeyAuth, str]: is_mapped_pass_through_route: bool = False - for mapped_route in LiteLLMRoutes.mapped_pass_through_routes.value: # type: ignore - if route.startswith(mapped_route): - is_mapped_pass_through_route = True + root_path = get_server_root_path() + if root_path and root_path != "/": + if route.startswith(root_path): + normalized_route = route[len(root_path):] + for mapped_route in LiteLLMRoutes.mapped_pass_through_routes.value: # type: ignore + if normalized_route.startswith(mapped_route): + is_mapped_pass_through_route = True + else: + for mapped_route in LiteLLMRoutes.mapped_pass_through_routes.value: # type: ignore + if route.startswith(mapped_route): + is_mapped_pass_through_route = True if is_mapped_pass_through_route: if request.headers.get("litellm_user_api_key") is not None: api_key = request.headers.get("litellm_user_api_key") or "" diff --git a/litellm/proxy/pass_through_endpoints/pass_through_endpoints.py b/litellm/proxy/pass_through_endpoints/pass_through_endpoints.py index 1033ba7921a..8d6a4c00b71 100644 --- a/litellm/proxy/pass_through_endpoints/pass_through_endpoints.py +++ b/litellm/proxy/pass_through_endpoints/pass_through_endpoints.py @@ -2058,14 +2058,21 @@ class InitPassThroughEndpointHelpers: bool: True if route is a registered pass-through endpoint, False otherwise """ ## CHECK IF MAPPED PASS THROUGH ENDPOINT - # Strip server root path prefix so mapped routes match when SERVER_ROOT_PATH is set + # When SERVER_ROOT_PATH is set, all valid routes carry that prefix. + # Strip it before comparing against mapped routes; if the route does not + # carry the prefix, it cannot be a mapped pass-through route. root_path = get_server_root_path() - normalized_route = route - if root_path and root_path != "/" and route.startswith(root_path): - normalized_route = route[len(root_path):] - for mapped_route in LiteLLMRoutes.mapped_pass_through_routes.value: - if normalized_route.startswith(mapped_route): - return True + if root_path and root_path != "/": + if route.startswith(root_path): + normalized_route = route[len(root_path):] + for mapped_route in LiteLLMRoutes.mapped_pass_through_routes.value: + if normalized_route.startswith(mapped_route): + return True + # Route lacks expected prefix — not a mapped pass-through route + else: + for mapped_route in LiteLLMRoutes.mapped_pass_through_routes.value: + if route.startswith(mapped_route): + return True # Fast path: check if any registered route key contains this path # Keys are in format: "{endpoint_id}:exact:{path}:{methods}" or "{endpoint_id}:subpath:{path}:{methods}"