{"record":{"id":"7583134b7eede497","repo":"larksuite/cli","slug":"url-host-is-required","errorCode":null,"errorMessage":"URL host is required","messagePattern":"URL host is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/validate/url.go","lineNumber":98,"sourceCode":"// ValidateDownloadSourceURL validates a download URL and blocks local/internal targets.\nfunc ValidateDownloadSourceURL(ctx context.Context, rawURL string) error {\n\tu, err := url.Parse(rawURL)\n\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 {","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/validate/url.go#L80-L116","documentation":"resolveDownloadHost normalizes the URL host and requires a non-empty value before performing DNS lookups for SSRF checks. An empty host means the URL had no authority component (or only a fragment/query), so no target can be resolved. Called by ValidateDownloadSourceURL and RoundTrip.","triggerScenarios":"resolveDownloadHost receives rawHost that is empty after TrimSpace/ToLower — i.e. the parsed URL had no host, e.g. \"https://\" alone, or a URL like \"https:///path\".","commonSituations":"Truncated URLs where the domain was cut off during copy-paste, template variables that failed to expand (\"https://$HOST/file\" with HOST unset), or malformed links from documents.","solutions":["Include the full host in the URL: https://example.com/path, not https:///path.","Verify any templated/host variable was actually set before expansion.","Re-copy the URL from the original source in full.","Validate locally: `python3 -c \"import urllib.parse,sys; print(urllib.parse.urlparse(sys.argv[1]).hostname)\" \"$URL\"` should print a host."],"exampleFix":"// before\nHOST=\"\"\nlark-cli download url \"https://$HOST/file.pdf\"\n// after\n: \"${HOST:?HOST must be set}\"\nlark-cli download url \"https://$HOST/file.pdf\"","handlingStrategy":"validation","validationCode":"u, err := url.Parse(rawURL)\nif err != nil || strings.TrimSpace(u.Hostname()) == \"\" {\n    return fmt.Errorf(\"URL must include a host\")\n}","typeGuard":null,"tryCatchPattern":"if err := validate.ValidateDownloadSourceURL(ctx, raw); err != nil {\n    if strings.Contains(err.Error(), \"URL host is required\") {\n        return fmt.Errorf(\"URL is missing its host; include the full domain: %w\", err)\n    }\n    return err\n}","preventionTips":["Always include the full domain: scheme://host/path.","Fail fast on unset variables used to build URLs (: \"${HOST:?\").","Re-copy truncated URLs from the original source.","Check u.Hostname() in your own tooling before handing URLs to the CLI."],"tags":["input-validation","url","download","ssrf"],"backgroundTag":"missing-url-host","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"}