{"record":{"id":"da007fbb4bd0cf0a","repo":"iflytek/astron-agent","slug":"outbound-hostname-is-invalid","errorCode":null,"errorMessage":"Outbound hostname is invalid","messagePattern":"Outbound hostname is invalid","errorType":"validation","errorClass":"OutboundPolicyError","httpStatus":null,"severity":"error","filePath":"core/plugin/link/infra/tool_exector/ssrf_guard.py","lineNumber":282,"sourceCode":"                \"PRIVATE_ENDPOINT_ALLOW_LIST entries must not include params or a query\"\n            )\n        endpoints.append(_endpoint(parsed))\n    return tuple(endpoints)\n\n\ndef _endpoint(parsed: SplitResult) -> Endpoint:\n    scheme, hostname, port = _origin(parsed.geturl())\n    return scheme, hostname, port, parsed.path or \"/\"\n\n\ndef _normalize_hostname(hostname: str) -> str:\n    value = hostname.strip().lower().rstrip(\".\")\n    if _parse_ip(value) is not None:\n        return value\n    try:\n        normalized = URL.build(scheme=\"http\", host=value).raw_host\n    except (TypeError, ValueError, UnicodeError) as exc:\n        raise OutboundPolicyError(\"Outbound hostname is invalid\") from exc\n    if not normalized:\n        raise OutboundPolicyError(\"Outbound hostname is invalid\")\n    return normalized.rstrip(\".\")\n\n\ndef _parse_ip(value: str) -> Union[IpAddress, None]:\n    try:\n        return ipaddress.ip_address(value)\n    except ValueError:\n        return None\n\n\ndef _matches_any(address: IpAddress, networks: Tuple[IpNetwork, ...]) -> bool:\n    candidates: Tuple[IpAddress, ...] = (address,)\n    if isinstance(address, ipaddress.IPv6Address) and address.ipv4_mapped:\n        candidates += (address.ipv4_mapped,)\n    return any(\n        candidate.version == network.version and candidate in network","sourceCodeStart":264,"sourceCodeEnd":300,"githubUrl":"https://github.com/iflytek/astron-agent/blob/5e758547a83371a5a4b29dadf4ac03e8dd527635/core/plugin/link/infra/tool_exector/ssrf_guard.py#L264-L300","documentation":"OutboundPolicyError from _normalize_hostname when the given hostname fails yarl's URL.build() host validation (TypeError/ValueError/UnicodeError). It guarantees every hostname checked by the SSRF guard is a syntactically valid host before IP/allow-list evaluation.","triggerScenarios":"validate_url, create_socket_factory, _origin, or _parse_domains pass a hostname that URL.build(scheme='http', host=value) rejects: illegal characters, IDN/Unicode encoding failures, or a value that isn't a string (TypeError).","commonSituations":"A tool config or plugin passes a URL whose host contains spaces/underscores/brackets, non-ASCII domains that fail IDNA, or code extracts a host incorrectly (e.g. includes port or path in the host variable).","solutions":["Fix the source URL so its host is a valid DNS name or IP (no spaces, underscores, brackets, or stray port).","If handling internationalized domains, pre-encode them to punycode (IDNA) before validation.","Check upstream parsing code that extracts the hostname — it may be grabbing too much or too little of the URL.","Catch OutboundPolicyError at the plugin boundary and surface a clear 'invalid target host' message to the caller."],"exampleFix":"// before\nurl = \"http://my_server space.internal/api\"\nawait validate_url(url)  # underscore+space host\n// after\nurl = \"http://my-server-space.internal/api\"\nawait validate_url(url)","handlingStrategy":"validation","validationCode":"from yarl import URL\ndef is_valid_host(value) -> bool:\n    if not isinstance(value, str) or not value.strip():\n        return False\n    try:\n        return bool(URL.build(scheme=\"http\", host=value.strip().lower().rstrip(\".\")).raw_host)\n    except (TypeError, ValueError, UnicodeError):\n        return False","typeGuard":"def is_str_hostname(v) -> bool:\n    return isinstance(v, str) and bool(v.strip()) and \" \" not in v and \";\" not in v","tryCatchPattern":"try:\n    await validate_url(target)\nexcept OutboundPolicyError as exc:\n    logger.warning(\"Blocked invalid outbound URL %r: %s\", target, exc)\n    return ToolResult(error=\"invalid target host\")","preventionTips":["Extract hosts with a URL parser, not string splitting","Punycode-encode internationalized domains before validation","Reject hosts with underscores/spaces at input time"],"tags":["ssrf","url-validation","hostname"],"backgroundTag":"invalid-url","analyzedSha":"5e758547a83371a5a4b29dadf4ac03e8dd527635","analyzedAt":"2026-09-12T08:03:51.356Z","contentChangedAt":"2026-09-12T08:03:51.356Z","schemaVersion":2},"datasetVersion":"2026-09-19T12:17:13.211Z"}