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
- Use a plain, canonical host with no percent-encoding or backslashes: 'https://example.com/media.mp3'
- If the URL came from user/LLM input, normalize with urllib.parse and re-encode components properly before passing
- 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
- Strip inline credentials and percent-encoded hosts from URLs before use
- Rebuild URLs from urlsplit parts instead of string surgery
- Never pass Windows-style backslash paths as URLs
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
- only the V2EX HTTPS API is allowed
- SSRF blocked: only public http(s) URLs are allowed
- SSRF blocked: URL host is invalid
- SSRF blocked: internal host is not allowed
- gh hosts.yml 无法安全读取
AI-assisted analysis of Panniantong/Agent-Reach@93ae1d18c3 (2026-08-14).
Data as JSON: /api/errors/3172382078f55627.
Report an issue: GitHub.