larksuite/cli · error

blocked download target: local/internal host is not allowed

Error message

blocked download target: local/internal host is not allowed

What it means

This is the SSRF guard of the download path: pinDownloadRequestTargetToIP rejects the request when the resolved target IP is nil or is a restricted (loopback, link-local, private, etc.) address. The download flow validates the host once, then pins the connection to that validated IP so DNS cannot be re-resolved to an internal address later. Hitting this error means the destination resolved to a local/internal IP, which the CLI refuses to contact.

Source

Thrown at internal/validate/url.go:389

	}, true
}

func cloneDownloadHTTPTransport(source *http.Transport) *http.Transport {
	cloned := source.Clone()
	if cloned.TLSNextProto == nil {
		if _, ok := source.TLSNextProto["h2"]; ok {
			cloned.ForceAttemptHTTP2 = true
		}
	}
	return cloned
}

func pinDownloadRequestTargetToIP(req *http.Request, targetIP net.IP) (*http.Request, error) {
	if req == nil || req.URL == nil {
		return nil, fmt.Errorf("download request URL is missing")
	}
	if targetIP == nil || isRestrictedDownloadIP(targetIP) {
		return nil, fmt.Errorf("blocked download target: local/internal host is not allowed")
	}

	originalHost := req.URL.Host
	pinnedHost := targetIP.String()
	if port := req.URL.Port(); port != "" {
		pinnedHost = net.JoinHostPort(pinnedHost, port)
	} else if strings.Contains(pinnedHost, ":") {
		pinnedHost = "[" + pinnedHost + "]"
	}

	pinned := req.Clone(req.Context())
	pinnedURL := *req.URL
	pinnedURL.Host = pinnedHost
	pinned.URL = &pinnedURL
	pinned.Host = originalHost
	return pinned, nil
}

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Use a public internet URL for the download target
  2. Verify DNS resolution of the hostname with dig/nslookup; if it returns a private IP, the host is not a valid public download endpoint
  3. If this is a legitimate internal download, use the approved internal transfer mechanism instead of the public download path
  4. Check for typos in the scheme/host (e.g. localhost vs the real host)

Example fix

// before
dlURL := "http://127.0.0.1:8080/file.bin" // blocked
// after
dlURL := "https://open.feishu.cn/file.bin" // public target passes SSRF guard
Defensive patterns

Strategy: validation

Validate before calling

// Pre-check the resolved target before calling the download API
ips, err := net.LookupIP(host)
if err != nil { return err }
for _, ip := range ips {
    if ip.IsLoopback() || ip.IsPrivate() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() {
        return fmt.Errorf("%s resolves to restricted IP %s; use a public URL", host, ip)
    }
}

Type guard

func isPublicIP(ip net.IP) bool {
    return ip != nil && !ip.IsLoopback() && !ip.IsPrivate() && !ip.IsLinkLocalUnicast() && !ip.IsUnspecified()
}

Try / catch

if err := download(url); err != nil && strings.Contains(err.Error(), "local/internal host is not allowed") {
    return fmt.Errorf("download target %s is internal/blocked by SSRF policy: %w", url, err)
}

Prevention

When it happens

Trigger: The URL host resolves to 127.0.0.1/::1, 169.254.x.x, RFC1918 (10/8, 172.16/12, 192.168/16), or another address matched by isRestrictedDownloadIP; or no IP could be determined (targetIP nil).

Common situations: Pointing a download at localhost or an internal service by mistake; a DNS name (or attacker-controlled redirect) resolving to an internal IP; IPv6 loopback addresses; misconfigured download URL in config using an internal hostname.

Related errors


AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04). Data as JSON: /api/errors/1b53852eeff80d22. Report an issue: GitHub.