{"record":{"id":"7418d590de921f48","repo":"iflytek/astron-agent","slug":"remote-resource-url-is-malformed","errorCode":null,"errorMessage":"Remote resource URL is malformed","messagePattern":"Remote resource URL is malformed","errorType":"exception","errorClass":"RemoteResourcePolicyError","httpStatus":null,"severity":"error","filePath":"core/plugin/aitools/common/clients/safe_download.py","lineNumber":192,"sourceCode":"\ndef _positive_float_setting(name: str, default: float) -> float:\n    try:\n        value = float(os.getenv(name, str(default)))\n    except (TypeError, ValueError):\n        return default\n    return value if math.isfinite(value) and value > 0 else default\n\n\ndef _parse_resource_url(url: str) -> SplitResult:\n    _validate_url_characters(url)\n    try:\n        # ``urlsplit`` deliberately keeps semicolon path parameters in ``path``.\n        # Dropping them would let policy validation inspect a different path from\n        # the one aiohttp sends to the object-storage origin.\n        parsed = urlsplit(url)\n        port = parsed.port\n    except (TypeError, ValueError) as exc:\n        raise RemoteResourcePolicyError(\"Remote resource URL is malformed\") from exc\n    _validate_parsed_resource_url(parsed, port)\n    return parsed\n\n\ndef _validate_url_characters(url: str) -> None:\n    if not isinstance(url, str) or any(\n        ord(character) < 0x20 or ord(character) == 0x7F for character in url\n    ):\n        raise RemoteResourcePolicyError(\"Remote resource URL is malformed\")\n\n\ndef _validate_parsed_resource_url(\n    parsed: SplitResult,\n    port: Optional[int],\n) -> None:\n    if parsed.scheme.lower() not in _ALLOWED_SCHEMES:\n        raise RemoteResourcePolicyError(\n            \"Only HTTP and HTTPS remote resources are allowed\"","sourceCodeStart":174,"sourceCodeEnd":210,"githubUrl":"https://github.com/iflytek/astron-agent/blob/5e758547a83371a5a4b29dadf4ac03e8dd527635/core/plugin/aitools/common/clients/safe_download.py#L174-L210","documentation":"_parse_resource_url wraps urllib.parse.urlsplit and the parsed.port property access; a TypeError or ValueError there (unparseable URL, invalid port, bad IPv6 literal) is converted to RemoteResourcePolicyError('Remote resource URL is malformed'). The library refuses to even evaluate policy on URLs it cannot parse deterministically.","triggerScenarios":"urlsplit raises on inputs like 'http://[::1' (unclosed bracket) or 'http://host:port' (non-numeric port); parsed.port raises ValueError for out-of-range ports like ':99999'; url is not a string-convertible value (TypeError).","commonSituations":"User-supplied URL with typos ('htp:/example.com', missing scheme/host); template strings left unformatted ('{file_url}'); port concatenation bugs (f-string building ':{}' with a bad value); hostnames with unencoded characters.","solutions":["Print/inspect the exact URL string being passed; look for truncation, unformatted placeholders, or stray characters.","Validate the URL with urllib.parse.urlsplit plus a try/except around .port before sending it.","URL-encode path/query components (urllib.parse.quote) instead of embedding raw special characters.","Ensure the URL includes scheme and host, e.g. 'https://host/path', not a bare path or hostname."],"exampleFix":"// before\nurl = f\"https://host:{port}/file\"  # port accidentally 'abc'\n// after\nurl = f\"https://host:{int(port)}/file\"  # validated numeric port","handlingStrategy":"validation","validationCode":"from urllib.parse import urlsplit\ntry:\n    p = urlsplit(url); _ = p.port\nexcept ValueError:\n    raise ValueError(\"malformed URL\")","typeGuard":"def is_parseable_url(u):\n    from urllib.parse import urlsplit\n    if not isinstance(u, str):\n        return False\n    try:\n        urlsplit(u).port\n        return True\n    except (TypeError, ValueError):\n        return False","tryCatchPattern":"try:\n    data = await fetch_public_resource(url)\nexcept HTTPClientException as e:\n    if \"malformed\" in str(e):\n        ...  # return 400 to the caller","preventionTips":["URL-encode user-supplied path/query segments","Reject control characters and placeholders before calling","Unit-test URL building code"],"tags":["url","validation","python","input-validation"],"backgroundTag":"invalid-url-format","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"}