{"record":{"id":"7b688811ce76db9d","repo":"Graphify-Labs/graphify","slug":"blocked-cloud-metadata-endpoint-hostname-got","errorCode":null,"errorMessage":"Blocked cloud metadata endpoint '{hostname}'. Got: {url!r}","messagePattern":"Blocked cloud metadata endpoint '(.+?)'\\. Got: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"graphify/security.py","lineNumber":122,"sourceCode":"    \"\"\"Raise ValueError if *url* is not http or https, or targets a private/internal IP.\n\n    Blocks file://, ftp://, data:, and any other scheme that could be used\n    for SSRF or local file access. Also blocks requests to private/reserved\n    IP ranges (127.x, 10.x, 169.254.x, etc.) and cloud metadata endpoints\n    to prevent SSRF in cloud environments.\n    \"\"\"\n    parsed = urllib.parse.urlparse(url)\n    if parsed.scheme.lower() not in _ALLOWED_SCHEMES:\n        raise ValueError(\n            f\"Blocked URL scheme '{parsed.scheme}' - only http and https are allowed. \"\n            f\"Got: {url!r}\"\n        )\n\n    hostname = parsed.hostname\n    if hostname:\n        # Block known cloud metadata hostnames\n        if hostname.lower() in _BLOCKED_HOSTS:\n            raise ValueError(\n                f\"Blocked cloud metadata endpoint '{hostname}'. \"\n                f\"Got: {url!r}\"\n            )\n\n        # Resolve hostname and block private/reserved IP ranges\n        try:\n            infos = socket.getaddrinfo(hostname, None, socket.AF_UNSPEC, socket.SOCK_STREAM)\n            for info in infos:\n                addr = info[4][0]\n                ip = ipaddress.ip_address(addr)\n                if _ip_is_blocked(ip):\n                    raise ValueError(\n                        f\"Blocked private/internal IP {addr} (resolved from '{hostname}'). \"\n                        f\"Got: {url!r}\"\n                    )\n        except socket.gaierror as exc:\n            raise ValueError(\n                f\"DNS resolution failed for '{hostname}': {exc}. Got: {url!r}\"","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/Graphify-Labs/graphify/blob/7fe58b0b0f3873be9a21c30106b8b8527c353aa6/graphify/security.py#L104-L140","documentation":"ValueError from validate_url when the URL's hostname is in _BLOCKED_HOSTS - known cloud metadata endpoint names (e.g. metadata.google.internal). This is tier two of the SSRF guard: even with an http(s) scheme, metadata hostnames are refused before DNS resolution or connection.","triggerScenarios":"validate_url(url) where parsed.hostname.lower() is in the _BLOCKED_HOSTS set (security.py:118-122). Any http://metadata.google.internal/... style URL from user input or config hits this immediately.","commonSituations":"SSRF probing of an app that accepts URLs (attack traffic); misconfigured service templates pointing at metadata names; testing scripts copied from cloud docs that use metadata hostnames as examples; proxy configs that forward metadata names.","solutions":["If this is your own service: treat the block as correct - do not proxy metadata endpoints; remove the URL from config.","If triggered by user input: return a 4xx and log the attempt (potential SSRF probe).","For cloud config that genuinely needs metadata, use the cloud SDK/IMDS client on the instance itself, not the URL fetcher."],"exampleFix":"# before\nurl = cfg['health_check_url']   # 'http://metadata.google.internal/computeMetadata/v1/'\nfetch(url)                       # ValueError: Blocked cloud metadata endpoint\n\n# after\nurl = 'https://real-service.internal/healthz'   # use a routable endpoint\nfetch(validate_url(url))","handlingStrategy":"validation","validationCode":"from urllib.parse import urlparse\n\nBLOCKED = {\"metadata.google.internal\", \"instance-data\"}  # mirror your guard's set\nif (urlparse(url).hostname or \"\").lower() in BLOCKED:\n    raise HTTPBadRequest(\"metadata endpoints are not allowed\")","typeGuard":"def is_safe_url(url: str) -> bool:\n    try:\n        validate_url(url)\n        return True\n    except ValueError:\n        return False","tryCatchPattern":"try:\n    safe = validate_url(url)\nexcept ValueError as exc:\n    if \"Blocked cloud metadata\" in str(exc):\n        log.security(\"SSRF attempt? url=%r\", url)\n        return forbidden(str(exc))\n    raise","preventionTips":["Audit URL-accepting endpoints for metadata-hostname handling; blocks like this should return 4xx with audit logs.","Keep the blocked-host list in sync with your cloud provider's metadata service names.","Never disable SSRF guards to 'unblock' internal tooling - restructure the call instead."],"tags":["security","ssrf","cloud","url","validation"],"backgroundTag":null,"analyzedSha":"7fe58b0b0f3873be9a21c30106b8b8527c353aa6","analyzedAt":"2026-08-14T19:23:21.323Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}