"""Safe HTTP client: open URLs only for allowed schemes (e.g. https, http) to avoid path traversal (CWE-22).""" from urllib.request import Request, urlopen from urllib.parse import urlparse def safe_urlopen( request: Request, timeout: float = 10, allowed_schemes: tuple[str, ...] = ("https", "http"), ) -> "urlopen": """Open URL only if its scheme is in allowed_schemes; otherwise raise ValueError. Use this instead of raw urlopen() to satisfy Bandit B310 and prevent opening file:// or other non-HTTP schemes. Args: request: urllib.request.Request (has .full_url or .get_full_url()). timeout: Timeout in seconds for the request. allowed_schemes: Tuple of lowercase scheme names, e.g. ("https", "http"). Returns: Context manager from urlopen (use with 'with'). Raises: ValueError: If the request URL scheme is not in allowed_schemes. """ url = request.get_full_url() parsed = urlparse(url) scheme = (parsed.scheme or "").lower() if scheme not in allowed_schemes: raise ValueError( f"URL scheme not allowed: {scheme!r} (allowed: {allowed_schemes})" ) # Scheme validated above; only https/http reach here (B310). return urlopen(request, timeout=timeout) # nosec B310