{"record":{"id":"68acc49e7db4b369","repo":"affaan-m/ECC","slug":"remote-import-host-resolves-to-a-non-public-addres","errorCode":null,"errorMessage":"remote import host resolves to a non-public address: {host}","messagePattern":"remote import host resolves to a non-public address: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"skills/continuous-learning-v2/scripts/instinct-cli.py","lineNumber":216,"sourceCode":"        addr_infos = socket.getaddrinfo(parsed.hostname, parsed.port or 443, type=socket.SOCK_STREAM)\n    except socket.gaierror as exc:\n        raise ValueError(f\"remote import host could not be resolved: {parsed.hostname}\") from exc\n\n    for family, _, _, _, sockaddr in addr_infos:\n        host = sockaddr[0]\n        try:\n            ip = ipaddress.ip_address(host)\n        except ValueError:\n            continue\n        if (\n            ip.is_private\n            or ip.is_loopback\n            or ip.is_link_local\n            or ip.is_multicast\n            or ip.is_reserved\n            or ip.is_unspecified\n        ):\n            raise ValueError(f\"remote import host resolves to a non-public address: {host}\")\n\n    return urllib.parse.urlunparse(parsed)\n\n\ndef _fetch_import_url(source: str, *, max_bytes: int = 2 * 1024 * 1024) -> str:\n    \"\"\"Fetch a validated remote instinct file with bounded size and timeout.\"\"\"\n    url = _validate_import_url(source)\n    req = urllib.request.Request(url, headers={\"User-Agent\": \"ECC-instinct-import/2\"})\n    with urllib.request.urlopen(req, timeout=15) as response:\n        content_type = response.headers.get(\"Content-Type\", \"\")\n        if content_type and not any(\n            allowed in content_type.lower()\n            for allowed in (\"text/\", \"markdown\", \"yaml\", \"json\", \"octet-stream\")\n        ):\n            raise ValueError(f\"unsupported remote content type: {content_type}\")\n        data = response.read(max_bytes + 1)\n    if len(data) > max_bytes:\n        raise ValueError(f\"remote import exceeds {max_bytes} bytes\")","sourceCodeStart":198,"sourceCodeEnd":234,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/continuous-learning-v2/scripts/instinct-cli.py#L198-L234","documentation":"Raised by _validate_import_url after DNS resolution when any resolved address for the hostname is private, loopback, link-local, multicast, reserved, or unspecified. This is an SSRF guard: the CLI refuses to fetch URLs whose host resolves into internal network space. It runs on every address returned by getaddrinfo, so one bad record is enough to fail.","triggerScenarios":"Importing from https://localhost/..., https://127.0.0.1/..., https://10.x.x.x/..., https://192.168.x.x/..., https://169.254.169.254/ (cloud metadata), or a public-looking hostname that resolves to a private IP (DNS rebinding or split-horizon DNS).","commonSituations":"Testing against a local mock server that must now be exposed differently; internal-only URLs that were never valid for this CLI; hostnames whose DNS returns private addresses in an office network.","solutions":["Use a genuinely public HTTPS endpoint for the import.","For local testing, host the file on a real public URL (e.g. a gist or public bucket) rather than localhost.","Check what the hostname resolves to (dig/nslookup) and switch to a hostname with public records.","If this is a false positive from split-horizon DNS, import the file locally via the file import path instead."],"exampleFix":"# before\nurl = \"http://169.254.169.254/latest/meta-data\"\n# after\nurl = \"https://raw.githubusercontent.com/org/repo/main/instincts.yaml\"","handlingStrategy":"validation","validationCode":"import ipaddress, socket\ninfos = socket.getaddrinfo(hostname, 443)\nfor info in infos:\n    ip = ipaddress.ip_address(info[4][0])\n    if not ip.is_global:\n        raise ValueError(f\"non-public address: {ip}\")","typeGuard":null,"tryCatchPattern":"try:\n    cli_import(url=source)\nexcept ValueError as e:\n    if \"non-public address\" in str(e):\n        print(\"host resolves to a private/internal IP; use a public URL\")","preventionTips":["Use public endpoints (raw.githubusercontent, public buckets) for imports","Never import from localhost/127.0.0.1/169.254.x links","Check DNS resolution if a public hostname unexpectedly resolves privately","Treat SSRF guards as intentional; route local testing through public staging"],"tags":["python","security","ssrf","network"],"backgroundTag":"invalid-url","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}