[SECURITY] Validate CDP URLs to Prevent SSRF (V-010, CVSS 8.4) #65

Merged
allegro merged 1 commits from security/fix-browser-cdp into main 2026-03-30 23:57:28 +00:00

View File

@@ -170,6 +170,9 @@ def _resolve_cdp_override(cdp_url: str) -> str:
For discovery-style endpoints we fetch /json/version and return the
webSocketDebuggerUrl so downstream tools always receive a concrete browser
websocket instead of an ambiguous host:port URL.
SECURITY FIX (V-010): Validates URLs before fetching to prevent SSRF.
Only allows localhost/private network addresses for CDP connections.
"""
raw = (cdp_url or "").strip()
if not raw:
@@ -191,6 +194,35 @@ def _resolve_cdp_override(cdp_url: str) -> str:
else:
version_url = discovery_url.rstrip("/") + "/json/version"
# SECURITY FIX (V-010): Validate URL before fetching
# Only allow localhost and private networks for CDP
from urllib.parse import urlparse
parsed = urlparse(version_url)
hostname = parsed.hostname or ""
# Allow only safe hostnames for CDP
allowed_hostnames = ["localhost", "127.0.0.1", "0.0.0.0", "::1"]
if hostname not in allowed_hostnames:
# Check if it's a private IP
try:
import ipaddress
ip = ipaddress.ip_address(hostname)
if not (ip.is_private or ip.is_loopback):
logger.error(
"SECURITY: Rejecting CDP URL '%s' - only localhost and private "
"networks are allowed to prevent SSRF attacks.",
raw
)
return raw # Return original without fetching
except ValueError:
# Not an IP - reject unknown hostnames
logger.error(
"SECURITY: Rejecting CDP URL '%s' - unknown hostname '%s'. "
"Only localhost and private IPs are allowed.",
raw, hostname
)
return raw
try:
response = requests.get(version_url, timeout=10)
response.raise_for_status()