Skip to content

Commit 8cfcdec

Browse files
fix(source): block reserved stream IP ranges
1 parent c0378ec commit 8cfcdec

2 files changed

Lines changed: 36 additions & 3 deletions

File tree

internal/source/source.go

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"fmt"
1616
"io"
1717
"net"
18+
"net/netip"
1819
"net/url"
1920
"os"
2021
"os/exec"
@@ -61,6 +62,19 @@ var youtubeHosts = map[string]bool{
6162
"youtu.be": true,
6263
}
6364

65+
var blockedIPPrefixes = []netip.Prefix{
66+
netip.MustParsePrefix("100.64.0.0/10"), // carrier-grade NAT / shared address space
67+
netip.MustParsePrefix("192.0.0.0/24"), // IETF protocol assignments
68+
netip.MustParsePrefix("192.0.2.0/24"), // TEST-NET-1
69+
netip.MustParsePrefix("198.18.0.0/15"), // benchmarking
70+
netip.MustParsePrefix("198.51.100.0/24"), // TEST-NET-2
71+
netip.MustParsePrefix("203.0.113.0/24"), // TEST-NET-3
72+
netip.MustParsePrefix("2001:db8::/32"), // documentation
73+
netip.MustParsePrefix("2002::/16"), // 6to4
74+
netip.MustParsePrefix("64:ff9b::/96"), // IPv4/IPv6 translation
75+
netip.MustParsePrefix("64:ff9b:1::/48"), // locally scoped IPv4/IPv6 translation
76+
}
77+
6478
// Classify decides how a raw URL is handled, and rejects anything that is not
6579
// http(s). It does not touch the network.
6680
func Classify(raw string) (Kind, error) {
@@ -87,16 +101,28 @@ func Classify(raw string) (Kind, error) {
87101

88102
// isBlockedIP reports whether an address must not be dialed: loopback,
89103
// link-local (incl. the 169.254.169.254 cloud-metadata endpoint), private
90-
// (RFC1918 / fc00::/7), multicast, or unspecified.
104+
// (RFC1918 / fc00::/7), shared/reserved, multicast, or unspecified.
91105
func isBlockedIP(ip net.IP) bool {
92-
return ip == nil ||
106+
if ip == nil ||
93107
ip.IsLoopback() ||
94108
ip.IsLinkLocalUnicast() ||
95109
ip.IsLinkLocalMulticast() ||
96110
ip.IsInterfaceLocalMulticast() ||
97111
ip.IsMulticast() ||
98112
ip.IsUnspecified() ||
99-
ip.IsPrivate()
113+
ip.IsPrivate() {
114+
return true
115+
}
116+
addr, ok := netip.AddrFromSlice(ip)
117+
if !ok {
118+
return true
119+
}
120+
for _, p := range blockedIPPrefixes {
121+
if p.Contains(addr.Unmap()) {
122+
return true
123+
}
124+
}
125+
return false
100126
}
101127

102128
// guardURL validates scheme and resolves the host, rejecting any URL that

internal/source/source_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,15 @@ func TestIsBlockedIP(t *testing.T) {
4747
"172.16.3.4", // private
4848
"192.168.1.1", // private
4949
"169.254.169.254", // link-local / cloud metadata
50+
"100.100.100.200", // shared space, commonly used by cloud metadata services
51+
"198.18.0.1", // benchmarking range
52+
"192.0.2.10", // TEST-NET-1
53+
"198.51.100.10", // TEST-NET-2
54+
"203.0.113.10", // TEST-NET-3
5055
"fe80::1", // link-local v6
5156
"fc00::1", // unique-local v6 (private)
57+
"2001:db8::1", // documentation v6
58+
"2002::1", // 6to4
5259
"0.0.0.0", // unspecified
5360
"224.0.0.1", // multicast
5461
}

0 commit comments

Comments
 (0)