{"record":{"id":"b80a68e7e4d3a1c3","repo":"iflytek/astron-agent","slug":"resolved-remote-resource-address-is-invalid","errorCode":null,"errorMessage":"Resolved remote resource address is invalid","messagePattern":"Resolved remote resource address is invalid","errorType":"exception","errorClass":"RemoteResourcePolicyError","httpStatus":null,"severity":"error","filePath":"core/plugin/aitools/common/clients/safe_download.py","lineNumber":68,"sourceCode":")\n\n\nclass RemoteResourcePolicyError(ValueError):\n    \"\"\"Raised when a caller-controlled download target is unsafe.\"\"\"\n\n\ndef create_public_socket_factory(\n    target_url: str,\n) -> Callable[[aiohttp.AddrInfoType], socket.socket]:\n    \"\"\"Validate the actual address selected by aiohttp before opening its socket.\"\"\"\n    _, allow_private_storage = _validate_resource_url(target_url)\n\n    def socket_factory(addr_info: aiohttp.AddrInfoType) -> socket.socket:\n        family, type_, proto, _, sockaddr = addr_info\n        try:\n            address = ipaddress.ip_address(sockaddr[0])\n        except ValueError as exc:\n            raise RemoteResourcePolicyError(\n                \"Resolved remote resource address is invalid\"\n            ) from exc\n        _validate_destination_address(\n            address,\n            allow_private_storage=allow_private_storage,\n        )\n        return socket.socket(family=family, type=type_, proto=proto)\n\n    return socket_factory\n\n\nasync def fetch_public_resource(\n    url: str,\n    span: Optional[SpanLike] = None,\n    *,\n    max_bytes: int = DEFAULT_MAX_DOWNLOAD_BYTES,\n) -> bytes:\n    \"\"\"Download a public or exact trusted-storage resource with SSRF checks.\"\"\"","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/iflytek/astron-agent/blob/5e758547a83371a5a4b29dadf4ac03e8dd527635/core/plugin/aitools/common/clients/safe_download.py#L50-L86","documentation":"socket_factory in safe_download.py implements SSRF protection: each DNS-resolved address is parsed with ipaddress.ip_address and then validated against the remote-resource policy (public addresses only, unless trusted storage is allowed). If the resolved address cannot be parsed as an IP, RemoteResourcePolicyError('Resolved remote resource address is invalid') is raised.","triggerScenarios":"fetch_public_resource() target URL resolves to a sockaddr whose first element is not a valid IP literal (e.g. unusual socket families like AF_UNIX, or malformed resolver output).","commonSituations":"DNS returning non-IPv4/IPv6 results, hostsfile/proxy oddities, or a URL whose host resolves through an exotic resolver (mDNS, .internal names).","solutions":["Use a URL with a hostname that resolves to a standard IPv4/IPv6 public address","Inspect DNS resolution of the host (dig/nslookup) for malformed or non-IP sockaddrs","Restrict downloads to conventional public hostnames"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"import ipaddress, socket\ninfos = socket.getaddrinfo(host, None)\nfor i in infos:\n    ipaddress.ip_address(i[4][0])  # must parse\n    if not ipaddress.ip_address(i[4][0]).is_global:\n        raise ValueError('non-public address')","typeGuard":"def resolves_to_public_ip(host: str) -> bool:\n    try:\n        return all(ipaddress.ip_address(i[4][0]).is_global for i in socket.getaddrinfo(host, None))\n    except (socket.gaierror, ValueError):\n        return False","tryCatchPattern":"try:\n    data = await fetch_public_resource(url)\nexcept RemoteResourcePolicyError as e:\n    logger.warning('download blocked: %s', e); data = None","preventionTips":["Only download from hostnames resolving to public IPv4/IPv6","Pre-resolve and validate addresses before invoking the downloader","Keep SSRF policy checks (allow_private_storage) enabled by default"],"tags":["security","ssrf","download","network"],"backgroundTag":"invalid-url","analyzedSha":"5e758547a83371a5a4b29dadf4ac03e8dd527635","analyzedAt":"2026-09-12T08:03:51.356Z","contentChangedAt":"2026-09-12T08:03:51.356Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}