diff --git a/pkg/cosign/cosign.go b/pkg/cosign/cosign.go index 2a8670bef5..8351ec6bb9 100644 --- a/pkg/cosign/cosign.go +++ b/pkg/cosign/cosign.go @@ -9,25 +9,22 @@ import ( "fmt" "strings" - v1 "github.com/kyverno/kyverno/api/kyverno/v1" - - "github.com/sigstore/cosign/cmd/cosign/cli/rekor" - - "github.com/sigstore/cosign/cmd/cosign/cli/fulcio" - "github.com/sigstore/cosign/pkg/oci/remote" - "github.com/go-logr/logr" "github.com/google/go-containerregistry/pkg/name" gcrremote "github.com/google/go-containerregistry/pkg/v1/remote" "github.com/in-toto/in-toto-golang/in_toto" wildcard "github.com/kyverno/go-wildcard" - "github.com/kyverno/kyverno/pkg/engine/common" + v1 "github.com/kyverno/kyverno/api/kyverno/v1" "github.com/kyverno/kyverno/pkg/registryclient" + "github.com/kyverno/kyverno/pkg/utils" "github.com/pkg/errors" + "github.com/sigstore/cosign/cmd/cosign/cli/fulcio" "github.com/sigstore/cosign/cmd/cosign/cli/options" + "github.com/sigstore/cosign/cmd/cosign/cli/rekor" "github.com/sigstore/cosign/pkg/cosign" "github.com/sigstore/cosign/pkg/cosign/attestation" "github.com/sigstore/cosign/pkg/oci" + "github.com/sigstore/cosign/pkg/oci/remote" sigs "github.com/sigstore/cosign/pkg/signature" "github.com/sigstore/sigstore/pkg/cryptoutils" "github.com/sigstore/sigstore/pkg/signature" @@ -269,7 +266,7 @@ func decodeStatement(payloadBase64 string) (map[string]interface{}, error) { // - in_toto.PredicateLinkV1 // - in_toto.PredicateSPDX // any other custom predicate - return common.ToMap(statement) + return utils.ToMap(statement) } return decodeCosignCustomProvenanceV01(statement) @@ -297,7 +294,7 @@ func decodeCosignCustomProvenanceV01(statement in_toto.Statement) (map[string]in statement.Predicate = predicate } - return common.ToMap(statement) + return utils.ToMap(statement) } func stringToJSONMap(i interface{}) (map[string]interface{}, error) { diff --git a/pkg/engine/common/utils.go b/pkg/engine/common/utils.go index b14e44f41f..3e165f429b 100644 --- a/pkg/engine/common/utils.go +++ b/pkg/engine/common/utils.go @@ -1,7 +1,6 @@ package common import ( - "encoding/json" "fmt" "reflect" @@ -10,53 +9,6 @@ import ( "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" ) -// CopyMap creates a full copy of the target map -func CopyMap(m map[string]interface{}) map[string]interface{} { - mapCopy := make(map[string]interface{}) - for k, v := range m { - mapCopy[k] = v - } - - return mapCopy -} - -// CopySlice creates a full copy of the target slice -func CopySlice(s []interface{}) []interface{} { - sliceCopy := make([]interface{}, len(s)) - copy(sliceCopy, s) - - return sliceCopy -} - -// CopySliceOfMaps creates a full copy of the target slice -func CopySliceOfMaps(s []map[string]interface{}) []interface{} { - sliceCopy := make([]interface{}, len(s)) - for i, v := range s { - sliceCopy[i] = CopyMap(v) - } - - return sliceCopy -} - -func ToMap(data interface{}) (map[string]interface{}, error) { - if m, ok := data.(map[string]interface{}); ok { - return m, nil - } - - b, err := json.Marshal(data) - if err != nil { - return nil, err - } - - mapData := make(map[string]interface{}) - err = json.Unmarshal(b, &mapData) - if err != nil { - return nil, err - } - - return mapData, nil -} - func GetRawKeyIfWrappedWithAttributes(str string) string { if len(str) < 2 { return str diff --git a/pkg/engine/common/utils_test.go b/pkg/engine/common/utils_test.go deleted file mode 100644 index 152cfe41da..0000000000 --- a/pkg/engine/common/utils_test.go +++ /dev/null @@ -1,37 +0,0 @@ -package common - -import ( - "testing" - - "gotest.tools/assert" -) - -func Test_OriginalMapMustNotBeChanged(t *testing.T) { - // no variables - originalMap := map[string]interface{}{ - "rsc": 3711, - "r": 2138, - "gri": 1908, - "adg": 912, - } - - mapCopy := CopyMap(originalMap) - mapCopy["r"] = 1 - - assert.Equal(t, originalMap["r"], 2138) -} - -func Test_OriginalSliceMustNotBeChanged(t *testing.T) { - // no variables - originalSlice := []interface{}{ - 3711, - 2138, - 1908, - 912, - } - - sliceCopy := CopySlice(originalSlice) - sliceCopy[0] = 1 - - assert.Equal(t, originalSlice[0], 3711) -} diff --git a/pkg/engine/jsonutils/traverse.go b/pkg/engine/jsonutils/traverse.go index 624f1773c8..738e484548 100644 --- a/pkg/engine/jsonutils/traverse.go +++ b/pkg/engine/jsonutils/traverse.go @@ -4,7 +4,7 @@ import ( "fmt" "strconv" - "github.com/kyverno/kyverno/pkg/engine/common" + "github.com/kyverno/kyverno/pkg/utils" ) // ActionData represents data available for action on current element @@ -69,13 +69,13 @@ func (t *Traversal) traverseJSON(element interface{}, path string) (interface{}, // traverse further switch typed := element.(type) { case map[string]interface{}: - return t.traverseObject(common.CopyMap(typed), path) + return t.traverseObject(utils.CopyMap(typed), path) case []interface{}: - return t.traverseList(common.CopySlice(typed), path) + return t.traverseList(utils.CopySlice(typed), path) case []map[string]interface{}: - return t.traverseList(common.CopySliceOfMaps(typed), path) + return t.traverseList(utils.CopySliceOfMaps(typed), path) case Key: return typed.Key, nil diff --git a/pkg/engine/mutate/mutation.go b/pkg/engine/mutate/mutation.go index c118c6b3f6..eb51281c49 100644 --- a/pkg/engine/mutate/mutation.go +++ b/pkg/engine/mutate/mutation.go @@ -8,11 +8,11 @@ import ( "github.com/go-logr/logr" kyverno "github.com/kyverno/kyverno/api/kyverno/v1" - "github.com/kyverno/kyverno/pkg/engine/common" "github.com/kyverno/kyverno/pkg/engine/context" "github.com/kyverno/kyverno/pkg/engine/mutate/patch" "github.com/kyverno/kyverno/pkg/engine/response" "github.com/kyverno/kyverno/pkg/engine/variables" + "github.com/kyverno/kyverno/pkg/utils" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) @@ -92,7 +92,7 @@ func ForEach(name string, foreach *kyverno.ForEachMutation, ctx *context.Context } func substituteAllInForEach(fe *kyverno.ForEachMutation, ctx *context.Context, logger logr.Logger) (*kyverno.ForEachMutation, error) { - jsonObj, err := common.ToMap(fe) + jsonObj, err := utils.ToMap(fe) if err != nil { return nil, err } diff --git a/pkg/engine/validation.go b/pkg/engine/validation.go index 9fa04045c5..ccf62f188c 100644 --- a/pkg/engine/validation.go +++ b/pkg/engine/validation.go @@ -16,9 +16,10 @@ import ( "github.com/go-logr/logr" gojmespath "github.com/jmespath/go-jmespath" "github.com/kyverno/kyverno/pkg/engine/response" - "github.com/kyverno/kyverno/pkg/engine/utils" + engineutils "github.com/kyverno/kyverno/pkg/engine/utils" "github.com/kyverno/kyverno/pkg/engine/validate" "github.com/kyverno/kyverno/pkg/engine/variables" + "github.com/kyverno/kyverno/pkg/utils" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "sigs.k8s.io/controller-runtime/pkg/log" ) @@ -179,7 +180,7 @@ func newValidator(log logr.Logger, ctx *PolicyContext, rule *kyverno.Rule) *vali func newForeachValidator(foreach *kyverno.ForEachValidation, rule *kyverno.Rule, ctx *PolicyContext, log logr.Logger) *validator { ruleCopy := rule.DeepCopy() - anyAllConditions, err := common.ToMap(foreach.AnyAllConditions) + anyAllConditions, err := utils.ToMap(foreach.AnyAllConditions) if err != nil { log.Error(err, "failed to convert ruleCopy.Validation.ForEachValidation.AnyAllConditions") } @@ -198,16 +199,16 @@ func newForeachValidator(foreach *kyverno.ForEachValidation, rule *kyverno.Rule, func (v *validator) validate() *response.RuleResponse { if err := v.loadContext(); err != nil { - return ruleError(v.rule, utils.Validation, "failed to load context", err) + return ruleError(v.rule, engineutils.Validation, "failed to load context", err) } preconditionsPassed, err := checkPreconditions(v.log, v.ctx, v.anyAllConditions) if err != nil { - return ruleError(v.rule, utils.Validation, "failed to evaluate preconditions", err) + return ruleError(v.rule, engineutils.Validation, "failed to evaluate preconditions", err) } if !preconditionsPassed && v.ctx.Policy.GetSpec().ValidationFailureAction != kyverno.Audit { - return ruleResponse(v.rule, utils.Validation, "preconditions not met", response.RuleStatusSkip) + return ruleResponse(v.rule, engineutils.Validation, "preconditions not met", response.RuleStatusSkip) } if v.deny != nil { @@ -216,14 +217,14 @@ func (v *validator) validate() *response.RuleResponse { if v.pattern != nil || v.anyPattern != nil { if err = v.substitutePatterns(); err != nil { - return ruleError(v.rule, utils.Validation, "variable substitution failed", err) + return ruleError(v.rule, engineutils.Validation, "variable substitution failed", err) } ruleResponse := v.validateResourceWithRule() if isUpdateRequest(v.ctx) { priorResp, err := validateOldObject(v.log, v.ctx, v.rule) if err != nil { - return ruleError(v.rule, utils.Validation, "failed to validate old object", err) + return ruleError(v.rule, engineutils.Validation, "failed to validate old object", err) } if isSameRuleResponse(ruleResponse, priorResp) { @@ -241,14 +242,14 @@ func (v *validator) validate() *response.RuleResponse { func (v *validator) validateForEach() *response.RuleResponse { if err := v.loadContext(); err != nil { - return ruleError(v.rule, utils.Validation, "failed to load context", err) + return ruleError(v.rule, engineutils.Validation, "failed to load context", err) } preconditionsPassed, err := checkPreconditions(v.log, v.ctx, v.anyAllConditions) if err != nil { - return ruleError(v.rule, utils.Validation, "failed to evaluate preconditions", err) + return ruleError(v.rule, engineutils.Validation, "failed to evaluate preconditions", err) } else if !preconditionsPassed && v.ctx.Policy.GetSpec().ValidationFailureAction != kyverno.Audit { - return ruleResponse(v.rule, utils.Validation, "preconditions not met", response.RuleStatusSkip) + return ruleResponse(v.rule, engineutils.Validation, "preconditions not met", response.RuleStatusSkip) } foreachList := v.rule.Validation.ForEachValidation @@ -278,10 +279,10 @@ func (v *validator) validateForEach() *response.RuleResponse { } if applyCount == 0 { - return ruleResponse(v.rule, utils.Validation, "rule skipped", response.RuleStatusSkip) + return ruleResponse(v.rule, engineutils.Validation, "rule skipped", response.RuleStatusSkip) } - return ruleResponse(v.rule, utils.Validation, "rule passed", response.RuleStatusPass) + return ruleResponse(v.rule, engineutils.Validation, "rule passed", response.RuleStatusPass) } func (v *validator) validateElements(foreach *kyverno.ForEachValidation, elements []interface{}, elementScope bool) (*response.RuleResponse, int) { @@ -295,7 +296,7 @@ func (v *validator) validateElements(foreach *kyverno.ForEachValidation, element ctx := v.ctx.Copy() if err := addElementToContext(ctx, e, i, elementScope); err != nil { v.log.Error(err, "failed to add element to context") - return ruleError(v.rule, utils.Validation, "failed to process foreach", err), applyCount + return ruleError(v.rule, engineutils.Validation, "failed to process foreach", err), applyCount } foreachValidator := newForeachValidator(foreach, v.rule, ctx, v.log) @@ -308,17 +309,17 @@ func (v *validator) validateElements(foreach *kyverno.ForEachValidation, element continue } else if r.Status != response.RuleStatusPass { msg := fmt.Sprintf("validation failure: %v", r.Message) - return ruleResponse(v.rule, utils.Validation, msg, r.Status), applyCount + return ruleResponse(v.rule, engineutils.Validation, msg, r.Status), applyCount } applyCount++ } - return ruleResponse(v.rule, utils.Validation, "", response.RuleStatusPass), applyCount + return ruleResponse(v.rule, engineutils.Validation, "", response.RuleStatusPass), applyCount } func addElementToContext(ctx *PolicyContext, e interface{}, elementIndex int, elementScope bool) error { - data, err := common.ToMap(e) + data, err := utils.ToMap(e) if err != nil { return err } @@ -359,24 +360,24 @@ func (v *validator) validateDeny() *response.RuleResponse { anyAllCond := v.deny.GetAnyAllConditions() anyAllCond, err := variables.SubstituteAll(v.log, v.ctx.JSONContext, anyAllCond) if err != nil { - return ruleError(v.rule, utils.Validation, "failed to substitute variables in deny conditions", err) + return ruleError(v.rule, engineutils.Validation, "failed to substitute variables in deny conditions", err) } if err = v.substituteDeny(); err != nil { - return ruleError(v.rule, utils.Validation, "failed to substitute variables in rule", err) + return ruleError(v.rule, engineutils.Validation, "failed to substitute variables in rule", err) } denyConditions, err := common.TransformConditions(anyAllCond) if err != nil { - return ruleError(v.rule, utils.Validation, "invalid deny conditions", err) + return ruleError(v.rule, engineutils.Validation, "invalid deny conditions", err) } deny := variables.EvaluateConditions(v.log, v.ctx.JSONContext, denyConditions) if deny { - return ruleResponse(v.rule, utils.Validation, v.getDenyMessage(deny), response.RuleStatusFail) + return ruleResponse(v.rule, engineutils.Validation, v.getDenyMessage(deny), response.RuleStatusFail) } - return ruleResponse(v.rule, utils.Validation, v.getDenyMessage(deny), response.RuleStatusPass) + return ruleResponse(v.rule, engineutils.Validation, v.getDenyMessage(deny), response.RuleStatusPass) } func (v *validator) getDenyMessage(deny bool) string { @@ -480,22 +481,22 @@ func (v *validator) validatePatterns(resource unstructured.Unstructured) *respon v.log.V(3).Info("validation error", "path", pe.Path, "error", err.Error()) if pe.Skip { - return ruleResponse(v.rule, utils.Validation, pe.Error(), response.RuleStatusSkip) + return ruleResponse(v.rule, engineutils.Validation, pe.Error(), response.RuleStatusSkip) } if pe.Path == "" { - return ruleResponse(v.rule, utils.Validation, v.buildErrorMessage(err, ""), response.RuleStatusError) + return ruleResponse(v.rule, engineutils.Validation, v.buildErrorMessage(err, ""), response.RuleStatusError) } - return ruleResponse(v.rule, utils.Validation, v.buildErrorMessage(err, pe.Path), response.RuleStatusFail) + return ruleResponse(v.rule, engineutils.Validation, v.buildErrorMessage(err, pe.Path), response.RuleStatusFail) } - return ruleResponse(v.rule, utils.Validation, v.buildErrorMessage(err, pe.Path), response.RuleStatusError) + return ruleResponse(v.rule, engineutils.Validation, v.buildErrorMessage(err, pe.Path), response.RuleStatusError) } v.log.V(4).Info("successfully processed rule") msg := fmt.Sprintf("validation rule '%s' passed.", v.rule.Name) - return ruleResponse(v.rule, utils.Validation, msg, response.RuleStatusPass) + return ruleResponse(v.rule, engineutils.Validation, msg, response.RuleStatusPass) } if v.anyPattern != nil { @@ -505,14 +506,14 @@ func (v *validator) validatePatterns(resource unstructured.Unstructured) *respon anyPatterns, err := deserializeAnyPattern(v.anyPattern) if err != nil { msg := fmt.Sprintf("failed to deserialize anyPattern, expected type array: %v", err) - return ruleResponse(v.rule, utils.Validation, msg, response.RuleStatusError) + return ruleResponse(v.rule, engineutils.Validation, msg, response.RuleStatusError) } for idx, pattern := range anyPatterns { err := validate.MatchPattern(v.log, resource.Object, pattern) if err == nil { msg := fmt.Sprintf("validation rule '%s' anyPattern[%d] passed.", v.rule.Name, idx) - return ruleResponse(v.rule, utils.Validation, msg, response.RuleStatusPass) + return ruleResponse(v.rule, engineutils.Validation, msg, response.RuleStatusPass) } if pe, ok := err.(*validate.PatternError); ok { @@ -536,11 +537,11 @@ func (v *validator) validatePatterns(resource unstructured.Unstructured) *respon v.log.V(4).Info(fmt.Sprintf("Validation rule '%s' failed. %s", v.rule.Name, errorStr)) msg := buildAnyPatternErrorMessage(v.rule, errorStr) - return ruleResponse(v.rule, utils.Validation, msg, response.RuleStatusFail) + return ruleResponse(v.rule, engineutils.Validation, msg, response.RuleStatusFail) } } - return ruleResponse(v.rule, utils.Validation, v.rule.Validation.Message, response.RuleStatusPass) + return ruleResponse(v.rule, engineutils.Validation, v.rule.Validation.Message, response.RuleStatusPass) } func deserializeAnyPattern(anyPattern apiextensions.JSON) ([]interface{}, error) { diff --git a/pkg/utils/util.go b/pkg/utils/util.go index 396f1ba3a1..80139413fe 100644 --- a/pkg/utils/util.go +++ b/pkg/utils/util.go @@ -25,6 +25,53 @@ import ( var regexVersion = regexp.MustCompile(`v(\d+).(\d+).(\d+)\.*`) +// CopyMap creates a full copy of the target map +func CopyMap(m map[string]interface{}) map[string]interface{} { + mapCopy := make(map[string]interface{}) + for k, v := range m { + mapCopy[k] = v + } + + return mapCopy +} + +// CopySlice creates a full copy of the target slice +func CopySlice(s []interface{}) []interface{} { + sliceCopy := make([]interface{}, len(s)) + copy(sliceCopy, s) + + return sliceCopy +} + +// CopySliceOfMaps creates a full copy of the target slice +func CopySliceOfMaps(s []map[string]interface{}) []interface{} { + sliceCopy := make([]interface{}, len(s)) + for i, v := range s { + sliceCopy[i] = CopyMap(v) + } + + return sliceCopy +} + +func ToMap(data interface{}) (map[string]interface{}, error) { + if m, ok := data.(map[string]interface{}); ok { + return m, nil + } + + b, err := json.Marshal(data) + if err != nil { + return nil, err + } + + mapData := make(map[string]interface{}) + err = json.Unmarshal(b, &mapData) + if err != nil { + return nil, err + } + + return mapData, nil +} + // Contains checks if a string is contained in a list of string func contains(list []string, element string, fn func(string, string) bool) bool { for _, e := range list { diff --git a/pkg/utils/util_test.go b/pkg/utils/util_test.go index 8198b0d9d3..e78987134b 100644 --- a/pkg/utils/util_test.go +++ b/pkg/utils/util_test.go @@ -6,6 +6,36 @@ import ( "gotest.tools/assert" ) +func Test_OriginalMapMustNotBeChanged(t *testing.T) { + // no variables + originalMap := map[string]interface{}{ + "rsc": 3711, + "r": 2138, + "gri": 1908, + "adg": 912, + } + + mapCopy := CopyMap(originalMap) + mapCopy["r"] = 1 + + assert.Equal(t, originalMap["r"], 2138) +} + +func Test_OriginalSliceMustNotBeChanged(t *testing.T) { + // no variables + originalSlice := []interface{}{ + 3711, + 2138, + 1908, + 912, + } + + sliceCopy := CopySlice(originalSlice) + sliceCopy[0] = 1 + + assert.Equal(t, originalSlice[0], 3711) +} + func Test_allEmpty(t *testing.T) { var list []string var element string