From 2018eec0dc7a09785c3568e376420b0077ef7731 Mon Sep 17 00:00:00 2001 From: Wang Qi Date: Tue, 30 Jun 2026 10:19:04 +0800 Subject: [PATCH] Fix: allow any host for url for development (#16459) --- common/ssrf_guard.py | 11 ++++++++++- internal/agent/tool/ssrf.go | 24 ++++++++++++++++++++++++ internal/utility/ssrf.go | 13 ++++++++++++- 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/common/ssrf_guard.py b/common/ssrf_guard.py index 12130bc6a2..e478cda0a7 100644 --- a/common/ssrf_guard.py +++ b/common/ssrf_guard.py @@ -151,6 +151,15 @@ def assert_url_is_safe( logger.warning("SSRF guard blocked URL with missing host: url=%r", url) raise ValueError("URL is missing a host.") + allow_any_host = _allow_any_host() + if allow_any_host: + logger.warning( + "SSRF guard bypass enabled via %s; allowing URL host without validation: hostname=%r url=%r", + _ALLOW_ANY_HOST_ENV, + hostname, + url, + ) + try: addr_infos = socket.getaddrinfo(hostname, None) except socket.gaierror as exc: @@ -161,7 +170,7 @@ def assert_url_is_safe( for _family, _type, _proto, _canonname, sockaddr in addr_infos: raw_ip = ipaddress.ip_address(sockaddr[0]) eff_ip = _effective_ip(raw_ip) - if not eff_ip.is_global: + if not allow_any_host and not eff_ip.is_global: logger.warning( "SSRF guard blocked URL: hostname=%r resolved to non-public address=%s", hostname, diff --git a/internal/agent/tool/ssrf.go b/internal/agent/tool/ssrf.go index 914f6a9645..0463cb68a6 100644 --- a/internal/agent/tool/ssrf.go +++ b/internal/agent/tool/ssrf.go @@ -21,6 +21,7 @@ import ( "fmt" "net" "net/url" + "os" "strings" ) @@ -83,6 +84,20 @@ func ResolveAndValidate(rawURL string) (originalHost string, pinnedIP net.IP, er return "", nil, fmt.Errorf("ssrf: empty host") } + if allowAnyHost() { + if ip := net.ParseIP(host); ip != nil { + return host, ip, nil + } + ips, lerr := net.LookupIP(host) + if lerr != nil { + return "", nil, fmt.Errorf("ssrf: resolve %s: %w", host, lerr) + } + if len(ips) == 0 { + return "", nil, fmt.Errorf("ssrf: %s has no A/AAAA records", host) + } + return host, ips[0], nil + } + // Short-circuit the well-known host aliases that DNS lookups may // also catch, but defending against the literal name is cheap and // saves a syscall on the common probe path. @@ -136,6 +151,15 @@ func isPrivateOrLoopback(ip net.IP) bool { return false } +func allowAnyHost() bool { + switch strings.ToLower(strings.TrimSpace(os.Getenv("ALLOW_ANY_HOST"))) { + case "1", "true", "yes", "on": + return true + default: + return false + } +} + // SanitizeURL strips query parameters whose names match a small set of // well-known credential names so error messages and logs that echo the // request URL do not leak API keys. Anything else is preserved. The diff --git a/internal/utility/ssrf.go b/internal/utility/ssrf.go index 252eb9d87d..b7ab1aad1c 100644 --- a/internal/utility/ssrf.go +++ b/internal/utility/ssrf.go @@ -22,6 +22,7 @@ import ( "net" "net/http" "net/url" + "os" "sort" "strings" "time" @@ -58,6 +59,7 @@ func AssertURLSafe(rawURL string) (hostname, resolvedIP string, err error) { return "", "", fmt.Errorf("URL is missing a host.") } + allowAny := allowAnyHost() addrs, err := LookupHost(hostname) if err != nil { return "", "", fmt.Errorf("Could not resolve hostname '%s': %v", hostname, err) @@ -71,7 +73,7 @@ func AssertURLSafe(rawURL string) (hostname, resolvedIP string, err error) { if ip == nil { return "", "", fmt.Errorf("Could not parse resolved address '%s' for hostname '%s'.", addr, hostname) } - if !isGlobalIP(effectiveIP(ip)) { + if !allowAny && !isGlobalIP(effectiveIP(ip)) { return "", "", fmt.Errorf("URL resolves to a non-public address (%s), which is not allowed.", ip.String()) } if resolvedIP == "" { @@ -81,6 +83,15 @@ func AssertURLSafe(rawURL string) (hostname, resolvedIP string, err error) { return hostname, resolvedIP, nil } +func allowAnyHost() bool { + switch strings.ToLower(strings.TrimSpace(os.Getenv("ALLOW_ANY_HOST"))) { + case "1", "true", "yes", "on": + return true + default: + return false + } +} + func schemeAllowed(scheme string) bool { for _, s := range AllowedURLSchemes { if s == scheme {