Panniantong/Agent-Reach · error · TranscribeError
SSRF blocked: internal host is not allowed
Error message
SSRF blocked: internal host is not allowed
What it means
Raised by _assert_safe_public_url (transcribe.py:244-245) when the canonicalized host is in _BLOCKED_HOSTS ({'localhost', 'metadata.google.internal'}) or ends with '.localhost'. These names resolve to the local machine or the cloud metadata service, so fetching them is exactly what the SSRF guard exists to prevent.
Source
Thrown at agent_reach/transcribe.py:245
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")
template = out_dir / "source.%(ext)s"
_run(
[
"yt-dlp",
"-x",
"--audio-format",
"m4a",
"--audio-quality",
"0",
"--no-playlist",View on GitHub (pinned to 93ae1d18c3)
Solutions
- Serve the file from a publicly reachable host or pass the local file path directly to transcribe()
- If you control the media server, bind it to a real (non-localhost-resolving) hostname reachable from the machine
- Never feed agent-supplied URLs to this API without your own allowlist — the blocklist is deliberate but minimal
Example fix
# before
download_audio("http://localhost:8080/ep.m4a", out_dir) # SSRF blocked: internal host is not allowed
# after: local file takes the direct path
text = transcribe("/srv/media/ep.m4a") Defensive patterns
Strategy: validation
Validate before calling
_BLOCKED = {"localhost", "metadata.google.internal"}
def host_not_internal(host: str) -> bool:
h = host.strip().rstrip(".").lower()
return h not in _BLOCKED and not h.endswith(".localhost") Try / catch
from agent_reach.transcribe import TranscribeError
try:
transcribe(url)
except TranscribeError as e:
if "internal host" in str(e):
serve_locally_or_reject(url) # never 'fix' by bypassing the guard
raise Prevention
- Never point transcription at localhost or *.localhost — use local file paths instead
- Treat metadata.google.internal as an attack indicator in agent-facing logs
- Keep an allowlist of trusted media hosts in front of transcribe()
When it happens
Trigger: download_audio('http://localhost:8080/recording.m4a'), 'https://api.localhost/x', 'http://metadata.google.internal/computeMetadata/v1/...'. Dot-suffixed variants ('localhost.') are normalized first, so trailing-dot evasion also fails.
Common situations: Local e2e tests pointing the transcription pipeline at a dev server; agents on cloud VMs being tricked into reading the metadata endpoint (the attack this blocks); misconfigured env vars that default a media base URL to localhost.
Related errors
- only the V2EX HTTPS API is allowed
- SSRF blocked: only public http(s) URLs are allowed
- SSRF blocked: encoded or ambiguous URL host
- SSRF blocked: URL host is invalid
- gh hosts.yml 无法安全读取
AI-assisted analysis of Panniantong/Agent-Reach@93ae1d18c3 (2026-08-14).
Data as JSON: /api/errors/3f21979b71199952.
Report an issue: GitHub.