{"record":{"id":"83bad85164f6d61f","repo":"Tencent/WeKnora","slug":"invalid-searxng-base-url-w","errorCode":null,"errorMessage":"invalid SearXNG base_url: %w","messagePattern":"invalid SearXNG base_url: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/infrastructure/web_search/searxng.go","lineNumber":56,"sourceCode":"// the service-layer parameter validation and the provider constructor so that\n// \"save\" and \"use\" never disagree.\nfunc ValidateSearxngBaseURL(rawURL string) error {\n\tbase := strings.TrimSpace(rawURL)\n\tif base == \"\" {\n\t\treturn fmt.Errorf(\"base_url is required for SearXNG provider\")\n\t}\n\tparsed, err := url.Parse(base)\n\tif err != nil || parsed.Scheme == \"\" || parsed.Host == \"\" {\n\t\treturn fmt.Errorf(\"invalid SearXNG base_url: must be an absolute http(s) URL\")\n\t}\n\tif parsed.Scheme != \"http\" && parsed.Scheme != \"https\" {\n\t\treturn fmt.Errorf(\"invalid SearXNG base_url scheme: %s\", parsed.Scheme)\n\t}\n\tif parsed.RawQuery != \"\" || parsed.Fragment != \"\" {\n\t\treturn fmt.Errorf(\"invalid SearXNG base_url: must not contain query or fragment\")\n\t}\n\tif err := utils.ValidateURLForSSRF(base); err != nil {\n\t\treturn fmt.Errorf(\"invalid SearXNG base_url: %w\", err)\n\t}\n\treturn nil\n}\n\n// NewSearxngProvider builds a SearXNG provider from tenant parameters.\nfunc NewSearxngProvider(params types.WebSearchProviderParameters) (interfaces.WebSearchProvider, error) {\n\tbase := strings.TrimSpace(params.BaseURL)\n\tif err := ValidateSearxngBaseURL(base); err != nil {\n\t\treturn nil, err\n\t}\n\n\tclient, err := NewSearchHTTPClient(defaultSearxngTimeout, params.ProxyURL)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &SearxngProvider{\n\t\tclient:  client,\n\t\tbaseURL: strings.TrimRight(base, \"/\"),","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/infrastructure/web_search/searxng.go#L38-L74","documentation":"ValidateSearxngBaseURL rejects a configured SearXNG base URL when utils.ValidateURLForSSRF reports it unsafe (private/loopback/link-local hosts, disallowed schemes, etc.). The check exists to prevent server-side request forgery, since the provider will issue HTTP GETs to whatever base URL is configured. The underlying SSRF reason is wrapped via %w so errors.Is/As still works.","triggerScenarios":"Calling NewSearxngProvider with params whose BaseURL points at a private IP (e.g. 127.0.0.1, 10.x, 192.168.x), localhost, metadata endpoints (169.254.169.254), or a non-http(s) scheme that passes earlier checks but fails SSRF validation.","commonSituations":"Self-hosted SearXNG running on localhost or the Docker network (192.168.x) inside a cluster where SSRF policy blocks internal addresses; misconfigured tenant web-search settings pointing at an internal search appliance.","solutions":["Host SearXNG on an address the SSRF validator allows (public DNS name or an explicitly allow-listed host)","Check the wrapped error with errors.Unwrap/errors.Is to see which SSRF rule fired (private range, loopback, scheme)","If self-hosting is intentional, use the library's documented allowlist mechanism to permit the internal host, or deploy SearXNG behind an allowed gateway","Verify the URL has no query/fragment and uses http/https, which are rejected earlier in the same function"],"exampleFix":"// before\nprovider, err := NewSearxngProvider(types.WebSearchProviderParameters{BaseURL: \"http://127.0.0.1:8888\"})\n// after\nprovider, err := NewSearxngProvider(types.WebSearchProviderParameters{BaseURL: \"https://searxng.example.com\"})","handlingStrategy":"validation","validationCode":"func validSearxngBase(u string) bool {\n    p, err := url.Parse(strings.TrimSpace(u))\n    if err != nil || (p.Scheme != \"http\" && p.Scheme != \"https\") || p.RawQuery != \"\" || p.Fragment != \"\" {\n        return false\n    }\n    host := p.Hostname()\n    ip := net.ParseIP(host)\n    if ip != nil && (ip.IsLoopback() || ip.IsPrivate() || ip.IsLinkLocalUnicast()) {\n        return false\n    }\n    return host != \"localhost\" && host != \"\"\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use a public DNS hostname for self-hosted SearXNG instead of localhost/private IPs","Validate base_url at config-load time, not at provider construction","Keep query strings and fragments out of the configured base URL","Familiarize yourself with the SSRF validator's rules and its allowlist mechanism"],"tags":["ssrf","config-validation","security","searxng"],"backgroundTag":"ssrf-url-validation-failed","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}