Skip to content

Commit

Permalink
Use CIDRs to decide allowed IPs for testing (#826)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
jsoriano authored May 24, 2022
1 parent dcfabeb commit 035bfdb
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 35 deletions.
24 changes: 9 additions & 15 deletions internal/fields/_static/allowed_geo_ips.txt
Original file line number Diff line number Diff line change
@@ -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
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
37 changes: 18 additions & 19 deletions internal/fields/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package fields

import (
"bufio"
_ "embed"
"encoding/json"
"fmt"
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -86,7 +87,7 @@ func CreateValidatorForDataStream(dataStreamRootPath string, opts ...ValidatorOp
}
}

v.allowedIPs = initializeAllowedIPsList()
v.allowedCIDRs = initializeAllowedCIDRsList()

v.Schema, err = loadFieldsForDataStream(dataStreamRootPath)
if err != nil {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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
}

Expand Down
43 changes: 42 additions & 1 deletion internal/fields/validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit 035bfdb

Please sign in to comment.