Add special IP validations to IP CEL type

This commit is contained in:
Joel Speed 2023-11-17 12:51:32 +00:00
parent c6aa360d3e
commit 13b22b23a1
No known key found for this signature in database
GPG Key ID: 6E80578D6751DEFB
2 changed files with 102 additions and 2 deletions

View File

@ -78,16 +78,51 @@ import (
// ip.isCanonical('2001:DB8::ABCD') // returns false
// ip.isCanonical('2001:db8::0:0:0:abcd') // returns false
//
// family
// family / isUnspecified / isLoopback / isLinkLocalMulticast / isLinkLocalUnicast / isGlobalUnicast
//
// - family: returns the IP addresses' family (IPv4 or IPv6) as an integer, either '4' or '6'.
//
// - isUnspecified: returns true if the IP address is the unspecified address.
// Either the IPv4 address "0.0.0.0" or the IPv6 address "::".
//
// - isLoopback: returns true if the IP address is the loopback address.
// Either an IPv4 address with a value of 127.x.x.x or an IPv6 address with a value of ::1.
//
// - isLinkLocalMulticast: returns true if the IP address is a link-local multicast address.
// Either an IPv4 address with a value of 224.0.0.x or an IPv6 address in the network ff00::/8.
//
// - isLinkLocalUnicast: returns true if the IP address is a link-local unicast address.
// Either an IPv4 address with a value of 169.254.x.x or an IPv6 address in the network fe80::/10.
//
// - isGlobalUnicast: returns true if the IP address is a global unicast address.
// Either an IPv4 address that is not zero or 255.255.255.255 or an IPv6 address that is not a link-local unicast, loopback or multicast address.
//
// Examples:
//
// ip('127.0.0.1').family() // returns '4”
// ip('::1').family() // returns '6'
// ip('127.0.0.1').family() == 4 // returns true
// ip('::1').family() == 6 // returns true
// ip('0.0.0.0').isUnspecified() // returns true
// ip('127.0.0.1').isUnspecified() // returns false
// ip('::').isUnspecified() // returns true
// ip('::1').isUnspecified() // returns false
// ip('127.0.0.1').isLoopback() // returns true
// ip('192.168.0.1').isLoopback() // returns false
// ip('::1').isLoopback() // returns true
// ip('2001:db8::abcd').isLoopback() // returns false
// ip('224.0.0.1').isLinkLocalMulticast() // returns true
// ip('224.0.1.1').isLinkLocalMulticast() // returns false
// ip('ff02::1').isLinkLocalMulticast() // returns true
// ip('fd00::1').isLinkLocalMulticast() // returns false
// ip('169.254.169.254').isLinkLocalUnicast() // returns true
// ip('192.168.0.1').isLinkLocalUnicast() // returns false
// ip('fe80::1').isLinkLocalUnicast() // returns true
// ip('fd80::1').isLinkLocalUnicast() // returns false
// ip('192.168.0.1').isGlobalUnicast() // returns true
// ip('255.255.255.255').isGlobalUnicast() // returns false
// ip('2001:db8::abcd').isGlobalUnicast() // returns true
// ip('ff00::1').isGlobalUnicast() // returns false
func IP() cel.EnvOption {
return cel.Lib(ipLib)
}
@ -113,6 +148,26 @@ var ipLibraryDecls = map[string][]cel.FunctionOpt{
cel.Overload("ip_is_canonical", []*cel.Type{cel.StringType}, cel.BoolType,
cel.UnaryBinding(ipIsCanonical)),
},
"isUnspecified": {
cel.MemberOverload("ip_is_unspecified", []*cel.Type{apiservercel.IPType}, cel.BoolType,
cel.UnaryBinding(isUnspecified)),
},
"isLoopback": {
cel.MemberOverload("ip_is_loopback", []*cel.Type{apiservercel.IPType}, cel.BoolType,
cel.UnaryBinding(isLoopback)),
},
"isLinkLocalMulticast": {
cel.MemberOverload("ip_is_link_local_multicast", []*cel.Type{apiservercel.IPType}, cel.BoolType,
cel.UnaryBinding(isLinkLocalMulticast)),
},
"isLinkLocalUnicast": {
cel.MemberOverload("ip_is_link_local_unicast", []*cel.Type{apiservercel.IPType}, cel.BoolType,
cel.UnaryBinding(isLinkLocalUnicast)),
},
"isGlobalUnicast": {
cel.MemberOverload("ip_is_global_unicast", []*cel.Type{apiservercel.IPType}, cel.BoolType,
cel.UnaryBinding(isGlobalUnicast)),
},
"isIP": {
cel.Overload("is_ip", []*cel.Type{cel.StringType}, cel.BoolType,
cel.UnaryBinding(isIP)),
@ -207,6 +262,51 @@ func isIP(arg ref.Val) ref.Val {
return types.Bool(err == nil)
}
func isUnspecified(arg ref.Val) ref.Val {
ip, ok := arg.(apiservercel.IP)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
}
return types.Bool(ip.Addr.IsUnspecified())
}
func isLoopback(arg ref.Val) ref.Val {
ip, ok := arg.(apiservercel.IP)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
}
return types.Bool(ip.Addr.IsLoopback())
}
func isLinkLocalMulticast(arg ref.Val) ref.Val {
ip, ok := arg.(apiservercel.IP)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
}
return types.Bool(ip.Addr.IsLinkLocalMulticast())
}
func isLinkLocalUnicast(arg ref.Val) ref.Val {
ip, ok := arg.(apiservercel.IP)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
}
return types.Bool(ip.Addr.IsLinkLocalUnicast())
}
func isGlobalUnicast(arg ref.Val) ref.Val {
ip, ok := arg.(apiservercel.IP)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
}
return types.Bool(ip.Addr.IsGlobalUnicast())
}
// parseIPAddr parses a string into an IP address.
// We use this function to parse IP addresses in the CEL library
// so that we can share the common logic of rejecting IP addresses

View File

@ -48,7 +48,7 @@ func TestLibraryCompatibility(t *testing.T) {
// Kubernetes <1.29>:
"add", "asApproximateFloat", "asInteger", "compareTo", "isGreaterThan", "isInteger", "isLessThan", "isQuantity", "quantity", "sign", "sub",
// Kubernetes <1.30>:
"ip", "family", "ip.isCanonical", "isIP", "string",
"ip", "family", "isUnspecified", "isLoopback", "isLinkLocalMulticast", "isLinkLocalUnicast", "isGlobalUnicast", "ip.isCanonical", "isIP", "string",
// Kubernetes <1.??>:
)