{"record":{"id":"18b6b39fecfc3d7e","repo":"iflytek/astron-agent","slug":"remote-resource-path-is-invalid","errorCode":null,"errorMessage":"Remote resource path is invalid","messagePattern":"Remote resource path is invalid","errorType":"exception","errorClass":"RemoteResourcePolicyError","httpStatus":null,"severity":"error","filePath":"core/plugin/aitools/common/clients/safe_download.py","lineNumber":323,"sourceCode":"            required_prefix\n        ):\n            return True\n    return False\n\n\ndef _effective_port(parsed: SplitResult) -> int:\n    if parsed.port is not None:\n        return parsed.port\n    return 443 if parsed.scheme.lower() == \"https\" else 80\n\n\ndef _decoded_path(raw_path: str) -> str:\n    try:\n        if _INVALID_PERCENT_ESCAPE.search(raw_path):\n            raise ValueError\n        value = unquote_to_bytes(raw_path).decode(\"utf-8\", errors=\"strict\")\n    except (UnicodeDecodeError, ValueError):\n        raise RemoteResourcePolicyError(\"Remote resource path is invalid\") from None\n    if (\n        not value.startswith(\"/\")\n        or \"\\\\\" in value\n        or any(ord(character) < 0x20 or ord(character) == 0x7F for character in value)\n        or any(segment in {\".\", \"..\"} for segment in value.split(\"/\"))\n    ):\n        raise RemoteResourcePolicyError(\"Remote resource path is invalid\")\n    return value\n\n\ndef _canonical_address(address: IpAddress) -> IpAddress:\n    if isinstance(address, ipaddress.IPv6Address) and address.ipv4_mapped:\n        return address.ipv4_mapped\n    return address\n\n\ndef _matches_any(address: IpAddress, networks: Tuple[IpNetwork, ...]) -> bool:\n    candidates: Tuple[IpAddress, ...] = (address,)","sourceCodeStart":305,"sourceCodeEnd":341,"githubUrl":"https://github.com/iflytek/astron-agent/blob/5e758547a83371a5a4b29dadf4ac03e8dd527635/core/plugin/aitools/common/clients/safe_download.py#L305-L341","documentation":"RemoteResourcePolicyError raised by _decoded_path when the URL path cannot be safely decoded: it contains a malformed percent-escape (e.g. '%zz', a bare '%') or its percent-decoded bytes are not valid UTF-8. Used only by _is_configured_storage_url to compare the candidate path against configured S3 bucket prefixes, so a broken path also means private-storage authorization silently fails.","triggerScenarios":"A candidate storage URL whose path contains invalid percent-encoding ('/my-bucket/file%.pdf') or percent-encoded bytes that don't form UTF-8 ('/my-bucket/%FF%FE.txt'), evaluated during private-storage authorization.","commonSituations":"Filenames uploaded with non-UTF-8 (e.g. GBK) encodings then blindly percent-encoded into URLs; double-encoding bugs producing stray '%' characters; copy-pasted URLs truncated mid-escape.","solutions":["Percent-encode the path correctly: urllib.parse.quote(path, safe='/') with a known UTF-8 source string","Re-upload or re-name the object with a UTF-8-safe filename and regenerate the URL","Fix double-encoding in the URL-producing code (don't quote an already-quoted string)"],"exampleFix":"// before\nurl = f\"https://s3.example.com/my-bucket/{filename}\"  # filename = '报告%进行中.pdf' (partially encoded)\n// after\nfrom urllib.parse import quote\nurl = f\"https://s3.example.com/my-bucket/{quote(filename, safe='')}\"","handlingStrategy":"validation","validationCode":"import re\nfrom urllib.parse import unquote_to_bytes\nINVALID_ESCAPE = re.compile(r\"%(?![0-9a-fA-F]{2})\")\ndef path_decodes_cleanly(url: str) -> bool:\n    raw = url.split(\"?\", 1)[0]\n    if INVALID_ESCAPE.search(raw):\n        return False\n    try:\n        raw_path = raw.split(\"://\", 1)[-1].split(\"/\", 1)\n        path = \"/\" + raw_path[1] if len(raw_path) > 1 else \"/\"\n        unquote_to_bytes(path).decode(\"utf-8\")\n    except (UnicodeDecodeError, ValueError):\n        return False\n    return True","typeGuard":null,"tryCatchPattern":"try:\n    data = await fetch_public_resource(url)\nexcept HTTPClientException as e:\n    if \"path is invalid\" in str(e):\n        log.warning(\"Storage URL path has invalid percent-encoding or non-UTF-8 bytes\")\n    raise","preventionTips":["Always percent-encode paths with urllib.parse.quote(value, safe='/') from UTF-8 source strings","Convert non-UTF-8 filenames (GBK etc.) to UTF-8 before encoding into URLs","Avoid double-encoding; never quote an already-quoted string"],"tags":["url-validation","encoding","utf-8","ssrf","python"],"backgroundTag":"invalid-argument-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"}