fix(proxy): make URL model authoritative for Bedrock path-routed passthrough actions

The allowlist check read model from the request body first, while
bedrock_llm_proxy_route dispatches purely on the path model for invoke,
converse, and their streaming variants. A caller could put an allowed
model in the JSON body while targeting a disallowed model in the URL and
slip past the check. count_tokens keeps reading from the body since its
route has no model segment in the path.
This commit is contained in:
Riddhi04 2026-07-24 16:38:34 +04:00 committed by mateo-berri
parent 534ab1628d
commit 6981ccf0ca
2 changed files with 53 additions and 9 deletions

View file

@ -1981,16 +1981,20 @@ def get_model_from_request(
if vertex_match:
model = vertex_match.group(1)
if model is None and route.lower().startswith("/bedrock"):
from litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints import (
_extract_model_from_bedrock_endpoint,
)
if route.lower().startswith("/bedrock"):
bedrock_endpoint = re.sub(r"^/bedrock/", "", route, flags=re.IGNORECASE)
try:
model = _extract_model_from_bedrock_endpoint(bedrock_endpoint)
except ValueError:
model = None
is_bedrock_count_tokens_route = (
"count_tokens" in bedrock_endpoint.lower() or "count-tokens" in bedrock_endpoint.lower()
)
if not is_bedrock_count_tokens_route:
from litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints import (
_extract_model_from_bedrock_endpoint,
)
try:
model = _extract_model_from_bedrock_endpoint(bedrock_endpoint)
except ValueError:
pass
return model

View file

@ -478,6 +478,46 @@ def test_get_model_from_request_bedrock_unparseable_endpoint_returns_none():
)
def test_get_model_from_request_bedrock_url_model_overrides_body_model():
assert (
get_model_from_request(
request_data={"model": "us.anthropic.claude-sonnet-4-6"},
route="/bedrock/model/us.anthropic.claude-opus-4-6-v1/converse",
)
== "us.anthropic.claude-opus-4-6-v1"
)
def test_get_model_from_request_bedrock_invoke_url_model_overrides_body_model():
assert (
get_model_from_request(
request_data={"model": "us.anthropic.claude-sonnet-4-6"},
route="/bedrock/model/us.anthropic.claude-opus-4-6-v1/invoke",
)
== "us.anthropic.claude-opus-4-6-v1"
)
def test_get_model_from_request_bedrock_count_tokens_uses_body_model():
assert (
get_model_from_request(
request_data={"model": "us.anthropic.claude-sonnet-4-6"},
route="/bedrock/v1/messages/count_tokens",
)
== "us.anthropic.claude-sonnet-4-6"
)
def test_get_model_from_request_bedrock_unparseable_endpoint_keeps_body_model():
assert (
get_model_from_request(
request_data={"model": "us.anthropic.claude-sonnet-4-6"},
route="/bedrock/agents/some-agent-route",
)
== "us.anthropic.claude-sonnet-4-6"
)
def test_get_model_from_request_includes_file_endpoint_header_model():
assert (
get_model_from_request(