{"record":{"id":"d311779d639dfa3b","repo":"Tencent/WeKnora","slug":"docreader-address-failed-ssrf-validation-w","errorCode":null,"errorMessage":"docreader address failed SSRF validation: %w","messagePattern":"docreader address failed SSRF validation: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/infrastructure/docparser/http_parser.go","lineNumber":69,"sourceCode":"\tMarkdownContent string            `json:\"markdown_content\"`\n\tImageRefs       []httpImageRef    `json:\"image_refs,omitempty\"`\n\tImageDirPath    string            `json:\"image_dir_path,omitempty\"`\n\tMetadata        map[string]string `json:\"metadata,omitempty\"`\n\tError           string            `json:\"error,omitempty\"`\n}\n\n// HTTPDocumentReader implements DocumentReader over HTTP/JSON.\ntype HTTPDocumentReader struct {\n\tmu      sync.RWMutex\n\tbaseURL string\n\tclient  *http.Client\n}\n\nfunc NewHTTPDocumentReader(baseURL string) (*HTTPDocumentReader, error) {\n\tbaseURL = strings.TrimSuffix(strings.TrimSpace(baseURL), \"/\")\n\tif baseURL != \"\" {\n\t\tif err := secutils.ValidateURLForSSRF(baseURL); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"docreader address failed SSRF validation: %w\", err)\n\t\t}\n\t}\n\tclientCfg := secutils.DefaultSSRFSafeHTTPClientConfig()\n\tclientCfg.Timeout = 5 * time.Minute\n\tp := &HTTPDocumentReader{\n\t\tbaseURL: baseURL,\n\t\tclient:  secutils.NewSSRFSafeHTTPClient(clientCfg),\n\t}\n\tif p.baseURL != \"\" {\n\t\tlogger.Infof(context.Background(), \"INFO: HTTP docreader base URL: %s\", p.baseURL)\n\t}\n\treturn p, nil\n}\n\nfunc (p *HTTPDocumentReader) base() string {\n\tp.mu.RLock()\n\tdefer p.mu.RUnlock()\n\treturn p.baseURL","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/infrastructure/docparser/http_parser.go#L51-L87","documentation":"NewHTTPDocumentReader validates the supplied docreader base URL with secutils.ValidateURLForSSRF before storing it and returns this wrapped error when the URL is not SSRF-safe (e.g. private/loopback/link-local addresses, disallowed scheme, or otherwise forbidden target). This guards against server-side request forgery where an attacker-controlled docreader address would make the service fetch from internal infrastructure.","triggerScenarios":"Constructing HTTPDocumentReader via NewHTTPDocumentReader (from initDocReaderClient or ResolveDocumentReader) with a baseURL that points at localhost/127.0.0.1, a private RFC1918 address, a metadata endpoint like 169.254.169.254, a non-http(s) scheme, or a malformed URL that fails validation.","commonSituations":"Local development pointing the docreader at http://localhost:8000 which SSRF policy rejects in production-mode builds; operator configures an internal cluster IP or k8s service name that the validator classifies as private; config/env value contains a typo or extra scheme making validation fail; dynamic address resolution (ResolveDocumentReader) yields a private IP for a hostname.","solutions":["Point the docreader address at a public/allowed URL that passes ValidateURLForSSRF (proper public host, https scheme)","Read the wrapped validation error to see exactly which rule failed (scheme, private IP, loopback, etc.) and adjust the URL accordingly","If the docreader legitimately runs internally, deploy it behind an approved public gateway/proxy endpoint rather than passing the internal address directly","Fix malformed URLs: ensure a single scheme (http:// or https://), no whitespace, no trailing garbage before validation","If this is a trusted local dev setup, use the configuration mechanism intended for dev mode instead of bypassing validation"],"exampleFix":"// before\nreader, err := docparser.NewHTTPDocumentReader(\"http://127.0.0.1:8000\")\n// error: docreader address failed SSRF validation: loopback address forbidden\n// after\nreader, err := docparser.NewHTTPDocumentReader(\"https://docreader.example.com\")","handlingStrategy":"validation","validationCode":"url := strings.TrimSuffix(strings.TrimSpace(cfg.DocreaderBaseURL), \"/\")\nif url != \"\" {\n    if err := secutils.ValidateURLForSSRF(url); err != nil {\n        return nil, fmt.Errorf(\"configured docreader url rejected: %w\", err)\n    }\n}\nreader, err := docparser.NewHTTPDocumentReader(url)","typeGuard":null,"tryCatchPattern":"reader, err := docparser.NewHTTPDocumentReader(cfg.DocreaderBaseURL)\nif err != nil {\n    var ssrfErr error\n    if strings.Contains(err.Error(), \"failed SSRF validation\") {\n        return nil, fmt.Errorf(\"config error: docreader url %q is not SSRF-safe: %w\", cfg.DocreaderBaseURL, err)\n    }\n    _ = ssrfErr\n    return nil, err\n}","preventionTips":["Never configure localhost, 127.0.0.1, or private/metadata IPs as the docreader address in shared environments","Expose internal docreader instances through an approved public gateway/ingress","Validate any user- or discovery-supplied docreader URL at config-load time, before constructing the reader","Use https URLs with explicit hosts; avoid scheme typos and stray whitespace"],"tags":["ssrf","security","http","url-validation"],"backgroundTag":"ssrf-validation-failed","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}