{"record":{"id":"6a72d9fb3612c4bd","repo":"iflytek/astron-agent","slug":"remote-resource-url-must-not-include-a-fragment","errorCode":null,"errorMessage":"Remote resource URL must not include a fragment","messagePattern":"Remote resource URL must not include a fragment","errorType":"exception","errorClass":"RemoteResourcePolicyError","httpStatus":null,"severity":"error","filePath":"core/plugin/aitools/common/clients/safe_download.py","lineNumber":221,"sourceCode":"\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\"\n        )\n    if not parsed.hostname:\n        raise RemoteResourcePolicyError(\"Remote resource URL must include a hostname\")\n    if parsed.username is not None or parsed.password is not None:\n        raise RemoteResourcePolicyError(\n            \"Remote resource URL must not include user information\"\n        )\n    if \"\\\\\" in parsed.netloc:\n        raise RemoteResourcePolicyError(\"Remote resource URL authority is invalid\")\n    if parsed.fragment:\n        raise RemoteResourcePolicyError(\n            \"Remote resource URL must not include a fragment\"\n        )\n    if port is not None and not 1 <= port <= 65535:\n        raise RemoteResourcePolicyError(\"Remote resource URL port is invalid\")\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 RemoteResourcePolicyError(\"Remote resource hostname is invalid\") from exc\n    if not normalized:\n        raise RemoteResourcePolicyError(\"Remote resource hostname is invalid\")\n    return normalized.rstrip(\".\")\n","sourceCodeStart":203,"sourceCodeEnd":239,"githubUrl":"https://github.com/iflytek/astron-agent/blob/5e758547a83371a5a4b29dadf4ac03e8dd527635/core/plugin/aitools/common/clients/safe_download.py#L203-L239","documentation":"RemoteResourcePolicyError raised when the URL contains a fragment ('#...'). The SSRF-safe download module rejects fragments because a fragment is never sent to the server; keeping it could cause the validated URL and the actually-requested URL to differ (fragment delimiters can also be used to confuse parsers). The check fires in _validate_parsed_resource_url via parsed.fragment being truthy.","triggerScenarios":"Calling fetch_public_resource(url) where url includes a '#fragment' suffix, e.g. 'https://cdn.example.com/file.pdf#page=3', or a caller-supplied URL copied from a browser address bar that retained an anchor.","commonSituations":"Users pasting browser URLs with anchors; document-processing pipelines that append '#page=N' to PDF links; frontend code forwarding full location.href (including hash) to a download API.","solutions":["Strip the fragment before downloading: use urlsplit(url)._replace(fragment='').geturl() or URL(url).with_fragment(None)","Validate/sanitize caller input to remove '#...' segments before invoking fetch_public_resource","If the fragment carries meaning (e.g. PDF page), extract it separately and handle it in application code, not in the download URL"],"exampleFix":"// before\nawait fetch_public_resource(\"https://cdn.example.com/report.pdf#page=3\")\n// after\nfrom urllib.parse import urlsplit, urlunsplit\nparts = urlsplit(url)\nclean = urlunsplit((parts.scheme, parts.netloc, parts.path, parts.query, \"\"))\nawait fetch_public_resource(clean)","handlingStrategy":"validation","validationCode":"def url_has_no_fragment(url: str) -> bool:\n    from urllib.parse import urlsplit\n    return not urlsplit(url).fragment","typeGuard":null,"tryCatchPattern":"try:\n    data = await fetch_public_resource(url)\nexcept RemoteResourcePolicyError:\n    data = await fetch_public_resource(url.split('#', 1)[0])","preventionTips":["Strip fragments from user-supplied URLs before downloading","Never forward location.href (with hash) from frontends to download APIs","Handle anchors (PDF pages, etc.) as separate application metadata"],"tags":["url-validation","ssrf","security","python"],"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"}