iflytek/astron-agent · error · OutboundPolicyError

Outbound URL authority is invalid

Error message

Outbound URL authority is invalid

What it means

A backslash in the URL authority (netloc) is rejected. Some parsers treat backslash as a path separator and others do not; excluding it from the netloc prevents parser-differential attacks where validation sees one host while the HTTP client connects to another.

Solutions

  1. Replace backslashes with forward slashes: url.replace('\\', '/').
  2. Verify the string is a proper http(s):// URL, not a Windows file path mistakenly passed as an endpoint.
  3. Sanitize user input by rejecting or normalizing '\\' before building the URL.
  4. Log repr(url) to see exact backslash placement, then fix the source string.

Example fix

// before
url = r"https:\\example.com\api"  # windows-style
// after
url = "https://example.com/api"
Defensive patterns

Strategy: validation

Validate before calling

def netloc_is_clean(url: str) -> bool:
    return "\\" not in urlsplit(url).netloc

Try / catch

try:
    client.get(url)
except OutboundPolicyError:
    url = url.replace("\\", "/")
    client.get(url)

Prevention

When it happens

Trigger: _parse_http_url gets a URL like https://example.com\\@evil.com or https:\\\\host (Windows-style backslashes in the authority), so '\\' is found in parsed.netloc.

Common situations: Windows paths pasted into a URL field (C:\\... used as host); attackers substituting \\ for // to bypass naive checks; template errors doubling backslashes.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


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

Appendix: source

Thrown at core/plugin/link/infra/tool_exector/ssrf_guard.py:208

        raise OutboundPolicyError("Outbound URL is malformed") from exc
    _validate_parsed_http_url(parsed, port)
    return parsed


def _validate_url_characters(url: str) -> None:
    if any(ord(character) < 0x20 or ord(character) == 0x7F for character in url):
        raise OutboundPolicyError("Outbound URL contains control characters")


def _validate_parsed_http_url(parsed: SplitResult, port: Union[int, None]) -> None:
    if parsed.scheme.lower() not in _ALLOWED_SCHEMES:
        raise OutboundPolicyError("Only HTTP and HTTPS tool URLs are allowed")
    if not parsed.hostname:
        raise OutboundPolicyError("Outbound URL must include a hostname")
    if parsed.username is not None or parsed.password is not None:
        raise OutboundPolicyError("Outbound URL must not include user information")
    if "\\" in parsed.netloc:
        raise OutboundPolicyError("Outbound URL authority is invalid")
    if parsed.fragment:
        raise OutboundPolicyError("Outbound URL must not include a fragment")
    if port is not None and not 1 <= port <= 65535:
        raise OutboundPolicyError("Outbound URL port is invalid")


def _parse_networks(raw_value: str, setting_name: str) -> Tuple[IpNetwork, ...]:
    networks = []
    for entry in raw_value.split(","):
        value = entry.strip()
        if not value:
            continue
        try:
            networks.append(ipaddress.ip_network(value, strict=False))
        except ValueError as exc:
            raise OutboundPolicyError(f"Invalid {setting_name} entry") from exc
    return tuple(networks)

View on GitHub (pinned to 5e758547a8)