mirror of
https://github.com/gotenberg/gotenberg.git
synced 2026-10-08 05:23:18 +01:00
fix(outbound): ignore IPv6 zone identifiers when classifying addresses
This commit is contained in:
@@ -109,11 +109,18 @@ var nonPublicIPv4Prefixes = []netip.Prefix{
|
|||||||
// the prefixes themselves are deprecated or translation-only. See
|
// the prefixes themselves are deprecated or translation-only. See
|
||||||
// [nonPublicIPv6Prefixes] and [nonPublicIPv4Prefixes] for the full lists
|
// [nonPublicIPv6Prefixes] and [nonPublicIPv4Prefixes] for the full lists
|
||||||
// and rationale.
|
// and rationale.
|
||||||
|
//
|
||||||
|
// An IPv6 zone identifier is ignored, so [::%1] classifies the same as [::].
|
||||||
func IsPublicIP(addr netip.Addr) bool {
|
func IsPublicIP(addr netip.Addr) bool {
|
||||||
if !addr.IsValid() {
|
if !addr.IsValid() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
addr = addr.Unmap()
|
// A zone does not change where a non-link-local address routes, but
|
||||||
|
// [netip.Prefix.Contains] never matches a zoned address and
|
||||||
|
// [netip.Addr.IsUnspecified] compares the zone too. Keeping it would let
|
||||||
|
// http://[::%251]/ or any zoned address in the prefixes below pass as
|
||||||
|
// public.
|
||||||
|
addr = addr.WithZone("").Unmap()
|
||||||
switch {
|
switch {
|
||||||
case addr.IsLoopback(),
|
case addr.IsLoopback(),
|
||||||
addr.IsPrivate(),
|
addr.IsPrivate(),
|
||||||
|
|||||||
@@ -103,6 +103,23 @@ func TestIsPublicIP(t *testing.T) {
|
|||||||
|
|
||||||
// Discard prefix (RFC 6666).
|
// Discard prefix (RFC 6666).
|
||||||
{"100::1", false},
|
{"100::1", false},
|
||||||
|
|
||||||
|
// A zone identifier must not change the classification.
|
||||||
|
{"::%1", false},
|
||||||
|
{"::%lo", false},
|
||||||
|
{"::1%1", false},
|
||||||
|
{"fe80::1%eth0", false},
|
||||||
|
{"fc00::1%1", false},
|
||||||
|
{"::ffff:127.0.0.1%1", false},
|
||||||
|
{"fec0::1%eth0", false},
|
||||||
|
{"2002:a9fe:a9fe::%1", false},
|
||||||
|
{"2001:0:abcd:ef12:3456:7890:a9fe:a9fe%1", false},
|
||||||
|
{"64:ff9b::a9fe:a9fe%1", false},
|
||||||
|
{"64:ff9b:1::a9fe:a9fe%1", false},
|
||||||
|
{"::a9fe:a9fe%1", false},
|
||||||
|
{"2001:db8::1%1", false},
|
||||||
|
{"100::1%1", false},
|
||||||
|
{"2606:4700:4700::1111%1", true},
|
||||||
} {
|
} {
|
||||||
t.Run(tc.addr, func(t *testing.T) {
|
t.Run(tc.addr, func(t *testing.T) {
|
||||||
addr, err := netip.ParseAddr(tc.addr)
|
addr, err := netip.ParseAddr(tc.addr)
|
||||||
@@ -304,6 +321,22 @@ func TestFilterOutboundURL(t *testing.T) {
|
|||||||
expectErr: true,
|
expectErr: true,
|
||||||
expectIs: ErrFiltered,
|
expectIs: ErrFiltered,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
scenario: "zoned unspecified address blocked with deny-private-ips",
|
||||||
|
rawURL: "http://[::%251]:9999/",
|
||||||
|
deny: defaultDeny,
|
||||||
|
opts: []DecideOption{WithDenyPrivateIPs(true)},
|
||||||
|
expectErr: true,
|
||||||
|
expectIs: ErrFiltered,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scenario: "zoned site-local address blocked with deny-private-ips",
|
||||||
|
rawURL: "http://[fec0:1234::3%25eth0]:8080/",
|
||||||
|
deny: defaultDeny,
|
||||||
|
opts: []DecideOption{WithDenyPrivateIPs(true)},
|
||||||
|
expectErr: true,
|
||||||
|
expectIs: ErrFiltered,
|
||||||
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(tc.scenario, func(t *testing.T) {
|
t.Run(tc.scenario, func(t *testing.T) {
|
||||||
if tc.stub != nil {
|
if tc.stub != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user