fix(security): block IPv6 transition ranges for webhooks

This commit is contained in:
naiba
2026-08-11 14:23:14 +00:00
parent 27e0a89aa8
commit d1fcde8e9e
2 changed files with 44 additions and 0 deletions
+2
View File
@@ -40,9 +40,11 @@ var blockedHTTPClientCIDRs = mustParseHTTPClientCIDRs([]string{
"::1/128", "::1/128",
"::ffff:0:0/96", "::ffff:0:0/96",
"64:ff9b::/96", "64:ff9b::/96",
"64:ff9b:1::/48",
"100::/64", "100::/64",
"2001::/23", "2001::/23",
"2001:db8::/32", "2001:db8::/32",
"2002::/16",
"fc00::/7", "fc00::/7",
"fe80::/10", "fe80::/10",
"ff00::/8", "ff00::/8",
+42
View File
@@ -1,6 +1,7 @@
package utils package utils
import ( import (
"errors"
"net" "net"
"net/http" "net/http"
"net/url" "net/url"
@@ -8,6 +9,47 @@ import (
"time" "time"
) )
func TestHTTPURLTargetIPAllowed(t *testing.T) {
tests := []struct {
name string
address string
allowed bool
}{
{name: "public IPv4", address: "1.1.1.1", allowed: true},
{name: "public IPv6", address: "2606:4700:4700::1111", allowed: true},
{name: "well-known NAT64", address: "64:ff9b::a9fe:a9fe", allowed: false},
{name: "local-use NAT64", address: "64:ff9b:1::a9fe:a9fe", allowed: false},
{name: "6to4 public IPv4 embedding", address: "2002:0101:0101::1", allowed: false},
{name: "6to4 link-local IPv4 embedding", address: "2002:a9fe:a9fe::1", allowed: false},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
ip := net.ParseIP(test.address)
if ip == nil {
t.Fatalf("ParseIP(%q) returned nil", test.address)
}
if got := HTTPURLTargetIPAllowed(ip); got != test.allowed {
t.Fatalf("HTTPURLTargetIPAllowed(%q) = %t, want %t", test.address, got, test.allowed)
}
})
}
}
func TestResolveAllowedHTTPURLRejectsSpecialIPv6Literals(t *testing.T) {
for _, rawURL := range []string{
"http://[64:ff9b:1::a9fe:a9fe]/metadata",
"http://[2002:a9fe:a9fe::1]/metadata",
} {
t.Run(rawURL, func(t *testing.T) {
_, _, err := ResolveAllowedHTTPURL(rawURL)
if !errors.Is(err, ErrHTTPURLTargetNotAllowed) {
t.Fatalf("ResolveAllowedHTTPURL(%q) error = %v, want %v", rawURL, err, ErrHTTPURLTargetNotAllowed)
}
})
}
}
func TestBuildRestrictedHTTPClientPreservesHostnameAsTLSServerName(t *testing.T) { func TestBuildRestrictedHTTPClientPreservesHostnameAsTLSServerName(t *testing.T) {
// Construct a hostname URL paired with an arbitrary public IP so we exercise // Construct a hostname URL paired with an arbitrary public IP so we exercise
// the SNI preservation path without depending on live DNS in unit tests. // the SNI preservation path without depending on live DNS in unit tests.