Graphify-Labs/graphify · error · ValueError
Blocked private/internal IP {addr} (resolved from '{hostname
Error message
Blocked private/internal IP {addr} (resolved from '{hostname}'). Got: {url!r} What it means
ValueError from validate_url when the hostname resolves (via getaddrinfo, AF_UNSPEC) to an IP that _ip_is_blocked considers private/reserved - 127.x, 10.x, 192.168.x, 169.254.x, ::1, fc00::/7, etc. Tier three of the SSRF guard: the DNS answer is checked, so numeric-IP and DNS-rebinding tricks against private ranges are caught (single-resolution window notwithstanding).
Source
Thrown at graphify/security.py:134
)
hostname = parsed.hostname
if hostname:
# Block known cloud metadata hostnames
if hostname.lower() in _BLOCKED_HOSTS:
raise ValueError(
f"Blocked cloud metadata endpoint '{hostname}'. "
f"Got: {url!r}"
)
# Resolve hostname and block private/reserved IP ranges
try:
infos = socket.getaddrinfo(hostname, None, socket.AF_UNSPEC, socket.SOCK_STREAM)
for info in infos:
addr = info[4][0]
ip = ipaddress.ip_address(addr)
if _ip_is_blocked(ip):
raise ValueError(
f"Blocked private/internal IP {addr} (resolved from '{hostname}'). "
f"Got: {url!r}"
)
except socket.gaierror as exc:
raise ValueError(
f"DNS resolution failed for '{hostname}': {exc}. Got: {url!r}"
) from exc
return url
# ---------------------------------------------------------------------------
# SSRF-guarded connections
#
# Instead of monkey-patching the process-global socket.getaddrinfo (a
# non-thread-safe TOCTOU hazard when multiple fetches run concurrently),
# we subclass the HTTP(S) connection so each connection resolves DNS exactly
# once, validates the resulting IP, and then connects to that exact IP. ThereView on GitHub (pinned to 7fe58b0b0f)
Solutions
- If you truly need internal fetches, use a dedicated HTTP client outside the guarded path - do not weaken validate_url.
- For local development, exempt via an explicit, narrow configuration only if your threat model allows; otherwise test against a public staging URL.
- If unexpected: treat as a possible DNS-rebinding probe - check what the hostname resolves to from the same box (getent hosts <name>).
Example fix
# before
url = 'http://intranet.corp.local/report' # resolves to 10.1.2.3
fetch(validate_url(url)) # ValueError: Blocked private/internal IP
# after - fetch internal resources with a client outside the SSRF-guarded path
resp = requests_internal.get('http://intranet.corp.local/report', timeout=10) Defensive patterns
Strategy: validation
Validate before calling
import ipaddress, socket
from urllib.parse import urlparse
def resolves_to_private(host: str) -> bool:
try:
infos = socket.getaddrinfo(host, None, socket.AF_UNSPEC, socket.SOCK_STREAM)
return any(
ipaddress.ip_address(i[4][0]).is_private or ipaddress.ip_address(i[4][0]).is_loopback
for i in infos
)
except socket.gaierror:
return False # let validate_url produce the DNS error instead Type guard
def is_safe_url(url: str) -> bool:
try:
validate_url(url)
return True
except ValueError:
return False Try / catch
try:
safe = validate_url(url)
except ValueError as exc:
if "private/internal IP" in str(exc):
return bad_request("internal addresses are not fetchable")
raise Prevention
- Never route intranet fetches through the public-facing URL fetcher; separate the trust boundaries.
- Remember the check resolves DNS once - rebinding races remain possible; keep the SSRF-guarded connection classes (see the code below validate_url) in play.
- Log blocked-IP events with the hostname for incident review.
When it happens
Trigger: validate_url(url) where any resolved address fails _ip_is_blocked (security.py:127-136): e.g. http://10.0.0.5/x, a public name whose DNS points at 127.0.0.1, IPv6 ULA hosts, or hostnames whose A/AAAA records include a private IP among others.
Common situations: Legit internal tooling accidentally pointed at the SSRF-guarded fetcher (users expect to fetch intranet URLs); DNS rebinding attacks where a public name flips to a private IP; containers resolving service names to 10.x/172.x overlay networks; local development against http://127.0.0.1:8000.
Related errors
- SSRF blocked: IP {addr} resolved from '{host}' is private/re
- SSRF blocked: no usable address resolved from '{host}'
- Blocked URL scheme '{parsed.scheme}' - only http and https a
- Blocked cloud metadata endpoint '{hostname}'. Got: {url!r}
- DNS resolution failed for '{hostname}': {exc}. Got: {url!r}
AI-assisted analysis of Graphify-Labs/graphify@7fe58b0b0f (2026-08-14).
Data as JSON: /api/errors/af47e1d53fe454cc.
Report an issue: GitHub.