Panniantong/Agent-Reach · error · TranscribeError

SSRF blocked: encoded or ambiguous URL host

Error message

SSRF blocked: encoded or ambiguous URL host

What it means

Raised by _assert_safe_public_url (transcribe.py:230-235) when the URL's authority segment (between '://' and the first /, ?, or #) contains a backslash or percent sign. These characters create parser ambiguity — different consumers (urlparse vs yt-dlp's extractor vs the C resolver) can disagree about where the host ends, a classic SSRF-bypass technique, so the guard fails closed.

Source

Thrown at agent_reach/transcribe.py:235

        before_slash = url.split("/", 1)[0]
        if ":" in before_slash:
            host_part, port_part = before_slash.rsplit(":", 1)
            if not host_part or not port_part.isdigit():
                raise TranscribeError("SSRF blocked: only public http(s) URLs are allowed")
        normalized_url = f"https://{url}"
        parsed = urlparse(normalized_url)
    else:
        normalized_url = url
        parsed = urlparse(url)
        if parsed.scheme not in {"http", "https"}:
            raise TranscribeError("SSRF blocked: only public http(s) URLs are allowed")

    raw_authority = normalized_url.split("://", 1)[1]
    raw_authority = raw_authority.split("/", 1)[0]
    raw_authority = raw_authority.split("?", 1)[0]
    raw_authority = raw_authority.split("#", 1)[0]
    if "\\" in raw_authority or "%" in raw_authority:
        raise TranscribeError("SSRF blocked: encoded or ambiguous URL host")

    raw_host = (parsed.hostname or "").strip().rstrip(".")
    if not raw_host:
        raise TranscribeError("SSRF blocked: URL host is missing")
    try:
        host = raw_host.encode("idna").decode("ascii").lower().rstrip(".")
    except UnicodeError:
        raise TranscribeError("SSRF blocked: URL host is invalid") from None
    if host in _BLOCKED_HOSTS or host.endswith(".localhost"):
        raise TranscribeError("SSRF blocked: internal host is not allowed")
    if _is_private_ip(host):
        raise TranscribeError("SSRF blocked: private/internal IP is not allowed")


def download_audio(url: str, out_dir: Path) -> Path:
    """Download audio with yt-dlp into out_dir; return the resulting file path."""
    _assert_safe_public_url(url)
    _require("yt-dlp")

View on GitHub (pinned to 93ae1d18c3)

Solutions

  1. Use a plain, canonical host with no percent-encoding or backslashes: 'https://example.com/media.mp3'
  2. If the URL came from user/LLM input, normalize with urllib.parse and re-encode components properly before passing
  3. Remove inline credentials ('user:pass@') — pass a clean host[:port]/path instead

Example fix

# before
url = "https://example.com%2f@evil.com/audio.mp3"  # SSRF blocked: encoded or ambiguous URL host

# after
from urllib.parse import urlsplit, urlunsplit
parts = urlsplit(url)
url = urlunsplit((parts.scheme, parts.netloc.rsplit("@", 1)[-1], parts.path, parts.query, ""))
# -> https://evil.com/audio.mp3 (validate that host is truly intended)
Defensive patterns

Strategy: validation

Validate before calling

from urllib.parse import urlparse

def has_clean_authority(url: str) -> bool:
    try:
        p = urlparse(url)
    except ValueError:
        return False
    if p.scheme not in {"http", "https"} or not p.hostname:
        return False
    auth = (p.netloc.rsplit("@", 1)[-1]
            .split("/", 1)[0].split("?", 1)[0].split("#", 1)[0])
    return "\\" not in auth and "%" not in auth

Try / catch

from agent_reach.transcribe import TranscribeError
try:
    transcribe(url)
except TranscribeError as e:
    if "encoded or ambiguous URL host" in str(e):
        url = canonicalize(url)  # strip credentials, re-encode properly
        return transcribe(url)
    raise

Prevention

When it happens

Trigger: URLs like 'https://example.com%2f@evil.com/a', 'https://evil.com\@example.com/a', or percent-encoded hosts ('https://%65xample.com/'). Also triggered by Windows-style paths mistakenly passed as URLs ('https://C:\\audio\\file.m4a').

Common situations: Copy-pasting URLs that carry encoded credentials or path traversal fragments; feeding Windows paths; deliberate SSRF probe payloads in security scanning of agent-facing endpoints.

Related errors


AI-assisted analysis of Panniantong/Agent-Reach@93ae1d18c3 (2026-08-14). Data as JSON: /api/errors/3172382078f55627. Report an issue: GitHub.