1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-06 16:06:56 +00:00
kyverno/pkg/engine/validate/pattern.go
shivkumar dudhani 5659f2fbcf merge master
2019-12-12 18:44:52 -08:00

254 lines
7 KiB
Go

package validate
import (
"math"
"regexp"
"strconv"
"strings"
"github.com/golang/glog"
"github.com/minio/minio/pkg/wildcard"
"github.com/nirmata/kyverno/pkg/engine/operator"
)
// ValidateValueWithPattern validates value with operators and wildcards
func ValidateValueWithPattern(value, pattern interface{}) bool {
switch typedPattern := pattern.(type) {
case bool:
typedValue, ok := value.(bool)
if !ok {
glog.V(4).Infof("Expected bool, found %T", value)
return false
}
return typedPattern == typedValue
case int:
return validateValueWithIntPattern(value, int64(typedPattern))
case int64:
return validateValueWithIntPattern(value, typedPattern)
case float64:
return validateValueWithFloatPattern(value, typedPattern)
case string:
return validateValueWithStringPatterns(value, typedPattern)
case nil:
return validateValueWithNilPattern(value)
case map[string]interface{}:
// TODO: check if this is ever called?
return validateValueWithMapPattern(value, typedPattern)
case []interface{}:
// TODO: check if this is ever called?
glog.Warning("Arrays as patterns are not supported")
return false
default:
glog.Warningf("Unknown type as pattern: %v", typedPattern)
return false
}
}
func validateValueWithMapPattern(value interface{}, typedPattern map[string]interface{}) bool {
// verify the type of the resource value is map[string]interface,
// we only check for existance of object, not the equality of content and value
//TODO: check if adding
_, ok := value.(map[string]interface{})
if !ok {
glog.Warningf("Expected map[string]interface{}, found %T\n", value)
return false
}
return true
}
// Handler for int values during validation process
func validateValueWithIntPattern(value interface{}, pattern int64) bool {
switch typedValue := value.(type) {
case int:
return int64(typedValue) == pattern
case int64:
return typedValue == pattern
case float64:
// check that float has no fraction
if typedValue == math.Trunc(typedValue) {
return int64(typedValue) == pattern
}
glog.Warningf("Expected int, found float: %f\n", typedValue)
return false
case string:
// extract int64 from string
int64Num, err := strconv.ParseInt(typedValue, 10, 64)
if err != nil {
glog.Warningf("Failed to parse int64 from string: %v", err)
return false
}
return int64Num == pattern
default:
glog.Warningf("Expected int, found: %T\n", value)
return false
}
}
// Handler for float values during validation process
func validateValueWithFloatPattern(value interface{}, pattern float64) bool {
switch typedValue := value.(type) {
case int:
// check that float has no fraction
if pattern == math.Trunc(pattern) {
return int(pattern) == value
}
glog.Warningf("Expected float, found int: %d\n", typedValue)
return false
case int64:
// check that float has no fraction
if pattern == math.Trunc(pattern) {
return int64(pattern) == value
}
glog.Warningf("Expected float, found int: %d\n", typedValue)
return false
case float64:
return typedValue == pattern
case string:
// extract float64 from string
float64Num, err := strconv.ParseFloat(typedValue, 64)
if err != nil {
glog.Warningf("Failed to parse float64 from string: %v", err)
return false
}
return float64Num == pattern
default:
glog.Warningf("Expected float, found: %T\n", value)
return false
}
}
// Handler for nil values during validation process
func validateValueWithNilPattern(value interface{}) bool {
switch typed := value.(type) {
case float64:
return typed == 0.0
case int:
return typed == 0
case int64:
return typed == 0
case string:
return typed == ""
case bool:
return typed == false
case nil:
return true
case map[string]interface{}, []interface{}:
glog.Warningf("Maps and arrays could not be checked with nil pattern")
return false
default:
glog.Warningf("Unknown type as value when checking for nil pattern: %T\n", value)
return false
}
}
// Handler for pattern values during validation process
func validateValueWithStringPatterns(value interface{}, pattern string) bool {
statements := strings.Split(pattern, "|")
for _, statement := range statements {
statement = strings.Trim(statement, " ")
if validateValueWithStringPattern(value, statement) {
return true
}
}
return false
}
// Handler for single pattern value during validation process
// Detects if pattern has a number
func validateValueWithStringPattern(value interface{}, pattern string) bool {
operator := operator.GetOperatorFromStringPattern(pattern)
pattern = pattern[len(operator):]
number, str := getNumberAndStringPartsFromPattern(pattern)
if "" == number {
return validateString(value, str, operator)
}
return validateNumberWithStr(value, number, str, operator)
}
// Handler for string values
func validateString(value interface{}, pattern string, operatorVariable operator.Operator) bool {
if operator.NotEqual == operatorVariable || operator.Equal == operatorVariable {
strValue, ok := value.(string)
if !ok {
glog.Warningf("Expected string, found %T\n", value)
return false
}
wildcardResult := wildcard.Match(pattern, strValue)
if operator.NotEqual == operatorVariable {
return !wildcardResult
}
return wildcardResult
}
glog.Warningf("Operators >, >=, <, <= are not applicable to strings")
return false
}
// validateNumberWithStr applies wildcard to suffix and operator to numerical part
func validateNumberWithStr(value interface{}, patternNumber, patternStr string, operator operator.Operator) bool {
// pattern has suffix
if "" != patternStr {
typedValue, ok := value.(string)
if !ok {
glog.Warningf("Number must have suffix: %s", patternStr)
return false
}
valueNumber, valueStr := getNumberAndStringPartsFromPattern(typedValue)
if !wildcard.Match(patternStr, valueStr) {
glog.Warningf("Suffix %s has not passed wildcard check: %s", valueStr, patternStr)
return false
}
return validateNumber(valueNumber, patternNumber, operator)
}
return validateNumber(value, patternNumber, operator)
}
// validateNumber compares two numbers with operator
func validateNumber(value, pattern interface{}, operatorVariable operator.Operator) bool {
floatPattern, err := convertToFloat(pattern)
if err != nil {
return false
}
floatValue, err := convertToFloat(value)
if err != nil {
return false
}
switch operatorVariable {
case operator.Equal:
return floatValue == floatPattern
case operator.NotEqual:
return floatValue != floatPattern
case operator.More:
return floatValue > floatPattern
case operator.MoreEqual:
return floatValue >= floatPattern
case operator.Less:
return floatValue < floatPattern
case operator.LessEqual:
return floatValue <= floatPattern
}
return false
}
// detects numerical and string parts in pattern and returns them
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]
}