{"record":{"id":"c4d5f571030a57d7","repo":"docling-project/docling","slug":"access-to-restricted-ip-address-not-allowed-ip","errorCode":null,"errorMessage":"Access to restricted IP address not allowed: {ip}","messagePattern":"Access to restricted IP address not allowed: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"docling/backend/utils/image_resource_loader.py","lineNumber":76,"sourceCode":"    except ValueError:\n        try:\n            ip_str = socket.gethostbyname(hostname)\n            ip = ipaddress.ip_address(ip_str)\n        except (socket.gaierror, socket.herror) as e:\n            raise ValueError(f\"Cannot resolve hostname: {hostname}\") from e\n\n    if not (\n        ip.is_global\n        and not (\n            ip.is_private\n            or ip.is_loopback\n            or ip.is_link_local\n            or ip.is_reserved\n            or ip.is_multicast\n            or ip.is_unspecified\n        )\n    ):\n        raise ValueError(f\"Access to restricted IP address not allowed: {ip}\")\n\n\nclass ImageResourceLoader:\n    \"\"\"Resolve and load image resources for declarative document backends.\n\n    The ``base_path`` against which relative locations are resolved is supplied\n    per call rather than stored, so a backend that mutates its base path between\n    calls always uses the current value.\n    \"\"\"\n\n    def __init__(\n        self,\n        *,\n        enable_local_fetch: bool = False,\n        enable_remote_fetch: bool = False,\n        max_image_data_base64_bytes: int = 20 * 1024 * 1024,\n        max_remote_image_bytes: int = 20 * 1024 * 1024,\n        max_redirects: int = 5,","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/docling-project/docling/blob/61d76f1ff3f8428065465889f7b4577da7df704c/docling/backend/utils/image_resource_loader.py#L58-L94","documentation":"ValueError raised at the end of validate_url_safety when the resolved IP fails the allowlist: it must be is_global and must not be private, loopback, link-local, reserved, multicast, or unspecified. This blocks SSRF attempts (and accidental internal requests) toward 127.0.0.1, 10.x, 192.168.x, 169.254.x (cloud metadata), ::1, etc., including hostnames that resolve to those ranges.","triggerScenarios":"A document image URL points to a private/internal address: http://10.0.0.5/chart.png, http://localhost:8080/x.png, a hostname resolving into RFC1918 space, or the cloud metadata IP 169.254.169.254. Requires enable_remote_fetch=True to be reached.","commonSituations":"Converting intranet HTML on a host where image hosts resolve privately; penetration-test payloads in uploaded HTML; localhost dev servers referenced as image sources.","solutions":["Do not fetch internal resources remotely: pre-download the images and enable local loading with a base_path instead.","Expose the internal image host through a public, proxied URL that the runner can reach legitimately.","If you truly need internal fetches, mirror the images to a location with a global IP rather than disabling the guard.","Treat this error on user-uploaded documents as a suspected SSRF attempt and log/alert."],"exampleFix":"# before\nloader = ImageResourceLoader(enable_remote_fetch=True)\ndata = loader.load_image_data('http://169.254.169.254/latest/meta-data', base)\n# ValueError: restricted IP\n\n# after (pre-mirror assets locally)\nloader = ImageResourceLoader(enable_local_fetch=True)\ndata = loader.load_image_data('images/chart.png', '/srv/mirror/report.html')","handlingStrategy":"validation","validationCode":"import ipaddress, socket\nfrom urllib.parse import urlparse\nhost = urlparse(url).hostname\nip = ipaddress.ip_address(host) if ':' in host else ipaddress.ip_address(socket.gethostbyname(host))\nassert ip.is_global and not (ip.is_private or ip.is_loopback or ip.is_link_local or ip.is_reserved or ip.is_multicast or ip.is_unspecified), 'restricted IP'","typeGuard":null,"tryCatchPattern":"try:\n    data = loader.load_image_data(src, base)\nexcept ValueError as e:\n    if 'restricted IP' in str(e):\n        alert_security(f'possible SSRF attempt: {src}')\n        data = None\n    else:\n        raise","preventionTips":["Keep remote fetch disabled unless required; mirror internal assets locally","Treat restricted-IP hits on user-uploaded docs as SSRF attempts and alert","Pre-resolve and allowlist external image hosts when operating in hybrid networks"],"tags":["ssrf","security","network","images"],"backgroundTag":null,"analyzedSha":"61d76f1ff3f8428065465889f7b4577da7df704c","analyzedAt":"2026-08-14T23:53:18.727Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}