fix(url_safety): allow DNS failure in proxy/sandbox environments

When the runtime blocks direct DNS (NVIDIA OpenShell, Docker + Squid,
corporate proxy with DNS-only-via-proxy), socket.getaddrinfo() fails
and is_safe_url() blocks *all* requests — including legitimate public
URLs via the configured proxy.

Add _proxy_is_configured() helper that checks HTTPS_PROXY, HTTP_PROXY,
http_proxy, https_proxy, ALL_PROXY, all_proxy.  When DNS fails AND a
proxy is configured, delegate DNS resolution to the proxy rather than
blocking outright.

Blocked hostnames (metadata.google.internal, 169.254.169.254, etc.)
are checked BEFORE DNS resolution, so cloud metadata endpoints remain
blocked regardless of proxy status.

Fixes #32217
This commit is contained in:
sg-architect 2026-07-21 16:43:19 +08:00 committed by Teknium
parent c769081803
commit 931ca437ff

View file

@ -39,6 +39,21 @@ from utils import is_truthy_value
logger = logging.getLogger(__name__)
# ── Proxy detection ──────────────────────────────────────────
# Proxy environment variables that indicate the runtime should
# delegate DNS to a proxy rather than attempting direct resolution.
_PROXY_ENV_VARS = (
"HTTPS_PROXY", "https_proxy",
"HTTP_PROXY", "http_proxy",
"ALL_PROXY", "all_proxy",
)
def _proxy_is_configured() -> bool:
"""Return True when at least one HTTP proxy env var is set."""
return any(os.environ.get(v) for v in _PROXY_ENV_VARS)
def normalize_url_for_request(url: str) -> str:
"""Return an ASCII-safe HTTP URL for Hermes-owned URL tools.
@ -420,8 +435,21 @@ def is_safe_url(url: str) -> bool:
try:
addr_info = socket.getaddrinfo(hostname, None, socket.AF_UNSPEC, socket.SOCK_STREAM)
except socket.gaierror:
# DNS resolution failed — fail closed. If DNS can't resolve it,
# the HTTP client will also fail, so blocking loses nothing.
# DNS resolution failed. In sandbox / proxy environments
# (NVIDIA OpenShell, Docker + Squid, etc.) the host may
# block direct DNS — only HTTP(S) through the proxy is
# permitted. When a proxy is configured, delegate DNS to
# the proxy rather than blocking the request outright.
# The hostname was already checked against
# _BLOCKED_HOSTNAMES above so metadata endpoints remain
# blocked regardless.
if _proxy_is_configured():
logger.debug(
"DNS resolution failed for %s — proxy configured, "
"allowing through for proxy-side resolution",
hostname,
)
return True
logger.warning("Blocked request — DNS resolution failed for: %s", hostname)
return False