{"record":{"id":"defd43690cfb340c","repo":"Tencent/WeKnora","slug":"outbound-request-blocked-by-ssrf-policy-w","errorCode":null,"errorMessage":"outbound request blocked by SSRF policy: %w","messagePattern":"outbound request blocked by SSRF policy: %w","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/utils/security.go","lineNumber":752,"sourceCode":"\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\")\n\t}\n\tif t == nil || t.Base == nil {\n\t\treturn nil, fmt.Errorf(\"outbound request blocked: base transport is required\")\n\t}\n\tif err := validateURLForSSRFForOutbound(req.URL.String()); err != nil {\n\t\treturn nil, fmt.Errorf(\"outbound request blocked by SSRF policy: %w\", err)\n\t}\n\treturn t.Base.RoundTrip(req)\n}\n\n// NewSSRFSafeHTTPClientWithTransport wraps a caller-supplied transport in an\n// *http.Client carrying the given timeout and the SSRF-aware redirect policy.\n// Pass a transport from NewSSRFSafeTransport (optionally shared across clients)\n// to reuse a single connection pool while keeping per-client timeouts.\nfunc NewSSRFSafeHTTPClientWithTransport(\n\tconfig SSRFSafeHTTPClientConfig, transport http.RoundTripper,\n) *http.Client {\n\tif transport == nil {\n\t\ttransport = NewSSRFSafeTransport(config)\n\t}\n\treturn &http.Client{\n\t\tTimeout:       config.Timeout,\n\t\tTransport:     &SSRFValidatingRoundTripper{Base: transport},\n\t\tCheckRedirect: newSSRFCheckRedirect(config.MaxRedirects),","sourceCodeStart":734,"sourceCodeEnd":770,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/utils/security.go#L734-L770","documentation":"The request URL failed the library's SSRF policy check performed by validateURLForSSRFForOutbound before the request is handed to the base transport. The library blocks outbound requests to disallowed destinations (private/loopback/link-local IPs, restricted hosts, restricted ports, non-safe schemes) to prevent server-side request forgery. The wrapped cause (%w) explains exactly which rule was violated.","triggerScenarios":"RoundTrip (e.g. via an http.Client configured with SSRFValidatingRoundTripper) invoked with a request whose URL fails validateURLForSSRFForOutbound: pointing at 127.0.0.1/localhost, private RFC1918 ranges, metadata endpoints (169.254.169.254), file/non-http schemes, restricted ports, or a hostname that resolves to a blocked IP. TestSSRFValidatingRoundTripperUsesOutboundCache reaches this path whenever the URL is not policy-compliant.","commonSituations":"Calling internal microservice endpoints like http://localhost:8080 from behind the SSRF-safe client; fetching user-supplied URLs that target internal addresses; cloud-metadata lookups from inside the guarded code path; tests pointing at a local httptest server.","solutions":["Inspect the wrapped error (errors.Unwrap / %v of the chain) to see which SSRF rule fired, then change the target URL to a public, allowed host.","If the destination is legitimately internal, add it to the SSRF whitelist mechanism provided by the library (whitelisted hosts bypass the checks) rather than disabling validation.","For tests, point the request at the approved test host or inject a whitelist entry instead of using 127.0.0.1.","Validate the URL yourself with ValidateURLForSSRF before building the request to get an earlier, clearer failure."],"exampleFix":"// before\nreq, _ := http.NewRequest(\"GET\", \"http://169.254.169.254/latest/meta-data/\", nil)\nresp, err := client.Do(req) // blocked by SSRF policy\n\n// after\nif err := utils.ValidateURLForSSRF(\"https://api.example.com/v1/info\"); err != nil { /* handle */ }\nreq, _ := http.NewRequest(\"GET\", \"https://api.example.com/v1/info\", nil)\nresp, err := client.Do(req)","handlingStrategy":"validation","validationCode":"if err := utils.ValidateURLForSSRF(targetURL); err != nil {\n    return fmt.Errorf(\"refusing to fetch %s: %w\", targetURL, err)\n}\nreq, _ := http.NewRequest(\"GET\", targetURL, nil)","typeGuard":null,"tryCatchPattern":"resp, err := client.Do(req)\nif err != nil {\n    var blocked *fmt.Errorf\n    if strings.Contains(err.Error(), \"blocked by SSRF policy\") {\n        // log the policy violation, do not retry — it will keep failing\n        return nil, fmt.Errorf(\"target rejected by SSRF policy: %w\", err)\n    }\n    return nil, err\n}","preventionTips":["Run ValidateURLForSSRF on any user-supplied URL before building the request.","Never point the SSRF-safe client at localhost/private IPs in production or tests without whitelisting.","Keep a whitelist for legitimate internal hosts instead of bypassing the guard.","Log unwrapped policy errors to identify which SSRF rule fired."],"tags":["ssrf","security-policy","http-request","url-validation"],"backgroundTag":"ssrf-request-blocked","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}