mirror of
https://github.com/axllent/mailpit.git
synced 2026-06-28 15:06:07 +00:00
Security: Include CGNAT (Carrier-Grade NAT) in internal IP checks (GHSA-j3fj-qppj-fmmc)
CGNAT (Carrier-Grade NAT) is a technique used by ISPs to conserve IPv4 addresses. Instead of assigning a unique public IP to every customer, the ISP places many customers behind a shared NAT, then gives them all addresses from the reserved 100.64.0.0/10 range (RFC 6598) on their internal network. This means traffic from multiple customers exits through a small pool of public IPs - a second layer of NAT on top of whatever NAT the customer's own router does (hence "double NAT").
This commit is contained in:
@@ -5,6 +5,15 @@ import (
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// cgnatRange is the CGNAT shared address space (RFC 6598), not covered by net.IP.IsPrivate().
|
||||
// CGNAT (Carrier-Grade NAT) is a technique used by ISPs to conserve IPv4 addresses. Instead of assigning a unique
|
||||
// public IP to every customer, the ISP places many customers behind a shared NAT, then gives them all addresses
|
||||
// from the reserved 100.64.0.0/10 range (RFC 6598) on their internal network.
|
||||
var cgnatRange = func() *net.IPNet {
|
||||
_, cidr, _ := net.ParseCIDR("100.64.0.0/10")
|
||||
return cidr
|
||||
}()
|
||||
|
||||
// IsInternalIP checks if the given IP address is an internal IP address (e.g., loopback, private, link-local, or multicast).
|
||||
// IsLoopback — 127.0.0.0/8, ::1
|
||||
// IsPrivate — 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, fc00::/7
|
||||
@@ -12,13 +21,15 @@ import (
|
||||
// IsLinkLocalMulticast — 224.0.0.0/24, ff02::/16
|
||||
// IsUnspecified — 0.0.0.0, ::
|
||||
// IsMulticast — 224.0.0.0/4, ff00::/8
|
||||
// CGNAT — 100.64.0.0/10 (RFC 6598) (Carrier-Grade NAT)
|
||||
func IsInternalIP(ip net.IP) bool {
|
||||
return ip.IsLoopback() ||
|
||||
ip.IsPrivate() ||
|
||||
ip.IsLinkLocalUnicast() ||
|
||||
ip.IsLinkLocalMulticast() ||
|
||||
ip.IsUnspecified() ||
|
||||
ip.IsMulticast()
|
||||
ip.IsMulticast() ||
|
||||
cgnatRange.Contains(ip)
|
||||
}
|
||||
|
||||
// IsValidLinkURL checks if the provided string is a valid URL with http or https scheme and a non-empty hostname.
|
||||
|
||||
@@ -1,10 +1,71 @@
|
||||
package tools
|
||||
|
||||
import (
|
||||
"net"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIsInternalIP(t *testing.T) {
|
||||
internal := []string{
|
||||
"127.0.0.1", // loopback
|
||||
"::1", // IPv6 loopback
|
||||
"10.0.0.1", // private
|
||||
"172.16.0.1", // private
|
||||
"192.168.1.1", // private
|
||||
"169.254.1.1", // link-local unicast
|
||||
"fe80::1", // IPv6 link-local
|
||||
"0.0.0.0", // unspecified
|
||||
"224.0.0.1", // multicast
|
||||
"100.64.0.1", // CGNAT start
|
||||
"100.127.255.255", // CGNAT end
|
||||
}
|
||||
external := []string{
|
||||
"8.8.8.8",
|
||||
"1.1.1.1",
|
||||
"100.128.0.1", // just outside CGNAT range
|
||||
}
|
||||
|
||||
for _, s := range internal {
|
||||
ip := net.ParseIP(s)
|
||||
if !IsInternalIP(ip) {
|
||||
t.Errorf("expected %s to be internal", s)
|
||||
}
|
||||
}
|
||||
for _, s := range external {
|
||||
ip := net.ParseIP(s)
|
||||
if IsInternalIP(ip) {
|
||||
t.Errorf("expected %s to be external", s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsValidLinkURL(t *testing.T) {
|
||||
valid := []string{
|
||||
"http://example.com",
|
||||
"https://example.com",
|
||||
"https://example.com/path?q=1#anchor",
|
||||
}
|
||||
invalid := []string{
|
||||
"",
|
||||
"ftp://example.com",
|
||||
"example.com",
|
||||
"//example.com",
|
||||
"https://",
|
||||
}
|
||||
|
||||
for _, s := range valid {
|
||||
if !IsValidLinkURL(s) {
|
||||
t.Errorf("expected %q to be a valid link URL", s)
|
||||
}
|
||||
}
|
||||
for _, s := range invalid {
|
||||
if IsValidLinkURL(s) {
|
||||
t.Errorf("expected %q to be an invalid link URL", s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestArgsParser(t *testing.T) {
|
||||
tests := map[string][]string{}
|
||||
tests["this is a test"] = []string{"this", "is", "a", "test"}
|
||||
|
||||
Reference in New Issue
Block a user