mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-24 00:52:24 +00:00
fix(ssrf): point the blocked-address remediation at litellm_settings (#42508)
* fix(ssrf): point the blocked-address remediation at litellm_settings Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * test(ssrf): pin the block message's named section to litellm_settings Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * test(compaction): pin litellm.max_budget so leaked proxy budget cannot 401 the child auth Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: shivam <shivam@berri.ai> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
b73696a15d
commit
0e6a288458
5 changed files with 33 additions and 3 deletions
|
|
@ -82,7 +82,7 @@ def _rejected_image_fetch(url: str, verdict: SSRFError) -> "litellm.ImageFetchEr
|
|||
verbose_logger.warning("Image fetch of %s rejected before any request went out: %s", url, verdict)
|
||||
return litellm.ImageFetchError(
|
||||
"Error: Unable to fetch image from URL. The proxy could not resolve this host or its URL policy rejected it; "
|
||||
f"an admin can check the proxy log and `user_url_allowed_hosts` in general_settings. url={url}"
|
||||
f"an admin can check the proxy log and `user_url_allowed_hosts` in litellm_settings. url={url}"
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -336,7 +336,7 @@ def validate_url(url: str) -> tuple[str, str]:
|
|||
raise SSRFError(
|
||||
f"URL targets a blocked address ({resolved_ip}). "
|
||||
"If this is a legitimate internal service, add the host "
|
||||
"to `user_url_allowed_hosts` in general_settings."
|
||||
"to `user_url_allowed_hosts` in litellm_settings."
|
||||
)
|
||||
|
||||
# For HTTPS with SSL verification enabled, TLS certificate validation
|
||||
|
|
|
|||
|
|
@ -427,7 +427,7 @@ async def test_async_inline_remote_media_cancels_the_other_fetches_when_one_fail
|
|||
_SSRF_VERDICTS = (
|
||||
SSRFError(
|
||||
"URL targets a blocked address (10.0.0.8). If this is a legitimate internal service, "
|
||||
"add the host to `user_url_allowed_hosts` in general_settings."
|
||||
"add the host to `user_url_allowed_hosts` in litellm_settings."
|
||||
),
|
||||
SSRFError("DNS resolution failed for 'internal.example': [Errno 8] nodename nor servname provided, or not known"),
|
||||
SSRFError("No addresses found for 'internal.example'"),
|
||||
|
|
|
|||
|
|
@ -2198,6 +2198,34 @@ async def test_ProxyConfig_load_config_wires_general_settings_url_validation(tmp
|
|||
litellm.provider_url_destination_allowed_hosts = original_provider_hosts
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ssrf_block_message_names_a_config_section_load_config_honors(tmp_path, monkeypatch):
|
||||
"""Regression for LIT-8349: the remediation in the SSRF block message must point at a section that works."""
|
||||
from litellm.litellm_core_utils.url_utils import SSRFError, validate_url
|
||||
|
||||
monkeypatch.setattr(litellm, "user_url_allowed_hosts", [])
|
||||
monkeypatch.setattr(litellm, "user_url_validation", True)
|
||||
with pytest.raises(SSRFError) as blocked:
|
||||
validate_url("http://10.96.3.245:10002/agent.json")
|
||||
section_match = re.search(r"add the host to `user_url_allowed_hosts` in (\w+)\.", str(blocked.value))
|
||||
assert section_match is not None, str(blocked.value)
|
||||
section: Final = section_match.group(1)
|
||||
assert section == "litellm_settings", f"block message points admins at {section}, which the docs contradict"
|
||||
|
||||
f = tmp_path / "c.yaml"
|
||||
f.write_text(f"model_list: []\n{section}:\n user_url_allowed_hosts:\n - '10.96.3.245:10002'\n")
|
||||
monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", None)
|
||||
monkeypatch.setattr("litellm.proxy.proxy_server.store_model_in_db", False)
|
||||
monkeypatch.delenv("LITELLM_CONFIG_BUCKET_NAME", raising=False)
|
||||
await ProxyConfig().load_config(router=None, config_file_path=str(f))
|
||||
|
||||
assert litellm.user_url_allowed_hosts == ["10.96.3.245:10002"], f"{section} did not apply the allowlist"
|
||||
assert validate_url("http://10.96.3.245:10002/agent.json") == (
|
||||
"http://10.96.3.245:10002/agent.json",
|
||||
"10.96.3.245:10002",
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ProxyConfig_load_config_wires_config_reload_interval(tmp_path, monkeypatch):
|
||||
"""general_settings.proxy_config_reload_interval_seconds must reach the proxy_server
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import pytest
|
|||
from fastapi import FastAPI, Request
|
||||
from pydantic import TypeAdapter
|
||||
|
||||
import litellm
|
||||
from litellm.caching.caching import DualCache
|
||||
from litellm.exceptions import BadRequestError
|
||||
from litellm.litellm_core_utils.initialize_dynamic_callback_params import inherit_message_logging_privacy
|
||||
|
|
@ -112,6 +113,7 @@ async def test_real_proxy_child_auth_privacy_and_body_policy(
|
|||
})))
|
||||
return asyncio.sleep(0, result=ModelResponse(id="private-summary", model="compactor"))
|
||||
|
||||
monkeypatch.setattr(litellm, "max_budget", 0)
|
||||
monkeypatch.setattr(proxy_server.app, "dependency_overrides", {})
|
||||
monkeypatch.setattr(proxy_server, "master_key", "sk-master-fixture")
|
||||
monkeypatch.setattr(proxy_server, "prisma_client", object())
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue