fix: block link-local addresses in SSRF protection

This commit is contained in:
Elias Schneider
2026-07-13 11:05:24 +02:00
parent a3f27ec2ec
commit 9714296efb
2 changed files with 55 additions and 7 deletions

View File

@@ -4,6 +4,7 @@ import (
"context"
"errors"
"net"
"net/netip"
"net/url"
"strings"
@@ -56,7 +57,17 @@ func IsTailscaleIP(ip net.IP) bool {
}
func IsPrivateIP(ip net.IP) bool {
return IsLocalhostIP(ip) || IsPrivateLanIP(ip) || IsTailscaleIP(ip) || IsLocalIPv6(ip)
if IsLocalhostIP(ip) || IsPrivateLanIP(ip) || IsTailscaleIP(ip) || IsLocalIPv6(ip) {
return true
}
addr, ok := netip.AddrFromSlice(ip)
if !ok {
return false
}
addr = addr.Unmap()
return addr.IsLoopback() || addr.IsPrivate() || addr.IsLinkLocalUnicast() || addr.IsLinkLocalMulticast() || addr.IsUnspecified()
}
func IsURLPrivate(ctx context.Context, u *url.URL) (bool, error) {

View File

@@ -110,12 +110,19 @@ func TestIsPrivateIP(t *testing.T) {
ip string
expected bool
}{
{"127.0.0.1", true}, // localhost
{"192.168.1.1", true}, // private LAN
{"100.64.0.1", true}, // Tailscale
{"fd00::1", true}, // local IPv6
{"8.8.8.8", false}, // public IPv4
{"2001:4860:4860::8888", false}, // public IPv6
{"127.0.0.1", true}, // localhost
{"192.168.1.1", true}, // private LAN
{"100.64.0.1", true}, // Tailscale
{"169.254.169.254", true}, // IPv4 link-local
{"169.254.170.2", true}, // IPv4 link-local
{"::ffff:169.254.169.254", true}, // IPv4-mapped link-local
{"fe80::1", true}, // IPv6 link-local
{"ff02::1", true}, // IPv6 link-local multicast
{"0.0.0.0", true}, // IPv4 unspecified
{"::", true}, // IPv6 unspecified
{"fd00::1", true}, // private IPv6
{"8.8.8.8", false}, // public IPv4
{"2001:4860:4860::8888", false}, // public IPv6
}
for _, tt := range tests {
@@ -231,6 +238,36 @@ func TestIsURLPrivate(t *testing.T) {
expectPriv: true,
expectError: false,
},
{
name: "IPv4 link-local metadata IP",
urlStr: "http://169.254.169.254",
expectPriv: true,
expectError: false,
},
{
name: "IPv4-mapped link-local metadata IP",
urlStr: "http://[::ffff:169.254.169.254]",
expectPriv: true,
expectError: false,
},
{
name: "IPv6 link-local IP",
urlStr: "http://[fe80::1]",
expectPriv: true,
expectError: false,
},
{
name: "IPv4 unspecified IP",
urlStr: "http://0.0.0.0",
expectPriv: true,
expectError: false,
},
{
name: "IPv6 unspecified IP",
urlStr: "http://[::]",
expectPriv: true,
expectError: false,
},
{
name: "public IP - Google DNS",
urlStr: "http://8.8.8.8",