{"record":{"id":"69c3a131e1fdf04a","repo":"iflytek/astron-agent","slug":"only-http-and-https-tool-urls-are-allowed","errorCode":null,"errorMessage":"Only HTTP and HTTPS tool URLs are allowed","messagePattern":"Only HTTP and HTTPS tool URLs are allowed","errorType":"exception","errorClass":"OutboundPolicyError","httpStatus":null,"severity":"error","filePath":"core/plugin/link/infra/tool_exector/ssrf_guard.py","lineNumber":202,"sourceCode":"        raise OutboundPolicyError(\"Outbound URL is malformed\")\n    _validate_url_characters(url)\n    try:\n        parsed = urlsplit(url)\n        port = parsed.port\n    except (TypeError, ValueError) as exc:\n        raise OutboundPolicyError(\"Outbound URL is malformed\") from exc\n    _validate_parsed_http_url(parsed, port)\n    return parsed\n\n\ndef _validate_url_characters(url: str) -> None:\n    if any(ord(character) < 0x20 or ord(character) == 0x7F for character in url):\n        raise OutboundPolicyError(\"Outbound URL contains control characters\")\n\n\ndef _validate_parsed_http_url(parsed: SplitResult, port: Union[int, None]) -> None:\n    if parsed.scheme.lower() not in _ALLOWED_SCHEMES:\n        raise OutboundPolicyError(\"Only HTTP and HTTPS tool URLs are allowed\")\n    if not parsed.hostname:\n        raise OutboundPolicyError(\"Outbound URL must include a hostname\")\n    if parsed.username is not None or parsed.password is not None:\n        raise OutboundPolicyError(\"Outbound URL must not include user information\")\n    if \"\\\\\" in parsed.netloc:\n        raise OutboundPolicyError(\"Outbound URL authority is invalid\")\n    if parsed.fragment:\n        raise OutboundPolicyError(\"Outbound URL must not include a fragment\")\n    if port is not None and not 1 <= port <= 65535:\n        raise OutboundPolicyError(\"Outbound URL port is invalid\")\n\n\ndef _parse_networks(raw_value: str, setting_name: str) -> Tuple[IpNetwork, ...]:\n    networks = []\n    for entry in raw_value.split(\",\"):\n        value = entry.strip()\n        if not value:\n            continue","sourceCodeStart":184,"sourceCodeEnd":220,"githubUrl":"https://github.com/iflytek/astron-agent/blob/5e758547a83371a5a4b29dadf4ac03e8dd527635/core/plugin/link/infra/tool_exector/ssrf_guard.py#L184-L220","documentation":"The SSRF guard only permits http and https schemes for outbound tool requests. Any other scheme (file:, ftp:, gopher:, dict:, ws:) is rejected because non-HTTP schemes can be used to reach local files or internal services in SSRF attacks.","triggerScenarios":"_parse_http_url receives a URL whose lowercased parsed scheme is not in _ALLOWED_SCHEMES — e.g. file:///etc/passwd, ftp://host/x, or a scheme-less string like example.com/path where urlsplit yields an empty scheme.","commonSituations":"Tool endpoint configured as file:// or ftp://; URL built by concatenation losing the scheme; scheme-less address given where a full http(s) URL is required.","solutions":["Use http:// or https:// explicitly in the configured tool URL.","If the URL has no scheme, prepend https:// before passing it to the plugin.","Fix the tool/plugin configuration value; only HTTP(S) endpoints are supported by policy.","If another protocol is genuinely needed, it is blocked by design — route through an allowed HTTP gateway instead."],"exampleFix":"// before\nurl = \"ftp://files.example.com/data\"\n// after\nurl = \"https://files.example.com/data\"","handlingStrategy":"validation","validationCode":"from urllib.parse import urlsplit\nALLOWED = {\"http\", \"https\"}\ndef has_allowed_scheme(url: str) -> bool:\n    return urlsplit(url).scheme.lower() in ALLOWED","typeGuard":null,"tryCatchPattern":"try:\n    client.get(url)\nexcept OutboundPolicyError:\n    url = \"https://\" + url  # only if scheme was missing\n    client.get(url)","preventionTips":["Store full http(s):// URLs in configuration, never scheme-less or file:// values","Normalize config URLs once at startup, not per-request","Treat non-HTTP protocols as blocked by policy; use an HTTP gateway"],"tags":["security","ssrf","url-validation"],"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"}