iflytek/astron-agent · error · RemoteResourcePolicyError

Remote resource address is unsafe

Error message

Remote resource address is unsafe

What it means

RemoteResourcePolicyError raised by _validate_destination_address when the target IP is in an explicitly forbidden range: unspecified (0.0.0.0/::), loopback, link-local, multicast, reserved, IPv6 site-local, or any network in _NEVER_CONNECT_NETWORKS (documentation ranges, 6to4, NAT64, etc.). This is the core SSRF guard — these addresses could reach internal infrastructure or loop back to the server itself.

Solutions

  1. Use a genuinely public URL (the resolved address must be a global unicast address)
  2. If downloading from your own S3/object storage is intended, configure OSS_TYPE=s3 with OSS_DOWNLOAD_HOST and OSS_BUCKET_NAME/OSS_BUCKET_CONSOLE so private storage origins are whitelisted via _is_configured_storage_url
  3. Expose the internal resource through a public endpoint instead of the private address
  4. For local testing, use a public test server (e.g. an HTTPS URL on a public host) rather than localhost

Example fix

// before
await fetch_public_resource("http://127.0.0.1:9000/files/report.pdf")  # loopback
// after
# serve through the configured public storage origin
await fetch_public_resource("https://s3.example.com/my-bucket/report.pdf")
# with OSS_TYPE=s3, OSS_DOWNLOAD_HOST=https://s3.example.com, OSS_BUCKET_NAME=my-bucket
Defensive patterns

Strategy: try-catch

Validate before calling

import ipaddress, socket
def resolves_to_public(url: str) -> bool:
    from urllib.parse import urlsplit
    host = urlsplit(url).hostname or ""
    try:
        infos = socket.getaddrinfo(host, None)
    except socket.gaierror:
        return False
    for info in infos:
        addr = ipaddress.ip_address(info[4][0])
        if not addr.is_global or addr.is_loopback or addr.is_link_local:
            return False
    return True

Try / catch

from plugin.aitools.common.clients.safe_download import RemoteResourcePolicyError
from plugin.aitools.common.exceptions.exceptions import HTTPClientException
try:
    data = await fetch_public_resource(url)
except HTTPClientException as e:
    # fetch_public_resource wraps RemoteResourcePolicyError into HTTPClientException
    log.warning("Download target rejected (SSRF policy): %s", e)
    return None

Prevention

When it happens

Trigger: fetch_public_resource called with a URL whose host resolves (or is a literal) to a forbidden address, e.g. 'http://127.0.0.1:8000', 'http://169.254.169.254/latest/meta-data' (cloud metadata), 'http://10.0.0.5/file', 'http://[::1]/', or DNS resolving a public-looking hostname to a private IP (caught in the socket_factory at connect time).

Common situations: Pointing the downloader at a local dev server during testing; cloud-metadata SSRF attempts via attacker-supplied URLs; internal service URLs mistakenly given to a public-download API; DNS rebinding where a hostname resolves to a private IP.

Related errors


AI-assisted analysis of iflytek/astron-agent@5e758547a8 (2026-09-12). Data as JSON: /api/errors/8760c35d3292a3aa. Report an issue: GitHub.

Appendix: source

Thrown at core/plugin/aitools/common/clients/safe_download.py:263

        return None


def _validate_destination_address(
    address: IpAddress,
    *,
    allow_private_storage: bool,
) -> None:
    canonical = _canonical_address(address)
    unsafe_properties = (
        canonical.is_unspecified,
        canonical.is_loopback,
        canonical.is_link_local,
        canonical.is_multicast,
        canonical.is_reserved,
        bool(getattr(canonical, "is_site_local", False)),
    )
    if any(unsafe_properties) or _matches_any(address, _NEVER_CONNECT_NETWORKS):
        raise RemoteResourcePolicyError("Remote resource address is unsafe")
    if not allow_private_storage and not canonical.is_global:
        raise RemoteResourcePolicyError("Remote resource address is unsafe")


def _is_configured_storage_url(candidate: SplitResult) -> bool:
    """Authorize only objects under the server-configured S3 download origin/bucket."""
    if os.getenv("OSS_TYPE", "ifly_gateway_storage").strip().lower() != "s3":
        return False
    origin_value = os.getenv("OSS_DOWNLOAD_HOST", "").strip()
    buckets = {
        value
        for setting in ("OSS_BUCKET_NAME", "OSS_BUCKET_CONSOLE")
        if (value := os.getenv(setting, "").strip())
        and value == value.lower()
        and _S3_BUCKET_PATTERN.fullmatch(value) is not None
    }
    if not origin_value or not buckets:
        return False

View on GitHub (pinned to 5e758547a8)