{"record":{"id":"b51365fba442bd62","repo":"larksuite/cli","slug":"local-internal-host-is-not-allowed","errorCode":null,"errorMessage":"local/internal host is not allowed","messagePattern":"local/internal host is not allowed","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/validate/url.go","lineNumber":101,"sourceCode":"\tif err != nil || u == nil {\n\t\treturn fmt.Errorf(\"invalid URL\")\n\t}\n\tif u.Scheme != \"http\" && u.Scheme != \"https\" {\n\t\treturn fmt.Errorf(\"only http/https URLs are supported\")\n\t}\n\t_, err = resolveDownloadHost(ctx, u.Hostname(), net.DefaultResolver.LookupIP)\n\treturn err\n}\n\ntype downloadLookupIPFunc func(context.Context, string, string) ([]net.IP, error)\n\nfunc resolveDownloadHost(ctx context.Context, rawHost string, lookupIP downloadLookupIPFunc) ([]net.IP, error) {\n\thost := strings.TrimSpace(strings.ToLower(rawHost))\n\tif host == \"\" {\n\t\treturn nil, fmt.Errorf(\"URL host is required\")\n\t}\n\tif host == \"localhost\" || strings.HasSuffix(host, \".localhost\") {\n\t\treturn nil, fmt.Errorf(\"local/internal host is not allowed\")\n\t}\n\tif ip := net.ParseIP(host); ip != nil {\n\t\tif isRestrictedDownloadIP(ip) {\n\t\t\treturn nil, fmt.Errorf(\"local/internal host is not allowed\")\n\t\t}\n\t\treturn []net.IP{ip}, nil\n\t}\n\tif lookupIP == nil {\n\t\tlookupIP = net.DefaultResolver.LookupIP\n\t}\n\tips, err := lookupIP(ctx, \"ip\", host)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to resolve host\")\n\t}\n\tif len(ips) == 0 {\n\t\treturn nil, fmt.Errorf(\"failed to resolve host\")\n\t}\n\tfor _, ip := range ips {","sourceCodeStart":83,"sourceCodeEnd":119,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/validate/url.go#L83-L119","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nerr := validate.ValidateDownloadSourceURL(ctx, \"http://localhost:8080/file.zip\")\n// after\nerr := validate.ValidateDownloadSourceURL(ctx, \"https://cdn.example.com/file.zip\")","handlingStrategy":"validation","validationCode":"u, err := url.Parse(downloadURL)\nif err == nil && u != nil {\n    h := strings.ToLower(strings.TrimSpace(u.Hostname()))\n    if h == \"localhost\" || strings.HasSuffix(h, \".localhost\") {\n        return fmt.Errorf(\"refusing to download from local host %q\", h)\n    }\n}","typeGuard":"func isPublicDownloadHost(rawURL string) bool {\n    u, err := url.Parse(rawURL)\n    if err != nil || u == nil || (u.Scheme != \"http\" && u.Scheme != \"https\") {\n        return false\n    }\n    h := strings.ToLower(strings.TrimSpace(u.Hostname()))\n    if h == \"\" || h == \"localhost\" || strings.HasSuffix(h, \".localhost\") {\n        return false\n    }\n    if ip := net.ParseIP(h); ip != nil {\n        return !ip.IsLoopback() && !ip.IsPrivate() && !ip.IsLinkLocalUnicast() && !ip.IsUnspecified()\n    }\n    return true\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["ssrf","network","security","url-validation"],"backgroundTag":"localhost-host-blocked","analyzedSha":"7fd6ef3c07182257ce776cdc5a614e122d5bd4b3","analyzedAt":"2026-09-04T21:17:44.649Z","contentChangedAt":"2026-09-04T21:17:44.649Z","schemaVersion":2},"datasetVersion":"2026-09-12T02:17:10.037Z"}