diff --git a/api/kyverno/v1/image_verification_test.go b/api/kyverno/v1/image_verification_test.go index 67883bd92e..3ff7a88178 100644 --- a/api/kyverno/v1/image_verification_test.go +++ b/api/kyverno/v1/image_verification_test.go @@ -199,9 +199,116 @@ func Test_ImageVerification(t *testing.T) { }, } + isAuditFailureAction := false for _, test := range testCases { subject := test.subject.Convert() - errs := subject.Validate(path) + errs := subject.Validate(isAuditFailureAction, path) + var expectedErrs field.ErrorList + if test.errors != nil { + expectedErrs = test.errors(subject) + } + + assert.Equal(t, len(errs), len(expectedErrs), fmt.Sprintf("test `%s` error count mismatch, errors %v", test.name, errs)) + if len(errs) != 0 { + assert.DeepEqual(t, errs, expectedErrs) + } + } +} + +func Test_Audit_VerifyImageRule(t *testing.T) { + path := field.NewPath("dummy") + testCases := []struct { + name string + subject ImageVerification + errors func(*ImageVerification) field.ErrorList + }{ + { + name: "mutateDigest set to true for audit failure action", + subject: ImageVerification{ + ImageReferences: []string{"*"}, + Attestations: []Attestation{ + { + PredicateType: "foo", + }, + }, + MutateDigest: true, + }, + errors: func(i *ImageVerification) field.ErrorList { + return field.ErrorList{ + field.Invalid( + path.Child("mutateDigest"), + i.MutateDigest, + "mutateDigest must be set to false for ‘Audit’ failure action"), + } + }, + }, + { + name: "mutateDigest set to false for audit failure action", + subject: ImageVerification{ + ImageReferences: []string{"*"}, + Attestations: []Attestation{ + { + PredicateType: "foo", + }, + }, + MutateDigest: false, + }, + }, + } + + isAuditFailureAction := true // indicates validateFailureAction set to Audit + for _, test := range testCases { + subject := test.subject.Convert() + errs := subject.Validate(isAuditFailureAction, path) + var expectedErrs field.ErrorList + if test.errors != nil { + expectedErrs = test.errors(subject) + } + + assert.Equal(t, len(errs), len(expectedErrs), fmt.Sprintf("test `%s` error count mismatch, errors %v", test.name, errs)) + if len(errs) != 0 { + assert.DeepEqual(t, errs, expectedErrs) + } + } +} + +func Test_Enforce_VerifyImageRule(t *testing.T) { + path := field.NewPath("dummy") + testCases := []struct { + name string + subject ImageVerification + errors func(*ImageVerification) field.ErrorList + }{ + { + name: "mutateDigest set to true for enforce failure action", + subject: ImageVerification{ + ImageReferences: []string{"*"}, + Attestations: []Attestation{ + { + PredicateType: "foo", + }, + }, + MutateDigest: true, + }, + }, + { + name: "mutateDigest set to false for enforce failure action", + subject: ImageVerification{ + ImageReferences: []string{"*"}, + Attestations: []Attestation{ + { + PredicateType: "foo", + }, + }, + MutateDigest: false, + }, + }, + } + + isAuditFailureAction := false // indicates validateFailureAction set to Enforce + for _, test := range testCases { + subject := test.subject.Convert() + errs := subject.Validate(isAuditFailureAction, path) var expectedErrs field.ErrorList if test.errors != nil { expectedErrs = test.errors(subject) diff --git a/api/kyverno/v1/image_verification_types.go b/api/kyverno/v1/image_verification_types.go index bdbab73cf2..4f281acdee 100644 --- a/api/kyverno/v1/image_verification_types.go +++ b/api/kyverno/v1/image_verification_types.go @@ -258,9 +258,13 @@ func (iv *ImageVerification) GetType() ImageVerificationType { } // Validate implements programmatic validation -func (iv *ImageVerification) Validate(path *field.Path) (errs field.ErrorList) { +func (iv *ImageVerification) Validate(isAuditFailureAction bool, path *field.Path) (errs field.ErrorList) { copy := iv.Convert() + if isAuditFailureAction && iv.MutateDigest { + errs = append(errs, field.Invalid(path.Child("mutateDigest"), iv.MutateDigest, "mutateDigest must be set to false for ‘Audit’ failure action")) + } + if len(copy.ImageReferences) == 0 { errs = append(errs, field.Invalid(path, iv, "An image reference is required")) } diff --git a/api/kyverno/v2beta1/image_verification_test.go b/api/kyverno/v2beta1/image_verification_test.go index e5054a1879..caa0c95f6b 100644 --- a/api/kyverno/v2beta1/image_verification_test.go +++ b/api/kyverno/v2beta1/image_verification_test.go @@ -120,9 +120,116 @@ func Test_ImageVerification(t *testing.T) { }, } + isAuditFailureAction := false for _, test := range testCases { subject := test.subject - errs := subject.Validate(path) + errs := subject.Validate(isAuditFailureAction, path) + var expectedErrs field.ErrorList + if test.errors != nil { + expectedErrs = test.errors(&subject) + } + + assert.Equal(t, len(errs), len(expectedErrs), fmt.Sprintf("test `%s` error count mismatch, errors %v", test.name, errs)) + if len(errs) != 0 { + assert.DeepEqual(t, errs, expectedErrs) + } + } +} + +func Test_Audit_VerifyImageRule(t *testing.T) { + path := field.NewPath("dummy") + testCases := []struct { + name string + subject ImageVerification + errors func(*ImageVerification) field.ErrorList + }{ + { + name: "mutateDigest set to true for audit failure action", + subject: ImageVerification{ + ImageReferences: []string{"*"}, + Attestations: []kyvernov1.Attestation{ + { + PredicateType: "foo", + }, + }, + MutateDigest: true, + }, + errors: func(i *ImageVerification) field.ErrorList { + return field.ErrorList{ + field.Invalid( + path.Child("mutateDigest"), + i.MutateDigest, + "mutateDigest must be set to false for ‘Audit’ failure action"), + } + }, + }, + { + name: "mutateDigest set to false for audit failure action", + subject: ImageVerification{ + ImageReferences: []string{"*"}, + Attestations: []kyvernov1.Attestation{ + { + PredicateType: "foo", + }, + }, + MutateDigest: false, + }, + }, + } + + isAuditFailureAction := true // indicates validateFailureAction set to Audit + for _, test := range testCases { + subject := test.subject + errs := subject.Validate(isAuditFailureAction, path) + var expectedErrs field.ErrorList + if test.errors != nil { + expectedErrs = test.errors(&subject) + } + + assert.Equal(t, len(errs), len(expectedErrs), fmt.Sprintf("test `%s` error count mismatch, errors %v", test.name, errs)) + if len(errs) != 0 { + assert.DeepEqual(t, errs, expectedErrs) + } + } +} + +func Test_Enforce_VerifyImageRule(t *testing.T) { + path := field.NewPath("dummy") + testCases := []struct { + name string + subject ImageVerification + errors func(*ImageVerification) field.ErrorList + }{ + { + name: "mutateDigest set to true for enforce failure action", + subject: ImageVerification{ + ImageReferences: []string{"*"}, + Attestations: []kyvernov1.Attestation{ + { + PredicateType: "foo", + }, + }, + MutateDigest: true, + }, + }, + { + name: "mutateDigest set to false for enforce failure action", + subject: ImageVerification{ + ImageReferences: []string{"*"}, + Attestations: []kyvernov1.Attestation{ + { + PredicateType: "foo", + }, + }, + MutateDigest: false, + }, + }, + } + + isAuditFailureAction := false // indicates validateFailureAction set to Enforce + for _, test := range testCases { + subject := test.subject + errs := subject.Validate(isAuditFailureAction, path) var expectedErrs field.ErrorList if test.errors != nil { expectedErrs = test.errors(&subject) diff --git a/api/kyverno/v2beta1/image_verification_types.go b/api/kyverno/v2beta1/image_verification_types.go index 62bb7f59ca..8485ccf15d 100644 --- a/api/kyverno/v2beta1/image_verification_types.go +++ b/api/kyverno/v2beta1/image_verification_types.go @@ -63,9 +63,13 @@ type ImageVerification struct { } // Validate implements programmatic validation -func (iv *ImageVerification) Validate(path *field.Path) (errs field.ErrorList) { +func (iv *ImageVerification) Validate(isAuditFailureAction bool, path *field.Path) (errs field.ErrorList) { copy := iv + if isAuditFailureAction && iv.MutateDigest { + errs = append(errs, field.Invalid(path.Child("mutateDigest"), iv.MutateDigest, "mutateDigest must be set to false for ‘Audit’ failure action")) + } + if len(copy.ImageReferences) == 0 { errs = append(errs, field.Invalid(path, iv, "An image reference is required")) } diff --git a/pkg/policy/validate.go b/pkg/policy/validate.go index 0aa236e857..b64a0e3c7f 100644 --- a/pkg/policy/validate.go +++ b/pkg/policy/validate.go @@ -337,9 +337,14 @@ func Validate(policy, oldPolicy kyvernov1.PolicyInterface, client dclient.Interf } if rule.HasVerifyImages() { + isAuditFailureAction := false + if spec.ValidationFailureAction == kyvernov1.Audit { + isAuditFailureAction = true + } + verifyImagePath := rulePath.Child("verifyImages") for index, i := range rule.VerifyImages { - errs = append(errs, i.Validate(verifyImagePath.Index(index))...) + errs = append(errs, i.Validate(isAuditFailureAction, verifyImagePath.Index(index))...) } if len(errs) != 0 { return warnings, errs.ToAggregate() diff --git a/test/conformance/kuttl/reports/background/verify-image-fail/policy.yaml b/test/conformance/kuttl/reports/background/verify-image-fail/policy.yaml index fd32eb4a0c..0d1ff4f3cf 100644 --- a/test/conformance/kuttl/reports/background/verify-image-fail/policy.yaml +++ b/test/conformance/kuttl/reports/background/verify-image-fail/policy.yaml @@ -17,6 +17,7 @@ spec: - imageReferences: - ghcr.io/kyverno/test-verify-image:* verifyDigest: false + mutateDigest: false required: false attestors: - entries: diff --git a/test/conformance/kuttl/reports/background/verify-image-pass/policy.yaml b/test/conformance/kuttl/reports/background/verify-image-pass/policy.yaml index fd32eb4a0c..0d1ff4f3cf 100644 --- a/test/conformance/kuttl/reports/background/verify-image-pass/policy.yaml +++ b/test/conformance/kuttl/reports/background/verify-image-pass/policy.yaml @@ -17,6 +17,7 @@ spec: - imageReferences: - ghcr.io/kyverno/test-verify-image:* verifyDigest: false + mutateDigest: false required: false attestors: - entries: