//go:build linux || freebsd // +build linux freebsd package transport import ( "net" "testing" ) // TestIsPrivateIP verifies that isPrivateIP correctly blocks private and // reserved ranges, allows loopback and public addresses, and — critically — // detects IPv4-mapped IPv6 representations of private addresses. func TestIsPrivateIP(t *testing.T) { cases := []struct { name string ip string want bool }{ // Loopback — must always pass (localhost is allowed). {"ipv4 loopback", "127.0.0.1", false}, {"ipv6 loopback", "::1", false}, // Plain IPv4 private/reserved — must be blocked. {"ipv4 rfc1918 10/8", "10.0.0.1", true}, {"ipv4 rfc1918 172.16/12", "172.16.0.1", true}, {"ipv4 rfc1918 192.168/16", "192.168.1.1", true}, {"ipv4 link-local", "169.254.0.1", true}, {"ipv4 unspecified", "0.0.0.0", true}, // IPv4-mapped IPv6 — must be normalized and blocked. // This is the SSRF bypass the fix addresses. {"ipv4-mapped private", "::ffff:10.0.0.1", true}, {"ipv4-mapped rfc1918 172.16", "::ffff:172.16.0.1", true}, {"ipv4-mapped rfc1918 192.168", "::ffff:192.168.1.1", true}, {"ipv4-mapped link-local", "::ffff:169.254.0.1", true}, {"ipv4-mapped loopback", "::ffff:127.0.0.1", false}, // Plain IPv6 private/link-local — must be blocked. {"ipv6 unique-local fc00::/7", "fc00::1", true}, {"ipv6 link-local fe80::/10", "fe80::1", true}, // Public addresses — must pass. {"ipv4 public", "8.8.8.8", false}, {"ipv4-mapped public", "::ffff:8.8.8.8", false}, {"ipv6 public", "2606:4700:4700::1111", false}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { ip := net.ParseIP(tc.ip) if ip == nil { t.Fatalf("net.ParseIP(%q) returned nil", tc.ip) } if got := isPrivateIP(ip); got != tc.want { t.Errorf("isPrivateIP(%s) = %v, want %v", tc.ip, got, tc.want) } }) } }