larksuite/cli · error
local/internal host is not allowed
Error message
local/internal host is not allowed
What it means
The download-source validator rejects any URL whose hostname is literally "localhost" or ends in ".localhost" before any DNS lookup happens. This is part of the SSRF protection in resolveDownloadHost: untrusted download URLs must point at public hosts only, so local/loopback names are blocked unconditionally. The check is purely on the lowercased, trimmed hostname string, so no network access occurs.
Source
Thrown at internal/validate/url.go:101
if err != nil || u == nil {
return fmt.Errorf("invalid URL")
}
if u.Scheme != "http" && u.Scheme != "https" {
return fmt.Errorf("only http/https URLs are supported")
}
_, err = resolveDownloadHost(ctx, u.Hostname(), net.DefaultResolver.LookupIP)
return err
}
type downloadLookupIPFunc func(context.Context, string, string) ([]net.IP, error)
func resolveDownloadHost(ctx context.Context, rawHost string, lookupIP downloadLookupIPFunc) ([]net.IP, error) {
host := strings.TrimSpace(strings.ToLower(rawHost))
if host == "" {
return nil, fmt.Errorf("URL host is required")
}
if host == "localhost" || strings.HasSuffix(host, ".localhost") {
return nil, fmt.Errorf("local/internal host is not allowed")
}
if ip := net.ParseIP(host); ip != nil {
if isRestrictedDownloadIP(ip) {
return nil, fmt.Errorf("local/internal host is not allowed")
}
return []net.IP{ip}, nil
}
if lookupIP == nil {
lookupIP = net.DefaultResolver.LookupIP
}
ips, err := lookupIP(ctx, "ip", host)
if err != nil {
return nil, fmt.Errorf("failed to resolve host")
}
if len(ips) == 0 {
return nil, fmt.Errorf("failed to resolve host")
}
for _, ip := range ips {View on GitHub (pinned to 7fd6ef3c07)
Solutions
- Replace the localhost / *.localhost hostname with the public production hostname the download is meant to come from.
- If you need a local test server, expose it via a public tunnel (ngrok, cloudflared) and use the resulting public https URL.
- If hosting your own mirror, deploy it on a host with a public IP and ensure DNS resolves it to a non-restricted address.
- Check environment-specific config (e.g. a base-URL or mirror setting) for leftover local development values.
Example fix
// before err := validate.ValidateDownloadSourceURL(ctx, "http://localhost:8080/file.zip") // after err := validate.ValidateDownloadSourceURL(ctx, "https://cdn.example.com/file.zip")
Defensive patterns
Strategy: validation
Validate before calling
u, err := url.Parse(downloadURL)
if err == nil && u != nil {
h := strings.ToLower(strings.TrimSpace(u.Hostname()))
if h == "localhost" || strings.HasSuffix(h, ".localhost") {
return fmt.Errorf("refusing to download from local host %q", h)
}
} Type guard
func isPublicDownloadHost(rawURL string) bool {
u, err := url.Parse(rawURL)
if err != nil || u == nil || (u.Scheme != "http" && u.Scheme != "https") {
return false
}
h := strings.ToLower(strings.TrimSpace(u.Hostname()))
if h == "" || h == "localhost" || strings.HasSuffix(h, ".localhost") {
return false
}
if ip := net.ParseIP(h); ip != nil {
return !ip.IsLoopback() && !ip.IsPrivate() && !ip.IsLinkLocalUnicast() && !ip.IsUnspecified()
}
return true
} Prevention
- Keep local test-server URLs out of committed config; use env-specific overrides.
- Validate any user-supplied download URL with this check before passing it along.
- Prefer https public hostnames in all download configuration.
- When testing against local servers, use a tunnel that yields a public URL rather than editing config to localhost.
When it happens
Trigger: Calling ValidateDownloadSourceURL(ctx, url) with a URL like http://localhost:8080/file, http://foo.localhost/file, or HTTPS://LOCALHOST/x (case-insensitive, whitespace-trimmed). Also raised inside NewDownloadHTTPClient redirect handling when a redirect target resolves to a *.localhost host, and via proxyAwareDownloadTransport.RoundTrip when the request URL host is a localhost name.
Common situations: Pointing a download/config option at a locally running dev server (localhost:3000) or a container-internal service (api.localhost); testing the CLI against a local mock; switching an environment from a local stub to production without updating the URL; IPv6 ::1 typed differently is caught by the IP branch instead.
Related errors
- blocked redirect target: %w
- blocked download target: local/internal host is not allowed
- official skills index redirected to non-HTTPS URL: %s
- only http/https URLs are supported
- redirect from https to http is not allowed
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/b51365fba442bd62.
Report an issue: GitHub.