diff --git a/appguardrail_core/controlplane.py b/appguardrail_core/controlplane.py index 576b990f..3dfef099 100644 --- a/appguardrail_core/controlplane.py +++ b/appguardrail_core/controlplane.py @@ -236,6 +236,8 @@ def _is_safe_url(url: str) -> bool: return False host = (parsed.hostname or "").lower() + if not host: + return False raw = host.split("%", 1)[0].strip("[]") def is_bad_ip(ip) -> bool: diff --git a/tests/test_controlplane.py b/tests/test_controlplane.py index 830f9693..fdba0647 100644 --- a/tests/test_controlplane.py +++ b/tests/test_controlplane.py @@ -505,3 +505,12 @@ def test_negative_content_length_rejected(server): resp = conn.getresponse() assert resp.status == 400 conn.close() + +def test_api_set_webhook_ssrf_protection_empty_host(server): + base, key = server + # Invalid empty host + for invalid_url in ["http://", "http://user:pass@", "https://"]: + with pytest.raises(urllib.error.HTTPError) as exc: + _req("POST", f"{base}/api/v1/webhook", key, {"url": invalid_url}) + assert exc.value.code == 400 + assert json.loads(exc.value.read())["error"] == "invalid webhook url"