{"record":{"id":"6c17dd750b60011e","repo":"HumanSignal/label-studio","slug":"url-resolves-to-a-reserved-network-address-block","errorCode":null,"errorMessage":"URL resolves to a reserved network address (block: {subnet})","messagePattern":"URL resolves to a reserved network address \\(block: (.+?)\\)","errorType":"exception","errorClass":"SsrfBlockedUrlError","httpStatus":null,"severity":"warning","filePath":"label_studio/core/utils/io.py","lineNumber":265,"sourceCode":"        '64:ff9b:1::/48',  # IPv4/IPv6 translation\n        '100::/64',  # discard prefix\n        '2001:0000::/32',  # Teredo tunneling\n        '2001:20::/28',  # ORCHIDv2\n        '2001:db8::/32',  # documentation\n        '2002::/16',  # 6to4\n        'fc00::/7',  # unique local\n        'fe80::/10',  # link-local\n        'ff00::/8',  # multicast\n    ]\n\n    banned_subnets = [\n        *(default_banned_subnets if settings.USE_DEFAULT_BANNED_SUBNETS else []),\n        *(settings.USER_ADDITIONAL_BANNED_SUBNETS or []),\n    ]\n\n    for subnet in banned_subnets:\n        if ipaddress.ip_address(ip) in ipaddress.ip_network(subnet):\n            raise SsrfBlockedUrlError(f'URL resolves to a reserved network address (block: {subnet})')\n\n\ndef ssrf_safe_request(method, url, *args, **kwargs):\n    block_local_urls = kwargs.pop('block_local_urls', settings.SSRF_PROTECTION_ENABLED)\n    validate_url_for_ssrf(url, block_local_urls=block_local_urls)\n    # Reason for #nosec: url has been validated as SSRF safe by the\n    # validation check above.\n    response = requests.request(method, url, *args, **kwargs)  # nosec\n\n    # second check for SSRF for prevent redirect and dns rebinding attacks\n    if block_local_urls:\n        try:\n            response_ip = response.raw._connection.sock.getpeername()[0]\n            validate_ip(response_ip)\n        except (AttributeError, TypeError, ValueError):\n            # Some adapters/mocks don't expose socket details.\n            pass\n    return response","sourceCodeStart":247,"sourceCodeEnd":283,"githubUrl":"https://github.com/HumanSignal/label-studio/blob/0b49e9b53917880baf1dd85d574fe5541a9aafb2/label_studio/core/utils/io.py#L247-L283","documentation":"As part of SSRF protection, validate_ip compares the resolved IP against a set of banned subnets (default reserved ranges plus optional USER_ADDITIONAL_BANNED_SUBNETS). If the IP falls in any banned subnet, SsrfBlockedUrlError is raised naming the offending subnet — the URL points at a private/reserved network address.","triggerScenarios":"Calling validate_ip via validate_url_for_ssrf or ssrf_safe_request with a URL that resolves to a reserved address (127.0.0.1, 10.x, 172.16-31.x, 192.168.x, 169.254.x, etc.), or to any subnet listed in USER_ADDITIONAL_BANNED_SUBNETS.","commonSituations":"Pointing Label Studio at internal storage/services (localhost MinIO, internal metadata endpoints) while SSRF protection is enabled; USER_ADDITIONAL_BANNED_SUBNETS overlapping a legit corporate range; hostname resolving to a private IP behind a proxy.","solutions":["Use a publicly reachable URL for the resource, or expose the internal service via a public/proxied endpoint.","If the target is legitimately internal and you accept the risk, disable SSRF protection via SSRF_PROTECTION_ENABLED=false (or pass block_local_urls=False where supported).","Remove or narrow the offending entry in USER_ADDITIONAL_BANNED_SUBNETS if it wrongly covers your target IP.","Check what IP the hostname resolves to (dig/nslookup) — you may be hitting an internal DNS record unintentionally."],"exampleFix":"// before\nssrf_safe_request('GET', 'http://127.0.0.1:9000/presign')  # blocked: loopback\n// after: use a hostname resolving to an allowed IP, or explicitly allow\nssrf_safe_request('GET', 'http://storage.internal.example.com:9000/presign')  # or SSRF_PROTECTION_ENABLED=false","handlingStrategy":"validation","validationCode":"import ipaddress, socket\nfrom urllib.parse import urlparse\ndef is_publicly_routable(url: str) -> bool:\n    ip = ipaddress.ip_address(socket.gethostbyname(urlparse(url).hostname))\n    return not (ip.is_private or ip.is_loopback or ip.is_link_local or ip.is_reserved)","typeGuard":"def url_ip_not_banned(url: str, banned: list[str]) -> bool:\n    ip = ipaddress.ip_address(socket.gethostbyname(urlparse(url).hostname))\n    return not any(ip in ipaddress.ip_network(s) for s in banned)","tryCatchPattern":"try:\n    ssrf_safe_request('GET', url)\nexcept SsrfBlockedUrlError as e:\n    logger.warning('Blocked SSRF-target URL %s: %s', url, e)\n    return None","preventionTips":["Only pass externally reachable URLs to upload/validation APIs.","Keep USER_ADDITIONAL_BANNED_SUBNETS minimal and reviewed.","Understand your SSRF_PROTECTION_ENABLED default before pointing at internal services.","Resolve hostnames ahead of time to know what IP will be checked."],"tags":["network","security","ssrf","ip-address"],"backgroundTag":"ssrf-blocked-url","analyzedSha":"0b49e9b53917880baf1dd85d574fe5541a9aafb2","analyzedAt":"2026-08-29T00:39:52.578Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}