{"record":{"id":"2f3d1c3b883e43a1","repo":"Tencent/WeKnora","slug":"w-w-2f3d1c","errorCode":null,"errorMessage":"%w: %w","messagePattern":"%w: %w","errorType":"http","errorClass":"ErrSSRFRedirectBlocked","httpStatus":null,"severity":"error","filePath":"internal/utils/security.go","lineNumber":728,"sourceCode":"\n\t\t// Strip credentials when the redirect crosses hosts so connector\n\t\t// tokens (e.g. Yuque X-Auth-Token) cannot leak to a third party.\n\t\tif len(via) > 0 && !sameHTTPOrigin(via[0].URL, req.URL) {\n\t\t\tstripRedirectSensitiveHeaders(req)\n\t\t}\n\n\t\t// Validate the redirect target URL for SSRF (whitelist-aware).\n\t\t// Even whitelisted hosts must use http/https to prevent scheme-based attacks.\n\t\tredirectScheme := strings.ToLower(req.URL.Scheme)\n\t\tif redirectScheme != \"http\" && redirectScheme != \"https\" {\n\t\t\treturn fmt.Errorf(\"%w: invalid scheme %s\", ErrSSRFRedirectBlocked, redirectScheme)\n\t\t}\n\t\tredirectHost := req.URL.Hostname()\n\t\tif redirectHost != \"\" && IsSSRFWhitelisted(redirectHost) {\n\t\t\treturn nil\n\t\t}\n\t\tif err := validateURLForSSRFForOutbound(req.URL.String()); err != nil {\n\t\t\treturn fmt.Errorf(\"%w: %w\", ErrSSRFRedirectBlocked, err)\n\t\t}\n\n\t\treturn nil\n\t}\n}\n\n// SSRFValidatingRoundTripper enforces the URL policy for every outbound\n// request, including URLs discovered at runtime by SDKs (for example OAuth\n// metadata) that never passed through an application handler. Dial-time checks\n// remain necessary to pin DNS answers and cover transports that cannot accept\n// this wrapper directly.\ntype SSRFValidatingRoundTripper struct {\n\tBase http.RoundTripper\n}\n\nfunc (t *SSRFValidatingRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {\n\tif req == nil || req.URL == nil {\n\t\treturn nil, fmt.Errorf(\"outbound request blocked: request URL is required\")","sourceCodeStart":710,"sourceCodeEnd":746,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/utils/security.go#L710-L746","documentation":"When a redirect target is not on the SSRF whitelist, it is run through validateURLForSSRFForOutbound; if that fails (private IP, blocked host, etc.) the policy returns ErrSSRFRedirectBlocked wrapped with the underlying validation error ('%w: %w'), so both the sentinel and the cause are matchable via errors.Is.","triggerScenarios":"Following a redirect whose Location URL fails validateURLForSSRFForOutbound — e.g. target resolves to loopback, RFC1918, or link-local addresses — while using newSSRFCheckRedirect.","commonSituations":"Open redirects on third-party APIs pointing at internal hosts, DNS rebinding attempts, or staging services that redirect to internal-only backends.","solutions":["Handle with errors.Is(err, secutils.ErrSSRFRedirectBlocked); log the wrapped cause for the specific reason.","Fix the upstream so it does not redirect to private/internal addresses.","If the target host is legitimately required, add it to the SSRF whitelist after security review (whitelisted hosts bypass outbound validation).","Resolve the public hostname instead of an internal one on the redirecting service."],"exampleFix":"// before\nresp, err := client.Do(req)\nreturn err\n// after\nif errors.Is(err, secutils.ErrSSRFRedirectBlocked) {\n    return fmt.Errorf(\"redirect target failed SSRF validation: %v\", err)\n}","handlingStrategy":"try-catch","validationCode":"// pre-resolve and check the redirect target\nips, _ := net.LookupHost(host)\nfor _, ip := range ips { if isPrivate(ip) { return fmt.Errorf(\"redirect target %s is private\", ip) } }","typeGuard":"func isRedirectSSRFBlocked(err error) bool { return errors.Is(err, secutils.ErrSSRFRedirectBlocked) }","tryCatchPattern":"if errors.Is(err, secutils.ErrSSRFRedirectBlocked) {\n    // wrapped cause explains why: private IP, blocked host, etc.\n    return fmt.Errorf(\"redirect failed SSRF policy: %w\", err)\n}","preventionTips":["Match the sentinel with errors.Is and log the wrapped cause.","Avoid following redirects from untrusted endpoints.","Whitelist only hosts that are legitimately required.","Watch logs for repeated blocks — may indicate probing."],"tags":["security","ssrf","http","redirect"],"backgroundTag":"ssrf-redirect-blocked","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}