T09 · Insecure Skill Coding Practices
- Location
scripts/mixtiles-cart.py:56- Finding
SSRF Protection Can Be Bypassed Through Redirects or DNS Rebinding
- Content
View full analysis
None: """Block private/internal URLs to prevent SSRF.""" import ipaddress, socket parsed = urllib.parse.urlparse(url) if parsed.scheme not in ("http", "https"): raise ValueError(f"Unsupported scheme: {parsed.scheme}") hostname = parsed.hostname or "" if not hostname: raise ValueError("No hostname in URL") # Block obvious internal hostnames blocked = ("localhost", "127.0.0.1", "0.0.0.0", "metadata.google.internal", "169.254.169.254") if hostname.lower() in blocked or hostname.lower().endswith(".local"): raise ValueError(f"Blocked internal hostname: {hostname}") # Resolve and check for private IPs try: for info in socket.getaddrinfo(hostname, None): addr = ipaddress.ip_address(info[4][0]) if addr.is_private or addr.is_loopback or addr.is_link_local or addr.is_reserved: raise ValueError(f"URL resolves to private/reserved IP: {addr}") except socket.gaierror: raise ValueError(f"Cannot resolve hostname: {hostname}") def download_to_temp(source_url: str) -> str: """Download a remote image to a temp file. Returns the temp file path.""" import tempfile _validate_url(source_url) req = urllib.request.Request(source_url, headers={ "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)" }) with urllib.request.urlopen(req, timeout=30) as resp: ``` ### Technical Analysis The function validates the hostname and its resolved addresses before passing the original URL to `urllib.request.urlopen`. This creates a time-of-check/time-of-use separation between validation and connection. `urlopen` can automatically follow HTTP redirects, but the redirect destination is not passed th ...[truncated 2230 chars]- Remediation
View remediation
