{"record":{"id":"eb316d3718e628a9","repo":"ory/hydra","slug":"key-s-validation-is-failing","errorCode":null,"errorMessage":"key %s validation is failing","messagePattern":"key (.+?) validation is failing","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"oryx/ipx/ip_validator.go","lineNumber":34,"sourceCode":"\n\t\"github.com/pkg/errors\"\n)\n\n// IsAssociatedIPAllowedWhenSet is a wrapper for IsAssociatedIPAllowed which returns valid\n// when ipOrHostnameOrURL is empty.\nfunc IsAssociatedIPAllowedWhenSet(ctx context.Context, ipOrHostnameOrURL string) error {\n\tif ipOrHostnameOrURL == \"\" {\n\t\treturn nil\n\t}\n\treturn IsAssociatedIPAllowed(ctx, ipOrHostnameOrURL)\n}\n\n// AreAllAssociatedIPsAllowed fails if one of the pairs is failing.\nfunc AreAllAssociatedIPsAllowed(ctx context.Context, pairs map[string]string) error {\n\tg, ctx := errgroup.WithContext(ctx)\n\tfor key, ipOrHostnameOrURL := range pairs {\n\t\tg.Go(func() error {\n\t\t\treturn errors.Wrapf(IsAssociatedIPAllowed(ctx, ipOrHostnameOrURL), \"key %s validation is failing\", key)\n\t\t})\n\t}\n\treturn g.Wait()\n}\n\n// IsAssociatedIPAllowed returns nil for a domain (with NS lookup), IP, or IPv6 address if it\n// does not resolve to a private IP subnet. This is a first level of defense against\n// SSRF attacks by disallowing any domain or IP to resolve to a private network range.\n//\n// Please keep in mind that validations for domains is valid only when looking up.\n// A malicious actor could easily update the DSN record post validation to point\n// to an internal IP\nfunc IsAssociatedIPAllowed(ctx context.Context, ipOrHostnameOrURL string) error {\n\tipOrHostname := ipOrHostnameOrURL\n\tif parsed, err := url.ParseRequestURI(ipOrHostnameOrURL); err == nil {\n\t\tipOrHostname = parsed.Hostname()\n\t}\n","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/ory/hydra/blob/4174065ffb052799890f7480f5360a877a67ffc1/oryx/ipx/ip_validator.go#L16-L52","documentation":"AreAllAssociatedIPsAllowed validates a map of key -> IP/hostname/URL pairs concurrently with an errgroup. If any single pair fails IsAssociatedIPAllowed (e.g. the address is a non-permitted destination or DNS resolution fails), that error is wrapped as 'key <key> validation is failing' and returned, identifying which entry in the map was rejected.","triggerScenarios":"Calling AreAllAssociatedIPsAllowed with a map where at least one value is an IP/hostname/URL that fails IsAssociatedIPAllowed — e.g. points to a private/loopback address when such destinations are forbidden, or a hostname whose NS lookup fails.","commonSituations":"Validating claimed redirect/allowed endpoints (e.g. courier SMTP or webhook configuration) where one configured host resolves to localhost or an internal IP; typo'd hostnames that fail DNS; SSRF-protection rejecting internal endpoints in dev/staging configs.","solutions":["Look at the wrapped inner error to see which check failed for that key","Replace the offending value for that key with a public, resolvable IP/hostname","If the endpoint is legitimately internal (dev environment), relax/adjust the IP allowlist configuration","Verify DNS resolution for the hostname (dig/nslookup) if the failure is lookup-related"],"exampleFix":"// before\npairs := map[string]string{\"webhook\": \"http://127.0.0.1:9090/callback\"}\nerr := ipx.AreAllAssociatedIPsAllowed(ctx, pairs) // fails: loopback not permitted\n// after\npairs := map[string]string{\"webhook\": \"https://api.example.com/callback\"}\nerr := ipx.AreAllAssociatedIPsAllowed(ctx, pairs)","handlingStrategy":"validation","validationCode":"for key, addr := range pairs {\n    if net.ParseIP(addr) == nil {\n        if _, err := net.LookupHost(addr); err != nil {\n            return fmt.Errorf(\"key %s: cannot resolve %q\", key, addr)\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":"if err := ipx.AreAllAssociatedIPsAllowed(ctx, pairs); err != nil {\n    var keyErr *fmt.WrapError\n    if errors.As(err, &keyErr) {\n        log.Printf(\"failed pair identified: %v\", err) // message names the key\n    }\n}","preventionTips":["Pre-resolve all hostnames before validation to catch DNS issues early","Reject loopback/private addresses in your own config validation","Keep a curated allowlist of public endpoints for webhooks/callbacks","Log the full wrapped error — it names the offending key"],"tags":["network","ssrf","dns","validation"],"backgroundTag":"ip-not-permitted","analyzedSha":"4174065ffb052799890f7480f5360a877a67ffc1","analyzedAt":"2026-09-03T14:52:41.581Z","contentChangedAt":"2026-09-03T14:52:41.581Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}