mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-10-31 02:08:13 +00:00 
			
		
		
		
	Split out IP validation functions into their own file
(No code changes.)
This commit is contained in:
		
							
								
								
									
										61
									
								
								staging/src/k8s.io/apimachinery/pkg/util/validation/ip.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								staging/src/k8s.io/apimachinery/pkg/util/validation/ip.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | |||||||
|  | /* | ||||||
|  | Copyright 2023 The Kubernetes Authors. | ||||||
|  |  | ||||||
|  | Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  | you may not use this file except in compliance with the License. | ||||||
|  | You may obtain a copy of the License at | ||||||
|  |  | ||||||
|  |     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  | ||||||
|  | Unless required by applicable law or agreed to in writing, software | ||||||
|  | distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  | See the License for the specific language governing permissions and | ||||||
|  | limitations under the License. | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | package validation | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"k8s.io/apimachinery/pkg/util/validation/field" | ||||||
|  | 	netutils "k8s.io/utils/net" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // IsValidIP tests that the argument is a valid IP address. | ||||||
|  | func IsValidIP(fldPath *field.Path, value string) field.ErrorList { | ||||||
|  | 	var allErrors field.ErrorList | ||||||
|  | 	if netutils.ParseIPSloppy(value) == nil { | ||||||
|  | 		allErrors = append(allErrors, field.Invalid(fldPath, value, "must be a valid IP address, (e.g. 10.9.8.7 or 2001:db8::ffff)").WithOrigin("format=ip-sloppy")) | ||||||
|  | 	} | ||||||
|  | 	return allErrors | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // IsValidIPv4Address tests that the argument is a valid IPv4 address. | ||||||
|  | func IsValidIPv4Address(fldPath *field.Path, value string) field.ErrorList { | ||||||
|  | 	var allErrors field.ErrorList | ||||||
|  | 	ip := netutils.ParseIPSloppy(value) | ||||||
|  | 	if ip == nil || ip.To4() == nil { | ||||||
|  | 		allErrors = append(allErrors, field.Invalid(fldPath, value, "must be a valid IPv4 address")) | ||||||
|  | 	} | ||||||
|  | 	return allErrors | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // IsValidIPv6Address tests that the argument is a valid IPv6 address. | ||||||
|  | func IsValidIPv6Address(fldPath *field.Path, value string) field.ErrorList { | ||||||
|  | 	var allErrors field.ErrorList | ||||||
|  | 	ip := netutils.ParseIPSloppy(value) | ||||||
|  | 	if ip == nil || ip.To4() != nil { | ||||||
|  | 		allErrors = append(allErrors, field.Invalid(fldPath, value, "must be a valid IPv6 address")) | ||||||
|  | 	} | ||||||
|  | 	return allErrors | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // IsValidCIDR tests that the argument is a valid CIDR value. | ||||||
|  | func IsValidCIDR(fldPath *field.Path, value string) field.ErrorList { | ||||||
|  | 	var allErrors field.ErrorList | ||||||
|  | 	_, _, err := netutils.ParseCIDRSloppy(value) | ||||||
|  | 	if err != nil { | ||||||
|  | 		allErrors = append(allErrors, field.Invalid(fldPath, value, "must be a valid CIDR value, (e.g. 10.9.8.0/24 or 2001:db8::/64)")) | ||||||
|  | 	} | ||||||
|  | 	return allErrors | ||||||
|  | } | ||||||
							
								
								
									
										320
									
								
								staging/src/k8s.io/apimachinery/pkg/util/validation/ip_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										320
									
								
								staging/src/k8s.io/apimachinery/pkg/util/validation/ip_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,320 @@ | |||||||
|  | /* | ||||||
|  | Copyright 2023 The Kubernetes Authors. | ||||||
|  |  | ||||||
|  | Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  | you may not use this file except in compliance with the License. | ||||||
|  | You may obtain a copy of the License at | ||||||
|  |  | ||||||
|  |     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  | ||||||
|  | Unless required by applicable law or agreed to in writing, software | ||||||
|  | distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  | See the License for the specific language governing permissions and | ||||||
|  | limitations under the License. | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | package validation | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"strings" | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"k8s.io/apimachinery/pkg/util/validation/field" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestIsValidIP(t *testing.T) { | ||||||
|  | 	for _, tc := range []struct { | ||||||
|  | 		name   string | ||||||
|  | 		in     string | ||||||
|  | 		family int | ||||||
|  | 		err    string | ||||||
|  | 	}{ | ||||||
|  | 		// GOOD VALUES | ||||||
|  | 		{ | ||||||
|  | 			name:   "ipv4", | ||||||
|  | 			in:     "1.2.3.4", | ||||||
|  | 			family: 4, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name:   "ipv4, all zeros", | ||||||
|  | 			in:     "0.0.0.0", | ||||||
|  | 			family: 4, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name:   "ipv4, max", | ||||||
|  | 			in:     "255.255.255.255", | ||||||
|  | 			family: 4, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name:   "ipv6", | ||||||
|  | 			in:     "1234::abcd", | ||||||
|  | 			family: 6, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name:   "ipv6, all zeros, collapsed", | ||||||
|  | 			in:     "::", | ||||||
|  | 			family: 6, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name:   "ipv6, max", | ||||||
|  | 			in:     "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", | ||||||
|  | 			family: 6, | ||||||
|  | 		}, | ||||||
|  |  | ||||||
|  | 		// GOOD, THOUGH NON-CANONICAL, VALUES | ||||||
|  | 		{ | ||||||
|  | 			name:   "ipv6, all zeros, expanded (non-canonical)", | ||||||
|  | 			in:     "0:0:0:0:0:0:0:0", | ||||||
|  | 			family: 6, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name:   "ipv6, leading 0s (non-canonical)", | ||||||
|  | 			in:     "0001:002:03:4::", | ||||||
|  | 			family: 6, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name:   "ipv6, capital letters (non-canonical)", | ||||||
|  | 			in:     "1234::ABCD", | ||||||
|  | 			family: 6, | ||||||
|  | 		}, | ||||||
|  |  | ||||||
|  | 		// BAD VALUES WE CURRENTLY CONSIDER GOOD | ||||||
|  | 		{ | ||||||
|  | 			name:   "ipv4 with leading 0s", | ||||||
|  | 			in:     "1.1.1.01", | ||||||
|  | 			family: 4, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name:   "ipv4-in-ipv6 value", | ||||||
|  | 			in:     "::ffff:1.1.1.1", | ||||||
|  | 			family: 4, | ||||||
|  | 		}, | ||||||
|  |  | ||||||
|  | 		// BAD VALUES | ||||||
|  | 		{ | ||||||
|  | 			name: "empty string", | ||||||
|  | 			in:   "", | ||||||
|  | 			err:  "must be a valid IP address", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "junk", | ||||||
|  | 			in:   "aaaaaaa", | ||||||
|  | 			err:  "must be a valid IP address", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "domain name", | ||||||
|  | 			in:   "myhost.mydomain", | ||||||
|  | 			err:  "must be a valid IP address", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "cidr", | ||||||
|  | 			in:   "1.2.3.0/24", | ||||||
|  | 			err:  "must be a valid IP address", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "ipv4 with out-of-range octets", | ||||||
|  | 			in:   "1.2.3.400", | ||||||
|  | 			err:  "must be a valid IP address", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "ipv4 with negative octets", | ||||||
|  | 			in:   "-1.0.0.0", | ||||||
|  | 			err:  "must be a valid IP address", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "ipv6 with out-of-range segment", | ||||||
|  | 			in:   "2001:db8::10005", | ||||||
|  | 			err:  "must be a valid IP address", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "ipv4:port", | ||||||
|  | 			in:   "1.2.3.4:80", | ||||||
|  | 			err:  "must be a valid IP address", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "ipv6 with brackets", | ||||||
|  | 			in:   "[2001:db8::1]", | ||||||
|  | 			err:  "must be a valid IP address", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "[ipv6]:port", | ||||||
|  | 			in:   "[2001:db8::1]:80", | ||||||
|  | 			err:  "must be a valid IP address", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "host:port", | ||||||
|  | 			in:   "example.com:80", | ||||||
|  | 			err:  "must be a valid IP address", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "ipv6 with zone", | ||||||
|  | 			in:   "1234::abcd%eth0", | ||||||
|  | 			err:  "must be a valid IP address", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "ipv4 with zone", | ||||||
|  | 			in:   "169.254.0.0%eth0", | ||||||
|  | 			err:  "must be a valid IP address", | ||||||
|  | 		}, | ||||||
|  | 	} { | ||||||
|  | 		t.Run(tc.name, func(t *testing.T) { | ||||||
|  | 			errs := IsValidIP(field.NewPath(""), tc.in) | ||||||
|  | 			if tc.err == "" { | ||||||
|  | 				if len(errs) != 0 { | ||||||
|  | 					t.Errorf("expected %q to be valid but got: %v", tc.in, errs) | ||||||
|  | 				} | ||||||
|  | 			} else { | ||||||
|  | 				if len(errs) != 1 { | ||||||
|  | 					t.Errorf("expected %q to have 1 error but got: %v", tc.in, errs) | ||||||
|  | 				} else if !strings.Contains(errs[0].Detail, tc.err) { | ||||||
|  | 					t.Errorf("expected error for %q to contain %q but got: %q", tc.in, tc.err, errs[0].Detail) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			errs = IsValidIPv4Address(field.NewPath(""), tc.in) | ||||||
|  | 			if tc.family == 4 { | ||||||
|  | 				if len(errs) != 0 { | ||||||
|  | 					t.Errorf("expected %q to pass IsValidIPv4Address but got: %v", tc.in, errs) | ||||||
|  | 				} | ||||||
|  | 			} else { | ||||||
|  | 				if len(errs) == 0 { | ||||||
|  | 					t.Errorf("expected %q to fail IsValidIPv4Address", tc.in) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			errs = IsValidIPv6Address(field.NewPath(""), tc.in) | ||||||
|  | 			if tc.family == 6 { | ||||||
|  | 				if len(errs) != 0 { | ||||||
|  | 					t.Errorf("expected %q to pass IsValidIPv6Address but got: %v", tc.in, errs) | ||||||
|  | 				} | ||||||
|  | 			} else { | ||||||
|  | 				if len(errs) == 0 { | ||||||
|  | 					t.Errorf("expected %q to fail IsValidIPv6Address", tc.in) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestIsValidCIDR(t *testing.T) { | ||||||
|  | 	for _, tc := range []struct { | ||||||
|  | 		name string | ||||||
|  | 		in   string | ||||||
|  | 		err  string | ||||||
|  | 	}{ | ||||||
|  | 		// GOOD VALUES | ||||||
|  | 		{ | ||||||
|  | 			name: "ipv4", | ||||||
|  | 			in:   "1.0.0.0/8", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "ipv4, all IPs", | ||||||
|  | 			in:   "0.0.0.0/0", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "ipv4, single IP", | ||||||
|  | 			in:   "1.1.1.1/32", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "ipv6", | ||||||
|  | 			in:   "2001:4860:4860::/48", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "ipv6, all IPs", | ||||||
|  | 			in:   "::/0", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "ipv6, single IP", | ||||||
|  | 			in:   "::1/128", | ||||||
|  | 		}, | ||||||
|  |  | ||||||
|  | 		// GOOD, THOUGH NON-CANONICAL, VALUES | ||||||
|  | 		{ | ||||||
|  | 			name: "ipv6, extra 0s (non-canonical)", | ||||||
|  | 			in:   "2a00:79e0:2:0::/64", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "ipv6, capital letters (non-canonical)", | ||||||
|  | 			in:   "2001:DB8::/64", | ||||||
|  | 		}, | ||||||
|  |  | ||||||
|  | 		// BAD VALUES WE CURRENTLY CONSIDER GOOD | ||||||
|  | 		{ | ||||||
|  | 			name: "ipv4 with leading 0s", | ||||||
|  | 			in:   "1.1.01.0/24", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "ipv4-in-ipv6 with ipv4-sized prefix", | ||||||
|  | 			in:   "::ffff:1.1.1.0/24", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "ipv4-in-ipv6 with ipv6-sized prefix", | ||||||
|  | 			in:   "::ffff:1.1.1.0/120", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "ipv4 with bits past prefix", | ||||||
|  | 			in:   "1.2.3.4/24", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "ipv6 with bits past prefix", | ||||||
|  | 			in:   "2001:db8::1/64", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "prefix length with leading 0s", | ||||||
|  | 			in:   "192.168.0.0/016", | ||||||
|  | 		}, | ||||||
|  |  | ||||||
|  | 		// BAD VALUES | ||||||
|  | 		{ | ||||||
|  | 			name: "empty string", | ||||||
|  | 			in:   "", | ||||||
|  | 			err:  "must be a valid CIDR value", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "junk", | ||||||
|  | 			in:   "aaaaaaa", | ||||||
|  | 			err:  "must be a valid CIDR value", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "IP address", | ||||||
|  | 			in:   "1.2.3.4", | ||||||
|  | 			err:  "must be a valid CIDR value", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "partial URL", | ||||||
|  | 			in:   "192.168.0.1/healthz", | ||||||
|  | 			err:  "must be a valid CIDR value", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "partial URL 2", | ||||||
|  | 			in:   "192.168.0.1/0/99", | ||||||
|  | 			err:  "must be a valid CIDR value", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "negative prefix length", | ||||||
|  | 			in:   "192.168.0.0/-16", | ||||||
|  | 			err:  "must be a valid CIDR value", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name: "prefix length with sign", | ||||||
|  | 			in:   "192.168.0.0/+16", | ||||||
|  | 			err:  "must be a valid CIDR value", | ||||||
|  | 		}, | ||||||
|  | 	} { | ||||||
|  | 		t.Run(tc.name, func(t *testing.T) { | ||||||
|  | 			errs := IsValidCIDR(field.NewPath(""), tc.in) | ||||||
|  | 			if tc.err == "" { | ||||||
|  | 				if len(errs) != 0 { | ||||||
|  | 					t.Errorf("expected %q to be valid but got: %v", tc.in, errs) | ||||||
|  | 				} | ||||||
|  | 			} else { | ||||||
|  | 				if len(errs) != 1 { | ||||||
|  | 					t.Errorf("expected %q to have 1 error but got: %v", tc.in, errs) | ||||||
|  | 				} else if !strings.Contains(errs[0].Detail, tc.err) { | ||||||
|  | 					t.Errorf("expected error for %q to contain %q but got: %q", tc.in, tc.err, errs[0].Detail) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -24,7 +24,6 @@ import ( | |||||||
| 	"unicode" | 	"unicode" | ||||||
|  |  | ||||||
| 	"k8s.io/apimachinery/pkg/util/validation/field" | 	"k8s.io/apimachinery/pkg/util/validation/field" | ||||||
| 	netutils "k8s.io/utils/net" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| const qnameCharFmt string = "[A-Za-z0-9]" | const qnameCharFmt string = "[A-Za-z0-9]" | ||||||
| @@ -369,45 +368,6 @@ func IsValidPortName(port string) []string { | |||||||
| 	return errs | 	return errs | ||||||
| } | } | ||||||
|  |  | ||||||
| // IsValidIP tests that the argument is a valid IP address. |  | ||||||
| func IsValidIP(fldPath *field.Path, value string) field.ErrorList { |  | ||||||
| 	var allErrors field.ErrorList |  | ||||||
| 	if netutils.ParseIPSloppy(value) == nil { |  | ||||||
| 		allErrors = append(allErrors, field.Invalid(fldPath, value, "must be a valid IP address, (e.g. 10.9.8.7 or 2001:db8::ffff)").WithOrigin("format=ip-sloppy")) |  | ||||||
| 	} |  | ||||||
| 	return allErrors |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // IsValidIPv4Address tests that the argument is a valid IPv4 address. |  | ||||||
| func IsValidIPv4Address(fldPath *field.Path, value string) field.ErrorList { |  | ||||||
| 	var allErrors field.ErrorList |  | ||||||
| 	ip := netutils.ParseIPSloppy(value) |  | ||||||
| 	if ip == nil || ip.To4() == nil { |  | ||||||
| 		allErrors = append(allErrors, field.Invalid(fldPath, value, "must be a valid IPv4 address")) |  | ||||||
| 	} |  | ||||||
| 	return allErrors |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // IsValidIPv6Address tests that the argument is a valid IPv6 address. |  | ||||||
| func IsValidIPv6Address(fldPath *field.Path, value string) field.ErrorList { |  | ||||||
| 	var allErrors field.ErrorList |  | ||||||
| 	ip := netutils.ParseIPSloppy(value) |  | ||||||
| 	if ip == nil || ip.To4() != nil { |  | ||||||
| 		allErrors = append(allErrors, field.Invalid(fldPath, value, "must be a valid IPv6 address")) |  | ||||||
| 	} |  | ||||||
| 	return allErrors |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // IsValidCIDR tests that the argument is a valid CIDR value. |  | ||||||
| func IsValidCIDR(fldPath *field.Path, value string) field.ErrorList { |  | ||||||
| 	var allErrors field.ErrorList |  | ||||||
| 	_, _, err := netutils.ParseCIDRSloppy(value) |  | ||||||
| 	if err != nil { |  | ||||||
| 		allErrors = append(allErrors, field.Invalid(fldPath, value, "must be a valid CIDR value, (e.g. 10.9.8.0/24 or 2001:db8::/64)")) |  | ||||||
| 	} |  | ||||||
| 	return allErrors |  | ||||||
| } |  | ||||||
|  |  | ||||||
| const percentFmt string = "[0-9]+%" | const percentFmt string = "[0-9]+%" | ||||||
| const percentErrMsg string = "a valid percent string must be a numeric string followed by an ending '%'" | const percentErrMsg string = "a valid percent string must be a numeric string followed by an ending '%'" | ||||||
|  |  | ||||||
|   | |||||||
| @@ -322,302 +322,6 @@ func TestIsValidLabelValue(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestIsValidIP(t *testing.T) { |  | ||||||
| 	for _, tc := range []struct { |  | ||||||
| 		name   string |  | ||||||
| 		in     string |  | ||||||
| 		family int |  | ||||||
| 		err    string |  | ||||||
| 	}{ |  | ||||||
| 		// GOOD VALUES |  | ||||||
| 		{ |  | ||||||
| 			name:   "ipv4", |  | ||||||
| 			in:     "1.2.3.4", |  | ||||||
| 			family: 4, |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name:   "ipv4, all zeros", |  | ||||||
| 			in:     "0.0.0.0", |  | ||||||
| 			family: 4, |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name:   "ipv4, max", |  | ||||||
| 			in:     "255.255.255.255", |  | ||||||
| 			family: 4, |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name:   "ipv6", |  | ||||||
| 			in:     "1234::abcd", |  | ||||||
| 			family: 6, |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name:   "ipv6, all zeros, collapsed", |  | ||||||
| 			in:     "::", |  | ||||||
| 			family: 6, |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name:   "ipv6, max", |  | ||||||
| 			in:     "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", |  | ||||||
| 			family: 6, |  | ||||||
| 		}, |  | ||||||
|  |  | ||||||
| 		// GOOD, THOUGH NON-CANONICAL, VALUES |  | ||||||
| 		{ |  | ||||||
| 			name:   "ipv6, all zeros, expanded (non-canonical)", |  | ||||||
| 			in:     "0:0:0:0:0:0:0:0", |  | ||||||
| 			family: 6, |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name:   "ipv6, leading 0s (non-canonical)", |  | ||||||
| 			in:     "0001:002:03:4::", |  | ||||||
| 			family: 6, |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name:   "ipv6, capital letters (non-canonical)", |  | ||||||
| 			in:     "1234::ABCD", |  | ||||||
| 			family: 6, |  | ||||||
| 		}, |  | ||||||
|  |  | ||||||
| 		// BAD VALUES WE CURRENTLY CONSIDER GOOD |  | ||||||
| 		{ |  | ||||||
| 			name:   "ipv4 with leading 0s", |  | ||||||
| 			in:     "1.1.1.01", |  | ||||||
| 			family: 4, |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name:   "ipv4-in-ipv6 value", |  | ||||||
| 			in:     "::ffff:1.1.1.1", |  | ||||||
| 			family: 4, |  | ||||||
| 		}, |  | ||||||
|  |  | ||||||
| 		// BAD VALUES |  | ||||||
| 		{ |  | ||||||
| 			name: "empty string", |  | ||||||
| 			in:   "", |  | ||||||
| 			err:  "must be a valid IP address", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name: "junk", |  | ||||||
| 			in:   "aaaaaaa", |  | ||||||
| 			err:  "must be a valid IP address", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name: "domain name", |  | ||||||
| 			in:   "myhost.mydomain", |  | ||||||
| 			err:  "must be a valid IP address", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name: "cidr", |  | ||||||
| 			in:   "1.2.3.0/24", |  | ||||||
| 			err:  "must be a valid IP address", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name: "ipv4 with out-of-range octets", |  | ||||||
| 			in:   "1.2.3.400", |  | ||||||
| 			err:  "must be a valid IP address", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name: "ipv4 with negative octets", |  | ||||||
| 			in:   "-1.0.0.0", |  | ||||||
| 			err:  "must be a valid IP address", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name: "ipv6 with out-of-range segment", |  | ||||||
| 			in:   "2001:db8::10005", |  | ||||||
| 			err:  "must be a valid IP address", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name: "ipv4:port", |  | ||||||
| 			in:   "1.2.3.4:80", |  | ||||||
| 			err:  "must be a valid IP address", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name: "ipv6 with brackets", |  | ||||||
| 			in:   "[2001:db8::1]", |  | ||||||
| 			err:  "must be a valid IP address", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name: "[ipv6]:port", |  | ||||||
| 			in:   "[2001:db8::1]:80", |  | ||||||
| 			err:  "must be a valid IP address", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name: "host:port", |  | ||||||
| 			in:   "example.com:80", |  | ||||||
| 			err:  "must be a valid IP address", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name: "ipv6 with zone", |  | ||||||
| 			in:   "1234::abcd%eth0", |  | ||||||
| 			err:  "must be a valid IP address", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name: "ipv4 with zone", |  | ||||||
| 			in:   "169.254.0.0%eth0", |  | ||||||
| 			err:  "must be a valid IP address", |  | ||||||
| 		}, |  | ||||||
| 	} { |  | ||||||
| 		t.Run(tc.name, func(t *testing.T) { |  | ||||||
| 			errs := IsValidIP(field.NewPath(""), tc.in) |  | ||||||
| 			if tc.err == "" { |  | ||||||
| 				if len(errs) != 0 { |  | ||||||
| 					t.Errorf("expected %q to be valid but got: %v", tc.in, errs) |  | ||||||
| 				} |  | ||||||
| 			} else { |  | ||||||
| 				if len(errs) != 1 { |  | ||||||
| 					t.Errorf("expected %q to have 1 error but got: %v", tc.in, errs) |  | ||||||
| 				} else if !strings.Contains(errs[0].Detail, tc.err) { |  | ||||||
| 					t.Errorf("expected error for %q to contain %q but got: %q", tc.in, tc.err, errs[0].Detail) |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			errs = IsValidIPv4Address(field.NewPath(""), tc.in) |  | ||||||
| 			if tc.family == 4 { |  | ||||||
| 				if len(errs) != 0 { |  | ||||||
| 					t.Errorf("expected %q to pass IsValidIPv4Address but got: %v", tc.in, errs) |  | ||||||
| 				} |  | ||||||
| 			} else { |  | ||||||
| 				if len(errs) == 0 { |  | ||||||
| 					t.Errorf("expected %q to fail IsValidIPv4Address", tc.in) |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			errs = IsValidIPv6Address(field.NewPath(""), tc.in) |  | ||||||
| 			if tc.family == 6 { |  | ||||||
| 				if len(errs) != 0 { |  | ||||||
| 					t.Errorf("expected %q to pass IsValidIPv6Address but got: %v", tc.in, errs) |  | ||||||
| 				} |  | ||||||
| 			} else { |  | ||||||
| 				if len(errs) == 0 { |  | ||||||
| 					t.Errorf("expected %q to fail IsValidIPv6Address", tc.in) |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		}) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestIsValidCIDR(t *testing.T) { |  | ||||||
| 	for _, tc := range []struct { |  | ||||||
| 		name string |  | ||||||
| 		in   string |  | ||||||
| 		err  string |  | ||||||
| 	}{ |  | ||||||
| 		// GOOD VALUES |  | ||||||
| 		{ |  | ||||||
| 			name: "ipv4", |  | ||||||
| 			in:   "1.0.0.0/8", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name: "ipv4, all IPs", |  | ||||||
| 			in:   "0.0.0.0/0", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name: "ipv4, single IP", |  | ||||||
| 			in:   "1.1.1.1/32", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name: "ipv6", |  | ||||||
| 			in:   "2001:4860:4860::/48", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name: "ipv6, all IPs", |  | ||||||
| 			in:   "::/0", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name: "ipv6, single IP", |  | ||||||
| 			in:   "::1/128", |  | ||||||
| 		}, |  | ||||||
|  |  | ||||||
| 		// GOOD, THOUGH NON-CANONICAL, VALUES |  | ||||||
| 		{ |  | ||||||
| 			name: "ipv6, extra 0s (non-canonical)", |  | ||||||
| 			in:   "2a00:79e0:2:0::/64", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name: "ipv6, capital letters (non-canonical)", |  | ||||||
| 			in:   "2001:DB8::/64", |  | ||||||
| 		}, |  | ||||||
|  |  | ||||||
| 		// BAD VALUES WE CURRENTLY CONSIDER GOOD |  | ||||||
| 		{ |  | ||||||
| 			name: "ipv4 with leading 0s", |  | ||||||
| 			in:   "1.1.01.0/24", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name: "ipv4-in-ipv6 with ipv4-sized prefix", |  | ||||||
| 			in:   "::ffff:1.1.1.0/24", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name: "ipv4-in-ipv6 with ipv6-sized prefix", |  | ||||||
| 			in:   "::ffff:1.1.1.0/120", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name: "ipv4 with bits past prefix", |  | ||||||
| 			in:   "1.2.3.4/24", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name: "ipv6 with bits past prefix", |  | ||||||
| 			in:   "2001:db8::1/64", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name: "prefix length with leading 0s", |  | ||||||
| 			in:   "192.168.0.0/016", |  | ||||||
| 		}, |  | ||||||
|  |  | ||||||
| 		// BAD VALUES |  | ||||||
| 		{ |  | ||||||
| 			name: "empty string", |  | ||||||
| 			in:   "", |  | ||||||
| 			err:  "must be a valid CIDR value", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name: "junk", |  | ||||||
| 			in:   "aaaaaaa", |  | ||||||
| 			err:  "must be a valid CIDR value", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name: "IP address", |  | ||||||
| 			in:   "1.2.3.4", |  | ||||||
| 			err:  "must be a valid CIDR value", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name: "partial URL", |  | ||||||
| 			in:   "192.168.0.1/healthz", |  | ||||||
| 			err:  "must be a valid CIDR value", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name: "partial URL 2", |  | ||||||
| 			in:   "192.168.0.1/0/99", |  | ||||||
| 			err:  "must be a valid CIDR value", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name: "negative prefix length", |  | ||||||
| 			in:   "192.168.0.0/-16", |  | ||||||
| 			err:  "must be a valid CIDR value", |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			name: "prefix length with sign", |  | ||||||
| 			in:   "192.168.0.0/+16", |  | ||||||
| 			err:  "must be a valid CIDR value", |  | ||||||
| 		}, |  | ||||||
| 	} { |  | ||||||
| 		t.Run(tc.name, func(t *testing.T) { |  | ||||||
| 			errs := IsValidCIDR(field.NewPath(""), tc.in) |  | ||||||
| 			if tc.err == "" { |  | ||||||
| 				if len(errs) != 0 { |  | ||||||
| 					t.Errorf("expected %q to be valid but got: %v", tc.in, errs) |  | ||||||
| 				} |  | ||||||
| 			} else { |  | ||||||
| 				if len(errs) != 1 { |  | ||||||
| 					t.Errorf("expected %q to have 1 error but got: %v", tc.in, errs) |  | ||||||
| 				} else if !strings.Contains(errs[0].Detail, tc.err) { |  | ||||||
| 					t.Errorf("expected error for %q to contain %q but got: %q", tc.in, tc.err, errs[0].Detail) |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		}) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestIsHTTPHeaderName(t *testing.T) { | func TestIsHTTPHeaderName(t *testing.T) { | ||||||
| 	goodValues := []string{ | 	goodValues := []string{ | ||||||
| 		// Common ones | 		// Common ones | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Dan Winship
					Dan Winship