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" | ||||
|  | ||||
| 	"k8s.io/apimachinery/pkg/util/validation/field" | ||||
| 	netutils "k8s.io/utils/net" | ||||
| ) | ||||
|  | ||||
| const qnameCharFmt string = "[A-Za-z0-9]" | ||||
| @@ -369,45 +368,6 @@ func IsValidPortName(port string) []string { | ||||
| 	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 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) { | ||||
| 	goodValues := []string{ | ||||
| 		// Common ones | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Dan Winship
					Dan Winship