{"record":{"id":"057df1d8420bf887","repo":"Tencent/WeKnora","slug":"invalid-scheme-s-only-http-https-allowed","errorCode":null,"errorMessage":"invalid scheme: %s (only http/https allowed)","messagePattern":"invalid scheme: (.+?) \\(only http/https allowed\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/utils/security.go","lineNumber":1205,"sourceCode":"\tif !strings.Contains(normalized, \"://\") {\n\t\tnormalized = \"https://\" + normalized\n\t}\n\n\tparsed, err := url.Parse(normalized)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"invalid URL: %w\", err)\n\t}\n\n\thostname := parsed.Hostname()\n\tif hostname == \"\" {\n\t\treturn fmt.Errorf(\"URL has no hostname\")\n\t}\n\n\t// A whitelist relaxes host/IP restrictions only. It must never turn other\n\t// schemes (file://, gopher://, etc.) into valid outbound request targets.\n\tscheme := strings.ToLower(parsed.Scheme)\n\tif scheme != \"http\" && scheme != \"https\" {\n\t\treturn fmt.Errorf(\"invalid scheme: %s (only http/https allowed)\", scheme)\n\t}\n\n\t// If the host is whitelisted, skip the heavy checks.\n\tif IsSSRFWhitelisted(hostname) {\n\t\treturn nil\n\t}\n\n\t// Delegate to the full SSRF validation (uses the normalised URL).\n\tif safe, reason := isSSRFSafeURL(normalized); !safe {\n\t\treturn fmt.Errorf(\"SSRF validation failed: %s\", reason)\n\t}\n\treturn nil\n}\n\n// IsSystemProxy 判断是否为系统代理\nfunc IsSystemProxy(host string) bool {\n\tproxyCfg := httpproxy.FromEnvironment()\n\tfor _, proxyUrl := range []string{","sourceCodeStart":1187,"sourceCodeEnd":1223,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/utils/security.go#L1187-L1223","documentation":"ValidateURLForSSRF only permits http and https schemes. Any other scheme — file://, gopher://, ftp://, dict://, ldap:// — is rejected with 'invalid scheme'. This is a core SSRF defense: a whitelist relaxes host/IP restrictions only and must never legitimize dangerous non-HTTP schemes that can reach local files or internal services.","triggerScenarios":"A storage endpoint or connectivity-check URL uses a non-http(s) scheme, e.g. 'file:///etc/passwd', 'gopher://127.0.0.1:70', 'ftp://files.example.com', passed to newS3Client, newOSSClient, newMinioClient, newKS3Client, NewObsFileService, or CheckObsConnectivity.","commonSituations":"Attempting to point a storage client at a local file path, copying a non-HTTP service URL into the endpoint config, or an attacker-controlled URL reaching the validation function during an SSRF probe.","solutions":["Use https:// (or http:// for explicitly internal/trusted endpoints) as the URL scheme","Do not use the storage client for local file access — read files directly with os.Open instead","Verify the endpoint config value wasn't accidentally prefixed with the wrong scheme","Note the scheme check is case-insensitive (HTTP:// is fine); the scheme itself is the problem, not casing"],"exampleFix":"// before\nendpoint := \"file:///data/export\"\nclient, err := newOSSClient(endpoint, ...)\n// after\nendpoint := \"https://oss.example.com\"\nclient, err := newOSSClient(endpoint, ...)","handlingStrategy":"validation","validationCode":"n := endpoint\nif !strings.Contains(n, \"://\") { n = \"https://\" + n }\nu, err := url.Parse(n)\nif err != nil {\n    return err\n}\ns := strings.ToLower(u.Scheme)\nif s != \"http\" && s != \"https\" {\n    return fmt.Errorf(\"scheme %q not allowed; use http/https\", s)\n}","typeGuard":"func isHTTPScheme(raw string) bool {\n    if !strings.Contains(raw, \"://\") { raw = \"https://\" + raw }\n    u, err := url.Parse(raw)\n    return err == nil && (strings.EqualFold(u.Scheme, \"http\") || strings.EqualFold(u.Scheme, \"https\"))\n}","tryCatchPattern":null,"preventionTips":["Only point storage clients at http(s) endpoints","Use os/io APIs for local files, never file:// through a storage client","Whitelists do not relax the scheme check — fix the scheme itself","Sanitize any user-supplied URL before it reaches ValidateURLForSSRF"],"tags":["ssrf","scheme","security","url-validation"],"backgroundTag":"invalid-url-scheme","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}