fix(proxy): ban ssl_verify as a request-body param

Veria flagged that making ssl_verify functional (previous commit) also
made it caller-controlled at the proxy: a client could send
ssl_verify=false to disable upstream certificate verification, or a
path string that reaches os.path.exists() as a local-file oracle.

Add ssl_verify to _BANNED_REQUEST_BODY_PARAMS, same treatment as
use_ssl. Legitimate per-deployment CA bundles still work via the
existing admin opt-ins (allow_client_side_credentials proxy-wide, or
configurable_clientside_auth_params per deployment); only caller-
supplied request-body values are rejected.
This commit is contained in:
Praveena Ganesan 2026-08-26 11:13:49 +05:30
parent 60e6545c75
commit fb6d1dcf75
3 changed files with 75 additions and 0 deletions

View file

@ -352,6 +352,11 @@ _BANNED_REQUEST_BODY_PARAMS: Final[tuple[str, ...]] = (
# the request away from the admin's pinned configuration.
"nvcf_function_id",
"use_ssl",
# TLS trust decision for the outbound provider connection. A caller-supplied
# value downgrades or disables certificate verification on a connection the
# admin pinned, and a string value reaches os.path.exists() as a local-file
# oracle. Deployment-level config only, same as ``use_ssl`` above.
"ssl_verify",
# Per-deployment opt-in that hands the whole call to the Rust core. It is a
# deployment decision, not a request one: the Rust path uses its own client
# rather than the one the deployment configured, and reports no post_call,

View file

@ -2616,6 +2616,75 @@ class TestIsRequestBodySafeBlocksRivaUseSsl:
)
class TestIsRequestBodySafeBlocksSslVerify:
"""``ssl_verify`` configures TLS trust for the outbound provider connection.
A caller-supplied ``false`` disables certificate verification on a
connection the admin pinned, and a string value reaches
``os.path.exists()`` as a local-file oracle, so it is rejected as a
request-body param unless the admin opted in proxy-wide or
per-deployment, same as ``use_ssl`` above."""
def test_ssl_verify_false_in_request_body_is_rejected(self):
with pytest.raises(ValueError, match="ssl_verify"):
is_request_body_safe(
request_body={
"model": "openai/gpt-3.5-turbo",
"ssl_verify": False,
},
general_settings={},
llm_router=None,
model="openai/gpt-3.5-turbo",
)
def test_ssl_verify_path_in_request_body_is_rejected(self):
with pytest.raises(ValueError, match="ssl_verify"):
is_request_body_safe(
request_body={
"model": "openai/gpt-3.5-turbo",
"ssl_verify": "/etc/passwd",
},
general_settings={},
llm_router=None,
model="openai/gpt-3.5-turbo",
)
def test_admin_opt_in_proxy_wide_allows_ssl_verify(self):
assert (
is_request_body_safe(
request_body={
"model": "openai/gpt-3.5-turbo",
"ssl_verify": "/opt/app/certs/ca.crt",
},
general_settings={"allow_client_side_credentials": True},
llm_router=None,
model="openai/gpt-3.5-turbo",
)
is True
)
def test_admin_opt_in_per_deployment_allows_ssl_verify(self, monkeypatch):
from litellm.proxy.auth import auth_utils
monkeypatch.setattr(
auth_utils,
"_allow_model_level_clientside_configurable_parameters",
lambda model, param, request_body_value, llm_router: param == "ssl_verify",
)
assert (
is_request_body_safe(
request_body={
"model": "openai/gpt-3.5-turbo",
"ssl_verify": "/opt/app/certs/ca.crt",
},
general_settings={},
llm_router=None,
model="openai/gpt-3.5-turbo",
)
is True
)
class TestIsRequestBodySafeBlocksBedrockTags:
"""``bedrock_tags`` lands as AWS resource tags on Bedrock batch jobs
created with the proxy's AWS identity, so a caller-supplied value can

View file

@ -28,6 +28,7 @@ from litellm.proxy.auth.auth_utils import is_request_body_safe # noqa: E402
"base_url",
"vertex_credentials",
"azure_ad_token",
"ssl_verify",
],
)
def test_banned_param_under_extra_body_is_rejected(banned_param):