Panniantong/Agent-Reach · error · ValueError
only the V2EX HTTPS API is allowed
Error message
only the V2EX HTTPS API is allowed
What it means
Raised by _validate_api_url() when the URL parses fine but violates the V2EX channel's allowlist: scheme must be https, host must be v2ex.com or www.v2ex.com, port only None/443, no userinfo (user:pass@), and the path must start with /api/. This SSRF guard ensures only the public V2EX JSON API is fetched, never arbitrary hosts.
Source
Thrown at agent_reach/channels/v2ex.py:43
return f"{_API_BASE}{path}?{urlencode(params)}"
def _validate_api_url(url: str) -> None:
"""Allow only the public V2EX HTTPS JSON API."""
try:
parsed = urlsplit(url)
port = parsed.port
except ValueError as exc:
raise ValueError("invalid V2EX API URL") from exc
if (
parsed.scheme.lower() != "https"
or (parsed.hostname or "").lower() not in {"v2ex.com", "www.v2ex.com"}
or port not in {None, 443}
or parsed.username is not None
or parsed.password is not None
or not parsed.path.startswith("/api/")
):
raise ValueError("only the V2EX HTTPS API is allowed")
def _get_json_with_urllib(url: str) -> Any:
"""Fetch JSON with Python's standard HTTP stack."""
_validate_api_url(url)
req = urllib.request.Request(url, headers={"User-Agent": _UA})
with urllib.request.urlopen(req, timeout=_TIMEOUT) as resp:
raw = resp.read(_MAX_RESPONSE_BYTES + 1)
if len(raw) > _MAX_RESPONSE_BYTES:
raise ValueError("V2EX API response exceeds the 1 MiB safety limit")
return json.loads(raw.decode("utf-8"))
def _is_unexpected_tls_eof(error: BaseException) -> bool:
"""Return whether an exception chain contains the retryable TLS EOF."""
pending: list[BaseException] = [error]
seen: set[int] = set()
while pending:View on GitHub (pinned to 93ae1d18c3)
Solutions
- Use only https://www.v2ex.com/api/... endpoints (or v2ex.com), no port, no credentials
- Convert page URLs to API URLs (e.g. topic page /t/123 → /api/topics/show.json?id=123)
- For mirrors/proxies you cannot use this channel — the allowlist is by design; call the mirror yourself outside Agent Reach
- If a proxy is required, configure it via HTTPS_PROXY env (urllib honors it) instead of rewriting the URL
Example fix
# before
_get_json_with_urllib("https://api.v2ex.com/topics/show.json?id=1")
_get_json_with_urllib("http://www.v2ex.com/api/topics/show.json?id=1")
# after
_get_json_with_urllib("https://www.v2ex.com/api/topics/show.json?id=1") Defensive patterns
Strategy: validation
Validate before calling
from urllib.parse import urlsplit
ALLOWED_HOSTS = {"v2ex.com", "www.v2ex.com"}
def url_on_v2ex_api(url: str) -> bool:
try:
p = urlsplit(url)
port = p.port
except ValueError:
return False
return (
p.scheme.lower() == "https"
and (p.hostname or "").lower() in ALLOWED_HOSTS
and port in (None, 443)
and p.username is None and p.password is None
and p.path.startswith("/api/")
) Type guard
def is_v2ex_api_url(url) -> bool:
if not isinstance(url, str):
return False
return url_on_v2ex_api(url) Try / catch
if not url_on_v2ex_api(url):
raise InvalidInput(url)
try:
_get_json_with_urllib(url)
except ValueError as exc:
if "only the V2EX HTTPS API is allowed" in str(exc):
map_page_url_to_api(url) # e.g. /t/123 -> /api/topics/show.json?id=123 Prevention
- Treat the allowlist as an SSRF contract; don't try to bypass it
- Translate page URLs to /api/ endpoints before calling the channel
- Use HTTPS_PROXY for network routing instead of URL rewriting
When it happens
Trigger: Calling _get_json_with_urllib() with http:// scheme, a different host (api.example.com), an odd port (https://v2ex.com:8443/...), embedded credentials (https://user:pass@v2ex.com/...), or a non-/api/ path (https://www.v2ex.com/t/123).
Common situations: Attempting to proxy the channel to a V2EX mirror or self-hosted instance; feeding a topic page URL (no /api/ prefix) instead of an API endpoint; adding a token as userinfo; trying http to dodge TLS issues behind a proxy.
Related errors
- invalid V2EX API URL
- SSRF blocked: only public http(s) URLs are allowed
- SSRF blocked: encoded or ambiguous URL host
- SSRF blocked: URL host is invalid
- V2EX API response exceeds the 1 MiB safety limit
AI-assisted analysis of Panniantong/Agent-Reach@93ae1d18c3 (2026-08-14).
Data as JSON: /api/errors/0177fec856d59c0b.
Report an issue: GitHub.