diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index dcb846a177d..40c66d5894f 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -4063,7 +4063,13 @@ class ProxyConfig: ## URL VALIDATION SETTINGS ## _user_url_validation = general_settings.get("user_url_validation", None) if _user_url_validation is not None: - litellm.user_url_validation = bool(_user_url_validation) + if isinstance(_user_url_validation, str): + litellm.user_url_validation = ( + _user_url_validation.strip().lower() + not in ("false", "0", "no", "off") + ) + else: + litellm.user_url_validation = bool(_user_url_validation) _user_url_allowed_hosts = general_settings.get( "user_url_allowed_hosts", None ) diff --git a/tests/proxy_unit_tests/test_proxy_config_unit_test.py b/tests/proxy_unit_tests/test_proxy_config_unit_test.py index faa60636886..08683c79eb1 100644 --- a/tests/proxy_unit_tests/test_proxy_config_unit_test.py +++ b/tests/proxy_unit_tests/test_proxy_config_unit_test.py @@ -360,13 +360,77 @@ async def test_general_settings_url_validation_wired_to_litellm(): config_file_path=temp_file_path, ) - assert litellm.user_url_validation is False, ( - "user_url_validation from general_settings should set litellm.user_url_validation" - ) - assert litellm.user_url_allowed_hosts == ["10.80.1.20", "internal.corp"], ( - "user_url_allowed_hosts from general_settings should set litellm.user_url_allowed_hosts" - ) + assert ( + litellm.user_url_validation is False + ), "user_url_validation from general_settings should set litellm.user_url_validation" + assert litellm.user_url_allowed_hosts == [ + "10.80.1.20", + "internal.corp", + ], "user_url_allowed_hosts from general_settings should set litellm.user_url_allowed_hosts" finally: os.unlink(temp_file_path) litellm.user_url_validation = original_validation litellm.user_url_allowed_hosts = original_hosts + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "raw_value,expected", + [ + ("false", False), + ("False", False), + ("FALSE", False), + ("0", False), + ("no", False), + ("off", False), + ("true", True), + ("True", True), + ("1", True), + ("yes", True), + ], +) +async def test_general_settings_url_validation_string_value(raw_value, expected): + """ + A quoted string value for user_url_validation in YAML must be interpreted + as a boolean rather than always coerced to True via bool(str). Otherwise + operators who write `user_url_validation: "false"` silently keep + validation enabled. + """ + import tempfile + + import yaml + + config_content = { + "model_list": [ + { + "model_name": "test-model", + "litellm_params": {"model": "openai/gpt-4", "api_key": "test-key"}, + } + ], + "general_settings": { + "user_url_validation": raw_value, + }, + } + + with tempfile.NamedTemporaryFile( + mode="w", suffix=".yaml", delete=False + ) as temp_file: + yaml.dump(config_content, temp_file) + temp_file_path = temp_file.name + + original_validation = litellm.user_url_validation + + try: + proxy_config = ProxyConfig() + await proxy_config.load_config( + router=None, + config_file_path=temp_file_path, + ) + + assert litellm.user_url_validation is expected, ( + f"user_url_validation={raw_value!r} should resolve to {expected}, " + f"got {litellm.user_url_validation!r}" + ) + finally: + os.unlink(temp_file_path) + litellm.user_url_validation = original_validation