{"record":{"id":"00b816b3e4a75dd2","repo":"sgl-project/sglang","slug":"invalid-media-url-url-r","errorCode":null,"errorMessage":"Invalid media URL: {url!r}","messagePattern":"Invalid media URL: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/srt/utils/common.py","lineNumber":1578,"sourceCode":"    checks apply to image, video, audio, cache, and model-specific loaders.\n    \"\"\"\n\n    if max_file_size_mb < 0:\n        raise ValueError(\"media_url_max_file_size_mb must be non-negative\")\n\n    normalized_domains = sorted(\n        {_normalize_media_domain(domain) for domain in allowed_media_domains or []}\n    )\n    global _allowed_media_domains, _media_url_max_file_size_bytes\n    _allowed_media_domains = frozenset(normalized_domains)\n    _media_url_max_file_size_bytes = max_file_size_mb * 1024 * 1024\n    return normalized_domains\n\n\ndef _assert_media_url_allowed(url: str) -> None:\n    parsed = urlparse(url)\n    if parsed.scheme not in {\"http\", \"https\"} or parsed.hostname is None:\n        raise ValueError(f\"Invalid media URL: {url!r}\")\n\n    hostname = _normalize_media_domain(parsed.hostname)\n    if _allowed_media_domains and hostname not in _allowed_media_domains:\n        raise ValueError(\n            \"Media URL domain is not allowed. \"\n            f\"Allowed domains: {sorted(_allowed_media_domains)}; \"\n            f\"input domain: {hostname}\"\n        )\n\n\ndef download_remote_media(url: str, timeout: float) -> bytes:\n    \"\"\"Download one HTTP(S) media object under the configured URL policy.\n\n    Redirects are followed manually so every destination is validated before\n    a connection is made. The response is streamed to enforce both the total\n    request deadline and the configured byte limit without first buffering an\n    attacker-controlled body in memory.\n    \"\"\"","sourceCodeStart":1560,"sourceCodeEnd":1596,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/utils/common.py#L1560-L1596","documentation":"download_remote_media's URL gate (_assert_media_url_allowed) only accepts http/https URLs with a parseable hostname. FTP/data/file scheme URLs, or relative/garbage strings with no hostname, are rejected before any network request to prevent SSRF and unsupported-protocol issues.","triggerScenarios":"Passing 'ftp://x/file.png', 'file:///tmp/a.jpg', 'data:image/png;base64,...', or 'not-a-url' to download_remote_media (via fetch_image, preprocess_audio, load_audio, etc.) when it is not routed to a local/base64 path first.","commonSituations":"Multimodal pipelines forwarding arbitrary user-supplied strings to the remote downloader; base64 payloads not being detected earlier; clients sending file:// paths hoping to read server-local files (SSRF/LFI attempt).","solutions":["Handle local paths and base64/data URIs before calling download_remote_media","Reject or URL-decode user input into the expected http(s) form client-side","Keep the allowlist configured so accidental non-http inputs are caught"],"exampleFix":"# before\nmedia = download_remote_media('data:image/png;base64,iVBOR...')\n# after\nif image.startswith('data:'): data = pybase64.b64decode(image.split(',', 1)[1])\nelse: data = download_remote_media(image)","handlingStrategy":"validation","validationCode":"from urllib.parse import urlparse\np = urlparse(url)\nassert p.scheme in ('http', 'https') and p.hostname, f'bad media url {url!r}'\ndata = download_remote_media(url, timeout=30)","typeGuard":"def is_http_url(v) -> bool:\n    if not isinstance(v, str): return False\n    p = urlparse(v)\n    return p.scheme in {'http', 'https'} and p.hostname is not None","tryCatchPattern":null,"preventionTips":["Route data:/file:/base64 inputs through their dedicated paths first","Reject non-http schemes at the request-validation layer"],"tags":["media","security","ssrf","url-validation"],"backgroundTag":"url-validation-failed","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}