{"record":{"id":"08d128a4032cde16","repo":"micro/go-micro","slug":"push-callback-host-q-resolves-to-a-blocked-addres","errorCode":null,"errorMessage":"push callback host %q resolves to a blocked address %s","messagePattern":"push callback host %q resolves to a blocked address (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gateway/a2a/pushsecurity.go","lineNumber":51,"sourceCode":"\tswitch u.Scheme {\n\tcase \"http\", \"https\":\n\tdefault:\n\t\treturn fmt.Errorf(\"push callback scheme %q not allowed (want http or https)\", u.Scheme)\n\t}\n\thost := u.Hostname()\n\tif host == \"\" {\n\t\treturn fmt.Errorf(\"push callback url has no host\")\n\t}\n\tips, err := resolvePushHost(host)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"push callback host %q: %w\", host, err)\n\t}\n\tif len(ips) == 0 {\n\t\treturn fmt.Errorf(\"push callback host %q did not resolve\", host)\n\t}\n\tfor _, ip := range ips {\n\t\tif blockedPushIP(ip) {\n\t\t\treturn fmt.Errorf(\"push callback host %q resolves to a blocked address %s\", host, ip)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc resolvePushHost(host string) ([]net.IP, error) {\n\tif ip := net.ParseIP(host); ip != nil {\n\t\treturn []net.IP{ip}, nil\n\t}\n\treturn pushLookupIP(host)\n}\n\n// blockedPushIP reports whether ip is one an outbound push callback must not\n// reach: loopback, private (RFC1918 / ULA), link-local (incl. 169.254.169.254\n// cloud metadata), multicast, or the unspecified address.\nfunc blockedPushIP(ip net.IP) bool {\n\treturn ip == nil ||\n\t\tip.IsLoopback() ||","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/micro/go-micro/blob/24529f140421a11a33b6999ab7944f2021cfd69c/gateway/a2a/pushsecurity.go#L33-L69","documentation":"This is the SSRF protection core of defaultPushURLPolicy: after resolving the callback host, each returned IP is checked with blockedPushIP, and this error is thrown if any address is loopback, private, link-local, multicast, or unspecified. It prevents push notifications from being aimed at internal infrastructure (localhost, metadata services, RFC1918 ranges).","triggerScenarios":"Calling SetPushNotificationConfig with a callback URL whose host resolves to 127.0.0.1, ::1, 10.x/172.16.x/192.168.x, 169.254.x (including cloud metadata 169.254.169.254), 0.0.0.0, or a multicast address.","commonSituations":"Attacker-supplied webhook URL in a multi-tenant app attempting SSRF against the gateway; developer pointing callbacks at a locally running service in a production deployment; DNS rebinding where a public name resolves to a private IP.","solutions":["Use a publicly routable callback host; expose the webhook receiver on a public address.","If internal delivery is genuinely required, deploy an approved public proxy/relay in front of the internal service.","Never accept raw user URLs for push callbacks without your own allowlist of webhook domains.","If this fires unexpectedly on a legitimate host, inspect what the name resolves to (possible rebinding or misconfigured DNS)."],"exampleFix":"// before\ncallback := \"http://localhost:9090/a2a/push\" // blocked\n// after\ncallback := \"https://push.example.com/a2a/push\" // public, routable","handlingStrategy":"validation","validationCode":"addrs, _ := net.LookupHost(host)\nfor _, a := range addrs {\n\tip := net.ParseIP(a)\n\tif ip.IsLoopback() || ip.IsPrivate() || ip.IsLinkLocalUnicast() || ip.IsUnspecified() {\n\t\treturn fmt.Errorf(\"callback host %q resolves to private address %s\", host, ip)\n\t}\n}","typeGuard":"func isPublicIP(ip net.IP) bool {\n\treturn ip != nil && !ip.IsLoopback() && !ip.IsPrivate() &&\n\t\t!ip.IsLinkLocalUnicast() && !ip.IsLinkLocalMulticast() && !ip.IsUnspecified()\n}","tryCatchPattern":null,"preventionTips":["Never pass raw user-supplied webhook URLs to the push API without an allowlist","Host webhook receivers on publicly routable endpoints","Use an outbound relay/proxy if internal delivery is required","Watch for DNS rebinding by re-checking resolution at registration time"],"tags":["security","ssrf","push-notifications","validation"],"backgroundTag":"ssrf-blocked-host","analyzedSha":"24529f140421a11a33b6999ab7944f2021cfd69c","analyzedAt":"2026-09-01T02:52:24.923Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}