{"record":{"id":"c026b4a4a90fa468","repo":"JuliusBrussee/caveman","slug":"ssrf-invalid-dial-address","errorCode":null,"errorMessage":"ssrf: invalid dial address","messagePattern":"ssrf: invalid dial address","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/ssrf/ssrf.go","lineNumber":398,"sourceCode":"\tdialer := &net.Dialer{\n\t\tTimeout:   connectTimeout,\n\t\tKeepAlive: 30 * time.Second,\n\t}\n\treturn dialContextWith(cfg, net.DefaultResolver.LookupNetIP, dialer.DialContext)\n}\n\ntype lookupNetIPFunc func(context.Context, string, string) ([]netip.Addr, error)\ntype rawDialContextFunc func(context.Context, string, string) (net.Conn, error)\n\n// dialContextWith resolves each hostname exactly once, validates every returned\n// address, then dials a validated IP literal. net.Transport passes hostnames to\n// DialContext; validating the hostname and handing it to net.Dialer would cause\n// a second DNS lookup and reopen the DNS-rebinding window.\nfunc dialContextWith(cfg Config, lookup lookupNetIPFunc, dial rawDialContextFunc) func(ctx context.Context, network, addr string) (net.Conn, error) {\n\treturn func(ctx context.Context, network, addr string) (net.Conn, error) {\n\t\thost, port, err := net.SplitHostPort(addr)\n\t\tif err != nil {\n\t\t\treturn nil, errors.New(\"ssrf: invalid dial address\")\n\t\t}\n\t\tif err := validateHostInput(host); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif cfg.ManagedMode && port != \"443\" {\n\t\t\treturn nil, errors.New(\"ssrf: managed mode requires port 443\")\n\t\t}\n\n\t\tif parsed, parseErr := netip.ParseAddr(host); parseErr == nil {\n\t\t\tparsed = parsed.WithZone(\"\").Unmap()\n\t\t\tif err := checkAddr(parsed, host, port, cfg); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\treturn dial(ctx, network, net.JoinHostPort(parsed.String(), port))\n\t\t}\n\n\t\tif strings.EqualFold(host, \"localhost\") && !(!cfg.ManagedMode && isInAllowList(host, port, cfg.AllowList)) {\n\t\t\treturn nil, fmt.Errorf(\"ssrf: host %q is blocked (loopback)\", host)","sourceCodeStart":380,"sourceCodeEnd":416,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/766dce6b1394ebb56a3090748d5a0240a5aefb36/shared/platform/ssrf/ssrf.go#L380-L416","documentation":"Dial-time enforcement inside dialContextWith: the address being connected to failed validation before the dialer was invoked. The hostname was resolved and every returned address is checked against SSRF block lists, then only a validated IP literal is dialed; this error means that validation failed at dial time.","triggerScenarios":"Thrown at shared/platform/ssrf/ssrf.go:398 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Compare the dial-time failure with the pre-flight ValidateURL result to find where DNS rebinding or a changed hostname bypassed pre-flight checks","Pin or re-validate the upstream configuration before retrying the connection"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"766dce6b1394ebb56a3090748d5a0240a5aefb36","analyzedAt":"2026-08-18T03:14:35.516Z","contentChangedAt":"2026-08-18T03:14:35.516Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}