{"record":{"id":"723914c6273ceadf","repo":"Graphify-Labs/graphify","slug":"blocked-url-scheme-parsed-scheme-only-http-a","errorCode":null,"errorMessage":"Blocked URL scheme '{parsed.scheme}' - only http and https are allowed. Got: {url!r}","messagePattern":"Blocked URL scheme '(.+?)' - only http and https are allowed\\. Got: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"graphify/security.py","lineNumber":113,"sourceCode":"        ip.is_private\n        or ip.is_reserved\n        or ip.is_loopback\n        or ip.is_link_local\n        or ip in _CGN_NETWORK\n    )\n\n\ndef validate_url(url: str) -> str:\n    \"\"\"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]","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/Graphify-Labs/graphify/blob/7fe58b0b0f3873be9a21c30106b8b8527c353aa6/graphify/security.py#L95-L131","documentation":"ValueError from validate_url (security.py) when the URL's scheme is not http/https. This is the first tier of graphify's SSRF protection: file://, ftp://, data:, gopher: and everything else is rejected before any DNS or connection work, with the offending URL echoed.","triggerScenarios":"validate_url(url) where urllib.parse.urlparse reports a scheme outside _ALLOWED_SCHEMES (security.py:110-115) - e.g. 'file:///etc/passwd', 'ftp://host/x', 'data:text/html,...', or a scheme-less 'example.com/path' (parsed scheme ''), which also fails since '' is not allowed.","commonSituations":"User-supplied URLs from tickets/config passed unchecked; copy-paste of paths without the https:// prefix; attempts to point fetchers at local files; protocol-handler confusion from frontend forms that strip the scheme.","solutions":["Normalize input: if no scheme, deliberately prepend 'https://' before validation - but never blanket-append schemes to untrusted input without review.","If the file is genuinely local, use local-file APIs instead of the URL fetcher - the block is intentional.","Reject early in your own UI/config layer with a clear message so users fix the URL at entry time."],"exampleFix":"# before\nurl = request.form['url']          # user sent 'example.com/page'\nvalidate_url(url)                   # ValueError: Blocked URL scheme ''\n\n# after - require an explicit scheme, default deliberately\nurl = request.form['url'].strip()\nif '://' not in url:\n    url = 'https://' + url          # only if defaulting is acceptable for your app\nurl = validate_url(url)","handlingStrategy":"validation","validationCode":"from urllib.parse import urlparse\n\ndef has_allowed_scheme(url: str) -> bool:\n    return urlparse(url).scheme.lower() in (\"http\", \"https\")\n\nif not has_allowed_scheme(url):\n    raise HTTPBadRequest(\"URL must start with http:// or https://\")","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 URL scheme\" in str(exc):\n        return bad_request(str(exc))  # user error - surface, do not log as 500\n    raise","preventionTips":["Validate URLs at the trust boundary (form/API entry), not deep in fetch code.","Never relax the scheme allowlist for 'just one' internal scheme - use dedicated local-file paths instead.","Echo the URL in errors (as this code does) so users can see what was parsed."],"tags":["security","ssrf","url","validation"],"backgroundTag":null,"analyzedSha":"7fe58b0b0f3873be9a21c30106b8b8527c353aa6","analyzedAt":"2026-08-14T19:23:21.323Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}