{"record":{"id":"a9f8951477a754a5","repo":"Tencent/WeKnora","slug":"empty-url","errorCode":null,"errorMessage":"empty url","messagePattern":"empty url","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/utils/security.go","lineNumber":1158,"sourceCode":"\t}\n\thost := rawURL\n\tif parsed, perr := parseHostForHint(rawURL); perr == nil && parsed != \"\" {\n\t\thost = parsed\n\t}\n\treturn fmt.Sprintf(\n\t\t\"%s 未通过安全校验：%v。如该地址确实可信，请联系运维在服务端环境变量 \"+\n\t\t\t\"SSRF_WHITELIST_EXTRA 中加入该主机（支持精确域名 / *.example.com 通配 / IP / CIDR），\"+\n\t\t\t\"示例：SSRF_WHITELIST_EXTRA=%s,*.example.com,10.0.0.0/8\",\n\t\tlabel, err, host,\n\t)\n}\n\n// parseHostForHint extracts a hostname from rawURL purely so we can echo\n// it back inside the SSRF hint. Best-effort — returns (\"\", err) for\n// completely unparseable input and the caller falls back to the raw URL.\nfunc parseHostForHint(rawURL string) (string, error) {\n\tif rawURL == \"\" {\n\t\treturn \"\", fmt.Errorf(\"empty url\")\n\t}\n\tnorm := rawURL\n\tif !strings.Contains(norm, \"://\") {\n\t\tnorm = \"https://\" + norm\n\t}\n\tu, err := url.Parse(norm)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn u.Hostname(), nil\n}\n\n// ValidateURLForSSRF is the centralised entry-point that all handlers should\n// call to validate a user-supplied URL. It first checks the SSRF_WHITELIST;\n// whitelisted hosts skip the full isSSRFSafeURL check.\n//\n// rawURL may be a full URL (\"https://example.com/v1\") or a bare host/host:port\n// (for cases like ReconnectDocReader). If a scheme is missing the function","sourceCodeStart":1140,"sourceCodeEnd":1176,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/utils/security.go#L1140-L1176","documentation":"parseHostForHint is a best-effort helper that extracts a hostname from a raw URL to echo back in SSRF error hints. It returns the 'empty url' error when given an empty string. Callers (FormatSSRFError) fall back to the raw URL, so this is usually swallowed and rarely surfaces directly to users.","triggerScenarios":"FormatSSRFError is called with an empty rawURL string — e.g. the SSRF validation failure was recorded before the URL was normalized, or a caller passed an empty config field through to the error formatter.","commonSituations":"Empty endpoint configuration (unset OBS/OSS/S3 URL env var) causing both a validation failure and an empty URL in the error path; logging code calling FormatSSRFError with a blank variable.","solutions":["Ensure the URL being validated is non-empty before calling the SSRF path; fail fast with a clearer 'endpoint not configured' message","Guard the formatter: skip hint generation when rawURL == \"\"","Populate the endpoint config value that is arriving empty"],"exampleFix":"// before\nhint := FormatSSRFError(rawURL, reason) // rawURL == \"\"\n// after\nif rawURL == \"\" {\n    return fmt.Errorf(\"endpoint URL is not configured\")\n}\nhint := FormatSSRFError(rawURL, reason)","handlingStrategy":"validation","validationCode":"if rawURL == \"\" {\n    return errors.New(\"endpoint URL is not configured\")\n}","typeGuard":"func hasURL(u string) bool { return strings.TrimSpace(u) != \"\" }","tryCatchPattern":"hint, err := parseHostForHint(rawURL)\nif err != nil {\n    hint = rawURL // library falls back to raw URL; mirror that\n}","preventionTips":["Fail fast on empty endpoint config before entering SSRF validation","Check required env vars at startup","Treat this as a symptom: the real bug is the empty URL upstream"],"tags":["ssrf","url-parsing","error-hint"],"backgroundTag":"empty-url","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}