fix(proxy): ban caller-supplied aws identity selectors in request bodies

This commit is contained in:
mateo-berri 2026-08-10 22:05:49 -07:00
parent b5eed5e526
commit 1b488f7c2f
3 changed files with 78 additions and 0 deletions

View file

@ -262,6 +262,14 @@ _BANNED_REQUEST_BODY_PARAMS: Final[tuple[str, ...]] = (
"aws_sts_endpoint",
"aws_web_identity_token",
"aws_role_name",
# Remaining AWS identity selectors. ``get_credentials`` prefers a named
# profile over the deployment's static keys, so a caller-supplied
# ``aws_profile_name`` signs Bedrock and S3 requests as any profile
# present on the proxy host; the two AssumeRole knobs are banned with it
# so the whole identity-selection family lives behind the same opt-in.
"aws_profile_name",
"aws_session_name",
"aws_external_id",
"vertex_credentials",
# Azure managed-identity / federated-auth token. The Azure provider
# transformer reads ``azure_ad_token`` (top-level or via

View file

@ -3131,3 +3131,72 @@ class TestHasUserSetupSso:
monkeypatch.setenv("SAML_IDP_METADATA_XML", "<EntityDescriptor/>")
assert _has_user_setup_sso() is True
class TestIsRequestBodySafeBlocksAwsIdentitySelectors:
"""A caller must not be able to redirect Bedrock signing to another identity
reachable from the proxy host. ``get_credentials`` prefers a named profile
and the AssumeRole knobs over the deployment's static keys, and the file /
batch endpoints fold the request body and the deployment credentials into a
single params dict, so these have to be rejected at the boundary (#36155).
"""
@pytest.mark.parametrize(
"selector",
["aws_profile_name", "aws_session_name", "aws_external_id"],
)
def test_aws_identity_selector_in_batch_body_is_rejected(self, selector):
with pytest.raises(ValueError, match=selector):
is_request_body_safe(
request_body={
"input_file_id": "file-abc123",
"endpoint": "/v1/chat/completions",
"completion_window": "24h",
"model": "bedrock-batch-model",
selector: "attacker-chosen",
},
general_settings={},
llm_router=None,
model="bedrock-batch-model",
)
@pytest.mark.parametrize(
"selector",
["aws_profile_name", "aws_session_name", "aws_external_id"],
)
def test_aws_identity_selector_under_extra_body_is_rejected(self, selector):
with pytest.raises(ValueError, match=selector):
is_request_body_safe(
request_body={
"model": "bedrock-batch-model",
"extra_body": {selector: "attacker-chosen"},
},
general_settings={},
llm_router=None,
model="bedrock-batch-model",
)
def test_aws_identity_selector_allowed_under_proxy_wide_opt_in(self):
assert (
is_request_body_safe(
request_body={
"model": "bedrock-batch-model",
"aws_profile_name": "admin-approved-profile",
},
general_settings={"allow_client_side_credentials": True},
llm_router=None,
model="bedrock-batch-model",
)
is True
)
def test_upload_body_without_identity_selectors_is_accepted(self):
assert (
is_request_body_safe(
request_body={"purpose": "batch", "model": "bedrock-batch-model"},
general_settings={},
llm_router=None,
model="bedrock-batch-model",
)
is True
)

View file

@ -23,6 +23,7 @@ from litellm.proxy.auth.auth_utils import is_request_body_safe # noqa: E402
"aws_web_identity_token",
"aws_sts_endpoint",
"aws_role_name",
"aws_profile_name",
"api_base",
"base_url",
"vertex_credentials",