1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-06 07:57:07 +00:00
kyverno/pkg/engine/validate/pattern.go

293 lines
8.4 KiB
Go
Raw Normal View History

package validate
2019-05-27 14:45:54 +03:00
import (
2020-03-17 11:05:20 -07:00
"fmt"
2019-05-27 14:45:54 +03:00
"math"
2019-05-27 18:07:24 +03:00
"regexp"
"strconv"
2019-05-27 14:45:54 +03:00
"strings"
2019-05-27 18:07:24 +03:00
2020-03-17 11:05:20 -07:00
"github.com/go-logr/logr"
"github.com/kyverno/kyverno/pkg/engine/operator"
2019-05-27 18:07:24 +03:00
"github.com/minio/minio/pkg/wildcard"
2019-12-13 13:17:22 -08:00
apiresource "k8s.io/apimachinery/pkg/api/resource"
2019-05-27 18:07:24 +03:00
)
2019-12-13 13:17:22 -08:00
type quantity int
const (
equal quantity = 0
lessThan quantity = -1
greaterThan quantity = 1
)
2019-05-27 14:45:54 +03:00
// ValidateValueWithPattern validates value with operators and wildcards
2020-03-17 11:05:20 -07:00
func ValidateValueWithPattern(log logr.Logger, value, pattern interface{}) bool {
2019-05-27 14:45:54 +03:00
switch typedPattern := pattern.(type) {
case bool:
typedValue, ok := value.(bool)
if !ok {
2020-03-17 11:05:20 -07:00
log.V(4).Info("Expected type bool", "type", fmt.Sprintf("%T", value), "value", value)
2019-05-27 14:45:54 +03:00
return false
}
return typedPattern == typedValue
case int:
2020-03-17 11:05:20 -07:00
return validateValueWithIntPattern(log, value, int64(typedPattern))
2019-05-27 18:07:24 +03:00
case int64:
2020-03-17 11:05:20 -07:00
return validateValueWithIntPattern(log, value, typedPattern)
2019-05-27 14:45:54 +03:00
case float64:
2020-03-17 11:05:20 -07:00
return validateValueWithFloatPattern(log, value, typedPattern)
2019-05-27 14:45:54 +03:00
case string:
2020-03-17 11:05:20 -07:00
return validateValueWithStringPatterns(log, value, typedPattern)
2019-05-27 18:07:24 +03:00
case nil:
2020-03-17 11:05:20 -07:00
return validateValueWithNilPattern(log, value)
2019-08-27 11:41:47 -07:00
case map[string]interface{}:
2019-09-25 16:06:37 -07:00
// TODO: check if this is ever called?
2020-03-17 11:05:20 -07:00
return validateValueWithMapPattern(log, value, typedPattern)
2019-08-27 11:41:47 -07:00
case []interface{}:
2019-09-25 16:06:37 -07:00
// TODO: check if this is ever called?
log.Info("arrays are not supported as patterns")
2019-05-27 14:45:54 +03:00
return false
default:
2020-11-17 12:01:01 -08:00
log.Info("Unknown type", "type", fmt.Sprintf("%T", typedPattern), "value", typedPattern)
2019-05-27 14:45:54 +03:00
return false
}
}
2020-03-17 11:05:20 -07:00
func validateValueWithMapPattern(log logr.Logger, value interface{}, typedPattern map[string]interface{}) bool {
2019-08-27 11:41:47 -07:00
// verify the type of the resource value is map[string]interface,
// we only check for existence of object, not the equality of content and value
2019-09-25 16:06:37 -07:00
//TODO: check if adding
2019-08-27 11:41:47 -07:00
_, ok := value.(map[string]interface{})
if !ok {
2020-03-17 11:05:20 -07:00
log.Info("Expected type map[string]interface{}", "type", fmt.Sprintf("%T", value), "value", value)
2019-08-27 11:41:47 -07:00
return false
}
return true
}
2019-06-10 17:32:26 +03:00
// Handler for int values during validation process
2020-03-17 11:05:20 -07:00
func validateValueWithIntPattern(log logr.Logger, value interface{}, pattern int64) bool {
2019-05-27 14:45:54 +03:00
switch typedValue := value.(type) {
case int:
2019-05-27 18:07:24 +03:00
return int64(typedValue) == pattern
case int64:
2019-05-27 14:45:54 +03:00
return typedValue == pattern
case float64:
// check that float has no fraction
if typedValue == math.Trunc(typedValue) {
2019-05-27 18:07:24 +03:00
return int64(typedValue) == pattern
2019-05-27 14:45:54 +03:00
}
2020-03-17 11:05:20 -07:00
log.Info("Expected type int", "type", fmt.Sprintf("%T", typedValue), "value", typedValue)
2019-05-27 14:45:54 +03:00
return false
case string:
// extract int64 from string
int64Num, err := strconv.ParseInt(typedValue, 10, 64)
if err != nil {
2020-03-17 11:05:20 -07:00
log.Error(err, "Failed to parse int64 from string")
return false
}
return int64Num == pattern
2019-05-27 14:45:54 +03:00
default:
2020-03-17 11:05:20 -07:00
log.Info("Expected type int", "type", fmt.Sprintf("%T", value), "value", value)
2019-05-27 14:45:54 +03:00
return false
}
}
2019-06-10 17:32:26 +03:00
// Handler for float values during validation process
2020-03-17 11:05:20 -07:00
func validateValueWithFloatPattern(log logr.Logger, value interface{}, pattern float64) bool {
2019-05-27 14:45:54 +03:00
switch typedValue := value.(type) {
case int:
// check that float has no fraction
if pattern == math.Trunc(pattern) {
return int(pattern) == value
}
2020-03-17 11:05:20 -07:00
log.Info("Expected type float", "type", fmt.Sprintf("%T", typedValue), "value", typedValue)
return false
case int64:
// check that float has no fraction
if pattern == math.Trunc(pattern) {
return int64(pattern) == value
}
2020-03-17 11:05:20 -07:00
log.Info("Expected type float", "type", fmt.Sprintf("%T", typedValue), "value", typedValue)
2019-05-27 14:45:54 +03:00
return false
case float64:
return typedValue == pattern
case string:
// extract float64 from string
float64Num, err := strconv.ParseFloat(typedValue, 64)
if err != nil {
2020-03-17 11:05:20 -07:00
log.Error(err, "Failed to parse float64 from string")
return false
}
return float64Num == pattern
2019-05-27 14:45:54 +03:00
default:
2020-03-17 11:05:20 -07:00
log.Info("Expected type float", "type", fmt.Sprintf("%T", value), "value", value)
2019-05-27 14:45:54 +03:00
return false
}
}
2019-06-10 17:32:26 +03:00
// Handler for nil values during validation process
2020-03-17 11:05:20 -07:00
func validateValueWithNilPattern(log logr.Logger, value interface{}) bool {
2019-05-27 14:45:54 +03:00
switch typed := value.(type) {
case float64:
return typed == 0.0
case int:
return typed == 0
2019-05-27 18:07:24 +03:00
case int64:
return typed == 0
2019-05-27 14:45:54 +03:00
case string:
return typed == ""
case bool:
2020-01-27 08:58:53 -08:00
return !typed
2019-05-27 14:45:54 +03:00
case nil:
return true
case map[string]interface{}, []interface{}:
2020-03-17 11:05:20 -07:00
log.Info("Maps and arrays could not be checked with nil pattern")
2019-05-27 14:45:54 +03:00
return false
default:
2020-03-17 11:05:20 -07:00
log.Info("Unknown type as value when checking for nil pattern", "type", fmt.Sprintf("%T", value), "value", value)
2019-05-27 14:45:54 +03:00
return false
}
}
2019-06-10 17:32:26 +03:00
// Handler for pattern values during validation process
2020-03-17 11:05:20 -07:00
func validateValueWithStringPatterns(log logr.Logger, value interface{}, pattern string) bool {
conditions := strings.Split(pattern, "|")
for _, condition := range conditions {
condition = strings.Trim(condition, " ")
if checkForAndConditionsAndValidate(log, value, condition) {
2019-05-27 14:45:54 +03:00
return true
}
}
return false
}
func checkForAndConditionsAndValidate(log logr.Logger, value interface{}, pattern string) bool {
conditions := strings.Split(pattern, "&")
for _, condition := range conditions {
condition = strings.Trim(condition, " ")
if !validateValueWithStringPattern(log, value, condition) {
return false
}
}
return true
}
2019-06-10 17:32:26 +03:00
// Handler for single pattern value during validation process
// Detects if pattern has a number
2020-03-17 11:05:20 -07:00
func validateValueWithStringPattern(log logr.Logger, value interface{}, pattern string) bool {
operator := operator.GetOperatorFromStringPattern(pattern)
2019-05-27 18:07:24 +03:00
pattern = pattern[len(operator):]
number, str := getNumberAndStringPartsFromPattern(pattern)
if "" == number {
2020-03-17 11:05:20 -07:00
return validateString(log, value, str, operator)
2019-05-27 18:07:24 +03:00
}
2020-03-17 11:05:20 -07:00
return validateNumberWithStr(log, value, pattern, operator)
2019-05-27 14:45:54 +03:00
}
2019-05-27 18:07:24 +03:00
2019-06-10 17:32:26 +03:00
// Handler for string values
2020-03-17 11:05:20 -07:00
func validateString(log logr.Logger, value interface{}, pattern string, operatorVariable operator.Operator) bool {
if operator.NotEqual == operatorVariable || operator.Equal == operatorVariable {
var strValue string
var ok bool = false
switch value.(type) {
case float64:
strValue = strconv.FormatFloat(value.(float64), 'E', -1, 64)
ok = true
case int:
strValue = strconv.FormatInt(int64(value.(int)), 10)
ok = true
case int64:
strValue = strconv.FormatInt(value.(int64), 10)
ok = true
case string:
strValue = value.(string)
ok = true
case bool:
strValue = strconv.FormatBool(value.(bool))
ok = true
case nil:
ok = false
}
2019-05-27 18:07:24 +03:00
if !ok {
2020-11-03 15:41:17 -08:00
log.V(4).Info("unexpected type", "got", value, "expect", pattern)
2019-05-27 18:07:24 +03:00
return false
}
wildcardResult := wildcard.Match(pattern, strValue)
if operator.NotEqual == operatorVariable {
2019-05-27 18:07:24 +03:00
return !wildcardResult
}
return wildcardResult
}
2020-03-17 11:05:20 -07:00
log.Info("Operators >, >=, <, <= are not applicable to strings")
2019-05-27 18:07:24 +03:00
return false
}
2019-12-13 13:17:22 -08:00
// validateNumberWithStr compares quantity if pattern type is quantity
// or a wildcard match to pattern string
2020-03-17 11:05:20 -07:00
func validateNumberWithStr(log logr.Logger, value interface{}, pattern string, operator operator.Operator) bool {
2020-10-22 11:59:11 -07:00
typedValue, err := convertNumberToString(value)
2019-12-13 13:17:22 -08:00
if err != nil {
2020-03-17 11:05:20 -07:00
log.Error(err, "failed to convert to string")
2019-12-13 13:17:22 -08:00
return false
}
2019-05-27 18:07:24 +03:00
2019-12-13 13:17:22 -08:00
patternQuan, err := apiresource.ParseQuantity(pattern)
// 1. nil error - quantity comparison
if err == nil {
valueQuan, err := apiresource.ParseQuantity(typedValue)
if err != nil {
2020-03-17 11:05:20 -07:00
log.Error(err, "invalid quantity in resource", "type", fmt.Sprintf("%T", typedValue), "value", typedValue)
2019-05-27 18:07:24 +03:00
return false
}
2019-12-13 13:17:22 -08:00
return compareQuantity(valueQuan, patternQuan, operator)
2019-05-27 18:07:24 +03:00
}
2019-12-13 13:17:22 -08:00
// 2. wildcard match
if !wildcard.Match(pattern, typedValue) {
2020-03-17 11:05:20 -07:00
log.Info("value failed wildcard check", "type", fmt.Sprintf("%T", typedValue), "value", typedValue, "check", pattern)
2019-05-27 18:07:24 +03:00
return false
}
2019-12-13 13:17:22 -08:00
return true
}
2019-05-27 18:07:24 +03:00
func compareQuantity(value, pattern apiresource.Quantity, op operator.Operator) bool {
2019-12-13 13:17:22 -08:00
result := value.Cmp(pattern)
switch op {
case operator.Equal:
2019-12-13 13:17:22 -08:00
return result == int(equal)
case operator.NotEqual:
2019-12-13 13:17:22 -08:00
return result != int(equal)
case operator.More:
2019-12-13 13:17:22 -08:00
return result == int(greaterThan)
case operator.Less:
2019-12-13 13:17:22 -08:00
return result == int(lessThan)
case operator.MoreEqual:
2019-12-13 13:17:22 -08:00
return (result == int(equal)) || (result == int(greaterThan))
case operator.LessEqual:
2019-12-13 13:17:22 -08:00
return (result == int(equal)) || (result == int(lessThan))
2019-05-27 18:07:24 +03:00
}
return false
}
2019-06-10 17:32:26 +03:00
// detects numerical and string parts in pattern and returns them
2019-05-27 18:07:24 +03:00
func getNumberAndStringPartsFromPattern(pattern string) (number, str string) {
regexpStr := `^(\d*(\.\d+)?)(.*)`
re := regexp.MustCompile(regexpStr)
matches := re.FindAllStringSubmatch(pattern, -1)
match := matches[0]
return match[1], match[3]
}