1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-05 07:26:55 +00:00

Extend new operators (#2788)

* extending new operators

Signed-off-by: anushkamittal20 <anumittal4641@gmail.com>

* Changes in file names

Signed-off-by: anushkamittal20 <anumittal4641@gmail.com>

* tests added

Signed-off-by: anushkamittal20 <anumittal4641@gmail.com>

* removed print statements

Signed-off-by: anushkamittal20 <anumittal4641@gmail.com>

* Changes to reduce code redundancy

Signed-off-by: anushkamittal20 <anumittal4641@gmail.com>

* Minor corrections in anyin and allin

Signed-off-by: anushkamittal20 <anumittal4641@gmail.com>

* added correction for anynotin and allnotin

Signed-off-by: anushkamittal20 <anumittal4641@gmail.com>
This commit is contained in:
Anushka Mittal 2022-01-04 23:07:00 +05:30 committed by GitHub
parent d126280184
commit 3089edafa4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 186 additions and 14 deletions

View file

@ -1,4 +1,4 @@
package validate
package common
import (
"fmt"

View file

@ -1,4 +1,4 @@
package validate
package common
import (
"fmt"

View file

@ -1,4 +1,4 @@
package validate
package common
import (
"encoding/json"

View file

@ -7,7 +7,7 @@ import (
"github.com/go-logr/logr"
commonAnchors "github.com/kyverno/kyverno/pkg/engine/anchor/common"
"github.com/kyverno/kyverno/pkg/engine/validate"
"github.com/kyverno/kyverno/pkg/engine/common"
"sigs.k8s.io/controller-runtime/pkg/log"
)
@ -144,7 +144,7 @@ func compareOverlay(resource, overlay interface{}, path string) (string, overlay
}
}
case string, float64, int, int64, bool, nil:
if !validate.ValidateValueWithPattern(log.Log, resource, overlay) {
if !common.ValidateValueWithPattern(log.Log, resource, overlay) {
log.Log.V(4).Info(fmt.Sprintf("Mutate rule: failed validating value %v with overlay %v", resource, overlay))
return path, newOverlayError(conditionFailure, fmt.Sprintf("Failed validating value %v with overlay %v", resource, overlay))
}

View file

@ -79,13 +79,13 @@ func validateResourceElement(log logr.Logger, resourceElement, patternElement, o
switch resource := resourceElement.(type) {
case []interface{}:
for _, res := range resource {
if !ValidateValueWithPattern(log, res, patternElement) {
if !common.ValidateValueWithPattern(log, res, patternElement) {
return path, fmt.Errorf("resource value '%v' does not match '%v' at path %s", resourceElement, patternElement, path)
}
}
return "", nil
default:
if !ValidateValueWithPattern(log, resourceElement, patternElement) {
if !common.ValidateValueWithPattern(log, resourceElement, patternElement) {
return path, fmt.Errorf("resource value '%v' does not match '%v' at path %s", resourceElement, patternElement, path)
}
}

View file

@ -291,6 +291,12 @@ func TestEvaluate(t *testing.T) {
{kyverno.Condition{Key: []interface{}{"5*"}, Operator: kyverno.ConditionOperators["AnyIn"], Value: []interface{}{"1.1.1.1", "2.2.2.2", "3.3.3.3"}}, false},
{kyverno.Condition{Key: []interface{}{"1.1.1.1", "2.2.2.2", "5.5.5.5"}, Operator: kyverno.ConditionOperators["AnyIn"], Value: []interface{}{"2*"}}, true},
{kyverno.Condition{Key: []interface{}{"1.1.1.1", "2.2.2.2", "5.5.5.5"}, Operator: kyverno.ConditionOperators["AnyIn"], Value: []interface{}{"4*"}}, false},
{kyverno.Condition{Key: []interface{}{"5"}, Operator: kyverno.ConditionOperators["AnyIn"], Value: "1-3"}, false},
{kyverno.Condition{Key: "5", Operator: kyverno.ConditionOperators["AnyIn"], Value: "1-10"}, true},
{kyverno.Condition{Key: 5, Operator: kyverno.ConditionOperators["AnyIn"], Value: "1-10"}, true},
{kyverno.Condition{Key: []interface{}{1, 5}, Operator: kyverno.ConditionOperators["AnyIn"], Value: "7-10"}, false},
{kyverno.Condition{Key: []interface{}{1, 5}, Operator: kyverno.ConditionOperators["AnyIn"], Value: "0-10"}, true},
{kyverno.Condition{Key: []interface{}{1.002, 1.222}, Operator: kyverno.ConditionOperators["AnyIn"], Value: "1.001-10"}, true},
// All In
{kyverno.Condition{Key: []interface{}{"1.1.1.1", "2.2.2.2"}, Operator: kyverno.ConditionOperators["AllIn"], Value: []interface{}{"1.1.1.1", "2.2.2.2", "3.3.3.3"}}, true},
@ -305,6 +311,9 @@ func TestEvaluate(t *testing.T) {
{kyverno.Condition{Key: []interface{}{"2.1.1.1", "2.2.2.2", "2.5.5.5"}, Operator: kyverno.ConditionOperators["AllIn"], Value: []interface{}{"2*"}}, true},
{kyverno.Condition{Key: []interface{}{"1.1.1.1", "2.2.2.2", "5.5.5.5"}, Operator: kyverno.ConditionOperators["AllIn"], Value: []interface{}{"4*"}}, false},
{kyverno.Condition{Key: []interface{}{"1.1.1.1", "5.5.5.5"}, Operator: kyverno.ConditionOperators["AllIn"], Value: "5.5.5.5"}, false},
{kyverno.Condition{Key: 5, Operator: kyverno.ConditionOperators["AllIn"], Value: "1-10"}, true},
{kyverno.Condition{Key: []interface{}{3, 2}, Operator: kyverno.ConditionOperators["AllIn"], Value: "1-10"}, true},
{kyverno.Condition{Key: []interface{}{3, 2}, Operator: kyverno.ConditionOperators["AllIn"], Value: "5-10"}, false},
// All Not In
{kyverno.Condition{Key: 1, Operator: kyverno.ConditionOperators["AllNotIn"], Value: []interface{}{1, 2, 3}}, false},
@ -323,6 +332,10 @@ func TestEvaluate(t *testing.T) {
{kyverno.Condition{Key: []interface{}{"1.1.1.1", "3.3.3.3", "5.5.5.5"}, Operator: kyverno.ConditionOperators["AllNotIn"], Value: []interface{}{"2*"}}, true},
{kyverno.Condition{Key: []interface{}{"4.1.1.1", "4.2.2.2", "4.5.5.5"}, Operator: kyverno.ConditionOperators["AllNotIn"], Value: []interface{}{"4*"}}, false},
{kyverno.Condition{Key: []interface{}{"1.1.1.1", "4.4.4.4"}, Operator: kyverno.ConditionOperators["AllNotIn"], Value: "2.2.2.2"}, true},
{kyverno.Condition{Key: 5.5, Operator: kyverno.ConditionOperators["AllNotIn"], Value: "6-10"}, true},
{kyverno.Condition{Key: "5", Operator: kyverno.ConditionOperators["AllNotIn"], Value: "1-6"}, false},
{kyverno.Condition{Key: []interface{}{3, 2}, Operator: kyverno.ConditionOperators["AllNotIn"], Value: "5-10"}, true},
{kyverno.Condition{Key: []interface{}{2, 6}, Operator: kyverno.ConditionOperators["AllNotIn"], Value: "5-10"}, false},
// Any Not In
{kyverno.Condition{Key: 1, Operator: kyverno.ConditionOperators["AnyNotIn"], Value: []interface{}{1, 2, 3}}, false},
@ -338,6 +351,9 @@ func TestEvaluate(t *testing.T) {
{kyverno.Condition{Key: []interface{}{"1*", "3*", "5*"}, Operator: kyverno.ConditionOperators["AnyNotIn"], Value: []interface{}{"1.1.1.1", "2.2.2.2", "3.3.3.3"}}, true},
{kyverno.Condition{Key: []interface{}{"1.1.1.1", "2.2.2.2", "5.5.5.5"}, Operator: kyverno.ConditionOperators["AnyNotIn"], Value: []interface{}{"2*"}}, true},
{kyverno.Condition{Key: []interface{}{"2.2*"}, Operator: kyverno.ConditionOperators["AnyNotIn"], Value: []interface{}{"2.2.2.2"}}, false},
{kyverno.Condition{Key: "5", Operator: kyverno.ConditionOperators["AnyNotIn"], Value: "1-3"}, true},
{kyverno.Condition{Key: []interface{}{1, 5, 11}, Operator: kyverno.ConditionOperators["AnyNotIn"], Value: "0-10"}, true},
{kyverno.Condition{Key: []interface{}{1, 5, 7}, Operator: kyverno.ConditionOperators["AnyNotIn"], Value: "0-10"}, false},
}
ctx := context.NewContext()

View file

@ -6,6 +6,7 @@ import (
"github.com/go-logr/logr"
"github.com/kyverno/kyverno/pkg/engine/context"
"github.com/kyverno/kyverno/pkg/engine/operator"
"github.com/minio/pkg/wildcard"
)
@ -28,6 +29,8 @@ func (allin AllInHandler) Evaluate(key, value interface{}) bool {
switch typedKey := key.(type) {
case string:
return allin.validateValueWithStringPattern(typedKey, value)
case int, int32, int64, float32, float64:
return allin.validateValueWithStringPattern(fmt.Sprint(typedKey), value)
case []interface{}:
var stringSlice []string
for _, v := range typedKey {
@ -41,7 +44,7 @@ func (allin AllInHandler) Evaluate(key, value interface{}) bool {
}
func (allin AllInHandler) validateValueWithStringPattern(key string, value interface{}) (keyExists bool) {
invalidType, keyExists := keyExistsInArray(key, value, allin.log)
invalidType, keyExists := allKeyExistsInArray(key, value, allin.log)
if invalidType {
allin.log.Info("expected type []string", "value", value, "type", fmt.Sprintf("%T", value))
return false
@ -50,6 +53,49 @@ func (allin AllInHandler) validateValueWithStringPattern(key string, value inter
return keyExists
}
func allKeyExistsInArray(key string, value interface{}, log logr.Logger) (invalidType bool, keyExists bool) {
switch valuesAvailable := value.(type) {
case []interface{}:
for _, val := range valuesAvailable {
if wildcard.Match(key, fmt.Sprint(val)) {
return false, true
}
}
case string:
if wildcard.Match(valuesAvailable, key) {
return false, true
}
operatorVariable := operator.GetOperatorFromStringPattern(fmt.Sprintf("%v", value))
if operatorVariable == operator.InRange {
return false, handleRange(key, value, log)
}
var arr []string
if json.Valid([]byte(valuesAvailable)) {
if err := json.Unmarshal([]byte(valuesAvailable), &arr); err != nil {
log.Error(err, "failed to unmarshal value to JSON string array", "key", key, "value", value)
return true, false
}
} else {
arr = append(arr, valuesAvailable)
}
for _, val := range arr {
if key == val {
return false, true
}
}
default:
return true, false
}
return false, false
}
func (allin AllInHandler) validateValueWithStringSetPattern(key []string, value interface{}) (keyExists bool) {
invalidType, isAllIn := allSetExistsInArray(key, value, allin.log, false)
if invalidType {
@ -80,9 +126,36 @@ func allSetExistsInArray(key []string, value interface{}, log logr.Logger, allNo
case string:
if len(key) == 1 && key[0] == valuesAvailable {
if allNotIn {
return false, false
}
return false, true
}
operatorVariable := operator.GetOperatorFromStringPattern(fmt.Sprintf("%v", value))
if operatorVariable == operator.InRange {
if allNotIn {
isAllNotInBool := true
for _, k := range key {
if handleRange(k, valuesAvailable, log) {
isAllNotInBool = false
}
}
return false, isAllNotInBool
} else {
isAllInCount := 0
for _, k := range key {
if handleRange(k, value, log) {
isAllInCount++
}
}
if isAllInCount == len(key) {
return false, true
}
return false, false
}
}
var arr []string
if json.Valid([]byte(valuesAvailable)) {
if err := json.Unmarshal([]byte(valuesAvailable), &arr); err != nil {

View file

@ -41,7 +41,7 @@ func (allnin AllNotInHandler) Evaluate(key, value interface{}) bool {
}
func (allnin AllNotInHandler) validateValueWithStringPattern(key string, value interface{}) bool {
invalidType, keyExists := keyExistsInArray(key, value, allnin.log)
invalidType, keyExists := allKeyExistsInArray(key, value, allnin.log)
if invalidType {
allnin.log.Info("expected type []string", "value", value, "type", fmt.Sprintf("%T", value))
return false

View file

@ -3,9 +3,12 @@ package operator
import (
"encoding/json"
"fmt"
"strings"
"github.com/go-logr/logr"
"github.com/kyverno/kyverno/pkg/engine/common"
"github.com/kyverno/kyverno/pkg/engine/context"
"github.com/kyverno/kyverno/pkg/engine/operator"
"github.com/minio/pkg/wildcard"
)
@ -43,7 +46,7 @@ func (anyin AnyInHandler) Evaluate(key, value interface{}) bool {
}
func (anyin AnyInHandler) validateValueWithStringPattern(key string, value interface{}) (keyExists bool) {
invalidType, keyExists := keyExistsInArray(key, value, anyin.log)
invalidType, keyExists := anyKeyExistsInArray(key, value, anyin.log)
if invalidType {
anyin.log.Info("expected type []string", "value", value, "type", fmt.Sprintf("%T", value))
return false
@ -52,6 +55,60 @@ func (anyin AnyInHandler) validateValueWithStringPattern(key string, value inter
return keyExists
}
// anykeyExistsInArray checks if the key exists in the array value
// The value can be a string, an array of strings, or a JSON format
// array of strings (e.g. ["val1", "val2", "val3"].
func anyKeyExistsInArray(key string, value interface{}, log logr.Logger) (invalidType bool, keyExists bool) {
switch valuesAvailable := value.(type) {
case []interface{}:
for _, val := range valuesAvailable {
if wildcard.Match(key, fmt.Sprint(val)) {
return false, true
}
}
case string:
if wildcard.Match(valuesAvailable, key) {
return false, true
}
operatorVariable := operator.GetOperatorFromStringPattern(fmt.Sprintf("%v", value))
if operatorVariable == operator.InRange {
return false, handleRange(key, value, log)
}
var arr []string
if json.Valid([]byte(valuesAvailable)) {
if err := json.Unmarshal([]byte(valuesAvailable), &arr); err != nil {
log.Error(err, "failed to unmarshal value to JSON string array", "key", key, "value", value)
return true, false
}
} else {
arr = append(arr, valuesAvailable)
}
for _, val := range arr {
if key == val {
return false, true
}
}
default:
return true, false
}
return false, false
}
func handleRange(key string, value interface{}, log logr.Logger) bool {
if !common.ValidateValueWithPattern(log, key, value) {
return false
} else {
return true
}
}
func (anyin AnyInHandler) validateValueWithStringSetPattern(key []string, value interface{}) (keyExists bool) {
invalidType, isAnyIn := anySetExistsInArray(key, value, anyin.log, false)
if invalidType {
@ -80,11 +137,37 @@ func anySetExistsInArray(key []string, value interface{}, log logr.Logger, anyNo
return false, isAnyIn(key, valueSlice)
case string:
if len(key) == 1 && key[0] == valuesAvailable {
if anyNotIn {
return false, false
}
return false, true
}
operatorVariable := operator.GetOperatorFromStringPattern(fmt.Sprintf("%v", value))
if operatorVariable == operator.InRange {
if anyNotIn {
isAnyNotInBool := false
stringForAnyNotIn := strings.Replace(valuesAvailable, "-", "!-", 1)
for _, k := range key {
if handleRange(k, stringForAnyNotIn, log) {
isAnyNotInBool = true
break
}
}
return false, isAnyNotInBool
} else {
isAnyInBool := false
for _, k := range key {
if handleRange(k, value, log) {
isAnyInBool = true
break
}
}
return false, isAnyInBool
}
}
var arr []string
if json.Valid([]byte(valuesAvailable)) {
if err := json.Unmarshal([]byte(valuesAvailable), &arr); err != nil {

View file

@ -41,7 +41,7 @@ func (anynin AnyNotInHandler) Evaluate(key, value interface{}) bool {
}
func (anynin AnyNotInHandler) validateValueWithStringPattern(key string, value interface{}) bool {
invalidType, keyExists := keyExistsInArray(key, value, anynin.log)
invalidType, keyExists := anyKeyExistsInArray(key, value, anynin.log)
if invalidType {
anynin.log.Info("expected type []string", "value", value, "type", fmt.Sprintf("%T", value))
return false

View file

@ -6,7 +6,7 @@ import (
"strconv"
"github.com/go-logr/logr"
"github.com/kyverno/kyverno/pkg/engine/validate"
"github.com/kyverno/kyverno/pkg/engine/common"
"github.com/kyverno/kyverno/pkg/engine/wildcards"
"sigs.k8s.io/controller-runtime/pkg/log"
)
@ -54,7 +54,7 @@ func validateResourceElement(log logr.Logger, resourceElement, patternElement, o
return validateArray(log, typedResourceElement, typedPatternElement, originPattern, path)
// elementary values
case string, float64, int, int64, bool, nil:
if !validate.ValidateValueWithPattern(log, resourceElement, patternElement) {
if !common.ValidateValueWithPattern(log, resourceElement, patternElement) {
return path, fmt.Errorf("value '%v' does not match '%v' at path %s", resourceElement, patternElement, path)
}