From 035bfdb4fbf495f85cae139b0f1f9b15be7a4985 Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Tue, 24 May 2022 12:48:00 +0200 Subject: [PATCH] Use CIDRs to decide allowed IPs for testing (#826) This fixes validation of IPv6 addresses, their string representation may vary depending on leading zeroes or abbreviated addresses. It also extends the number of IP addresses that can be used for testing, specially IPv6 addresses. --- internal/fields/_static/allowed_geo_ips.txt | 24 +++++------- internal/fields/validate.go | 37 +++++++++--------- internal/fields/validate_test.go | 43 ++++++++++++++++++++- 3 files changed, 69 insertions(+), 35 deletions(-) diff --git a/internal/fields/_static/allowed_geo_ips.txt b/internal/fields/_static/allowed_geo_ips.txt index 500b8b6bb..bbeec058c 100644 --- a/internal/fields/_static/allowed_geo_ips.txt +++ b/internal/fields/_static/allowed_geo_ips.txt @@ -1,15 +1,9 @@ -1.128.3.4 -175.16.199.1 -216.160.83.57 -216.160.83.61 -81.2.69.143 -81.2.69.144 -81.2.69.145 -81.2.69.193 -89.160.20.112 -89.160.20.156 -67.43.156.12 -67.43.156.13 -67.43.156.14 -67.43.156.15 -2a02:cf40:add:4002:91f2:a9b2:e09a:6fc6 \ No newline at end of file +1.128.0.0/11 +175.16.199.0/24 +216.160.83.56/29 +81.2.69.142/31 +81.2.69.192/28 +89.160.20.112/28 +89.160.20.128/25 +67.43.156.0/24 +2a02:cf40::/29 diff --git a/internal/fields/validate.go b/internal/fields/validate.go index d41751d36..b416fb308 100644 --- a/internal/fields/validate.go +++ b/internal/fields/validate.go @@ -5,6 +5,7 @@ package fields import ( + "bufio" _ "embed" "encoding/json" "fmt" @@ -35,7 +36,7 @@ type Validator struct { disabledDependencyManagement bool enabledAllowedIPCheck bool - allowedIPs map[string]struct{} + allowedCIDRs []*net.IPNet } // ValidatorOption represents an optional flag that can be passed to CreateValidatorForDataStream. @@ -86,7 +87,7 @@ func CreateValidatorForDataStream(dataStreamRootPath string, opts ...ValidatorOp } } - v.allowedIPs = initializeAllowedIPsList() + v.allowedCIDRs = initializeAllowedCIDRsList() v.Schema, err = loadFieldsForDataStream(dataStreamRootPath) if err != nil { @@ -118,20 +119,17 @@ func CreateValidatorForDataStream(dataStreamRootPath string, opts ...ValidatorOp //go:embed _static/allowed_geo_ips.txt var allowedGeoIPs string -func initializeAllowedIPsList() map[string]struct{} { - m := map[string]struct{}{ - "0.0.0.0": {}, "255.255.255.255": {}, - "0:0:0:0:0:0:0:0": {}, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff": {}, "::": {}, - } - for _, ip := range strings.Split(allowedGeoIPs, "\n") { - ip = strings.Trim(ip, " \n\t") - if ip == "" { - continue +func initializeAllowedCIDRsList() (cidrs []*net.IPNet) { + s := bufio.NewScanner(strings.NewReader(allowedGeoIPs)) + for s.Scan() { + _, cidr, err := net.ParseCIDR(s.Text()) + if err != nil { + panic("invalid ip in _static/allowed_geo_ips.txt: " + s.Text()) } - m[ip] = struct{}{} + cidrs = append(cidrs, cidr) } - return m + return cidrs } func loadFieldsForDataStream(dataStreamRootPath string) ([]FieldDefinition, error) { @@ -442,17 +440,18 @@ func (v *Validator) parseElementValue(key string, definition FieldDefinition, va // - 0.0.0.0 and 255.255.255.255 for IPv4 // - 0:0:0:0:0:0:0:0 and ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff for IPv6 func (v *Validator) isAllowedIPValue(s string) bool { - if _, found := v.allowedIPs[s]; found { - return true - } - ip := net.ParseIP(s) if ip == nil { return false } - if ip.IsPrivate() || ip.IsLoopback() || - ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() { + for _, allowedCIDR := range v.allowedCIDRs { + if allowedCIDR.Contains(ip) { + return true + } + } + + if ip.IsUnspecified() || ip.IsPrivate() || ip.IsLoopback() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() { return true } diff --git a/internal/fields/validate_test.go b/internal/fields/validate_test.go index 05fc3b298..5bdf382e8 100644 --- a/internal/fields/validate_test.go +++ b/internal/fields/validate_test.go @@ -194,6 +194,42 @@ func Test_parseElementValue(t *testing.T) { }, fail: true, }, + { + key: "ip in allowed list", + value: "1.128.3.4", + definition: FieldDefinition{ + Type: "ip", + }, + }, + { + key: "ipv6 in allowed list", + value: "2a02:cf40:add:4002:91f2:a9b2:e09a:6fc6", + definition: FieldDefinition{ + Type: "ip", + }, + }, + { + key: "unspecified ipv6", + value: "::", + definition: FieldDefinition{ + Type: "ip", + }, + }, + { + key: "abbreviated ipv6 in allowed list with leading 0", + value: "2a02:cf40:0add:0::1", + definition: FieldDefinition{ + Type: "ip", + }, + }, + { + key: "ip not in geoip database", + value: "8.8.8.8", + definition: FieldDefinition{ + Type: "ip", + }, + fail: true, + }, // text { key: "text", @@ -266,7 +302,12 @@ func Test_parseElementValue(t *testing.T) { }, }, } { - v := Validator{disabledDependencyManagement: true} + v := Validator{ + disabledDependencyManagement: true, + enabledAllowedIPCheck: true, + allowedCIDRs: initializeAllowedCIDRsList(), + } + t.Run(test.key, func(t *testing.T) { err := v.parseElementValue(test.key, test.definition, test.value) if test.fail {