1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-28 18:38:40 +00:00

fix: verifyImages w/ multiple entries is not consistent (#8357)

* fix: verifyImages w/ multiple entries is not consistent

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* clean

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* unit tests

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

---------

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
This commit is contained in:
Charles-Edouard Brétéché 2023-09-12 17:20:21 +02:00 committed by GitHub
parent dbad967150
commit 44f0de5c53
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 276 additions and 261 deletions

View file

@ -67,10 +67,9 @@ type ApplyCommandConfig struct {
}
func Command() *cobra.Command {
var cmd *cobra.Command
var removeColor, detailedResults, table bool
applyCommandConfig := &ApplyCommandConfig{}
cmd = &cobra.Command{
cmd := &cobra.Command{
Use: "apply",
Short: command.FormatDescription(true, websiteUrl, false, description...),
Long: command.FormatDescription(false, websiteUrl, false, description...),
@ -298,7 +297,7 @@ func (c *ApplyCommandConfig) applyPolicytoResource(
if err != nil {
return &rc, resources, responses, sanitizederror.NewWithError(fmt.Errorf("failed to apply policies on resource %v", resource.GetName()).Error(), err)
}
responses = append(responses, processSkipEngineResponses(ers)...)
responses = append(responses, ers...)
}
return &rc, resources, responses, nil
}
@ -475,37 +474,3 @@ func exit(rc *processor.ResultCounts, warnExitCode int, warnNoPassed bool) error
}
return nil
}
func processSkipEngineResponses(responses []engineapi.EngineResponse) []engineapi.EngineResponse {
var processedEngineResponses []engineapi.EngineResponse
for _, response := range responses {
if !response.IsEmpty() {
pol := response.Policy()
if polType := pol.GetType(); polType == engineapi.ValidatingAdmissionPolicyType {
return processedEngineResponses
}
for _, rule := range autogen.ComputeRules(pol.GetPolicy().(kyvernov1.PolicyInterface)) {
if rule.HasValidate() || rule.HasVerifyImageChecks() || rule.HasVerifyImages() {
ruleFoundInEngineResponse := false
for _, valResponseRule := range response.PolicyResponse.Rules {
if rule.Name == valResponseRule.Name() {
ruleFoundInEngineResponse = true
}
}
if !ruleFoundInEngineResponse {
response.PolicyResponse.Rules = append(response.PolicyResponse.Rules,
*engineapi.RuleSkip(
rule.Name,
engineapi.Validation,
rule.Validation.Message,
),
)
}
}
}
}
processedEngineResponses = append(processedEngineResponses, response)
}
return processedEngineResponses
}

View file

@ -24,118 +24,126 @@ func Test_Apply(t *testing.T) {
assert.NilError(t, err)
defer func() { _ = os.Remove(localFileName) }()
testcases := []*TestCase{{
config: ApplyCommandConfig{
PolicyPaths: []string{"../../../../../test/best_practices/disallow_latest_tag.yaml"},
ResourcePaths: []string{"../../../../../test/resources/pod_with_version_tag.yaml"},
PolicyReport: true,
},
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
Summary: policyreportv1alpha2.PolicyReportSummary{
Pass: 2,
Fail: 0,
Skip: 0,
Error: 0,
Warn: 0,
testcases := []*TestCase{
{
config: ApplyCommandConfig{
PolicyPaths: []string{"../../../../../test/best_practices/disallow_latest_tag.yaml"},
ResourcePaths: []string{"../../../../../test/resources/pod_with_version_tag.yaml"},
PolicyReport: true,
},
}},
}, {
config: ApplyCommandConfig{
PolicyPaths: []string{localFileName},
ResourcePaths: []string{"../../../../../test/resources/pod_with_version_tag.yaml"},
PolicyReport: true,
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
Summary: policyreportv1alpha2.PolicyReportSummary{
Pass: 2,
Fail: 0,
Skip: 0,
Error: 0,
Warn: 0,
},
}},
},
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
Summary: policyreportv1alpha2.PolicyReportSummary{
Pass: 2,
Fail: 0,
Skip: 0,
Error: 0,
Warn: 0,
{
config: ApplyCommandConfig{
PolicyPaths: []string{localFileName},
ResourcePaths: []string{"../../../../../test/resources/pod_with_version_tag.yaml"},
PolicyReport: true,
},
}},
}, {
config: ApplyCommandConfig{
PolicyPaths: []string{"../../../../../test/best_practices/disallow_latest_tag.yaml"},
ResourcePaths: []string{"../../../../../test/resources/pod_with_latest_tag.yaml"},
PolicyReport: true,
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
Summary: policyreportv1alpha2.PolicyReportSummary{
Pass: 2,
Fail: 0,
Skip: 0,
Error: 0,
Warn: 0,
},
}},
},
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
Summary: policyreportv1alpha2.PolicyReportSummary{
Pass: 1,
Fail: 1,
Skip: 0,
Error: 0,
Warn: 0,
{
config: ApplyCommandConfig{
PolicyPaths: []string{"../../../../../test/best_practices/disallow_latest_tag.yaml"},
ResourcePaths: []string{"../../../../../test/resources/pod_with_latest_tag.yaml"},
PolicyReport: true,
},
}},
}, {
config: ApplyCommandConfig{
PolicyPaths: []string{"../../../../../test/cli/apply/policies"},
ResourcePaths: []string{"../../../../../test/cli/apply/resource"},
PolicyReport: true,
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
Summary: policyreportv1alpha2.PolicyReportSummary{
Pass: 1,
Fail: 1,
Skip: 0,
Error: 0,
Warn: 0,
},
}},
},
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
Summary: policyreportv1alpha2.PolicyReportSummary{
Pass: 1,
Fail: 1,
Skip: 8,
Error: 0,
Warn: 2,
{
config: ApplyCommandConfig{
PolicyPaths: []string{"../../../../../test/cli/apply/policies"},
ResourcePaths: []string{"../../../../../test/cli/apply/resource"},
PolicyReport: true,
},
}},
}, {
config: ApplyCommandConfig{
PolicyPaths: []string{"../../../../../test/best_practices/disallow_latest_tag.yaml"},
ResourcePaths: []string{"../../../../../test/resources/pod_with_latest_tag.yaml"},
PolicyReport: true,
AuditWarn: true,
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
Summary: policyreportv1alpha2.PolicyReportSummary{
Pass: 1,
Fail: 1,
Skip: 0,
Error: 0,
Warn: 2,
},
}},
},
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
Summary: policyreportv1alpha2.PolicyReportSummary{
Pass: 1,
Fail: 0,
Skip: 0,
Error: 0,
Warn: 1,
{
config: ApplyCommandConfig{
PolicyPaths: []string{"../../../../../test/best_practices/disallow_latest_tag.yaml"},
ResourcePaths: []string{"../../../../../test/resources/pod_with_latest_tag.yaml"},
PolicyReport: true,
AuditWarn: true,
},
}},
}, {
config: ApplyCommandConfig{
PolicyPaths: []string{"-"},
ResourcePaths: []string{"../../../../../test/resources/pod_with_latest_tag.yaml"},
PolicyReport: true,
AuditWarn: true,
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
Summary: policyreportv1alpha2.PolicyReportSummary{
Pass: 1,
Fail: 0,
Skip: 0,
Error: 0,
Warn: 1,
},
}},
},
stdinFile: "../../../../../test/best_practices/disallow_latest_tag.yaml",
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
Summary: policyreportv1alpha2.PolicyReportSummary{
Pass: 1,
Fail: 0,
Skip: 0,
Error: 0,
Warn: 1,
{
config: ApplyCommandConfig{
PolicyPaths: []string{"-"},
ResourcePaths: []string{"../../../../../test/resources/pod_with_latest_tag.yaml"},
PolicyReport: true,
AuditWarn: true,
},
}},
}, {
config: ApplyCommandConfig{
PolicyPaths: []string{"../../../../../test/best_practices/disallow_latest_tag.yaml"},
ResourcePaths: []string{"-"},
PolicyReport: true,
AuditWarn: true,
stdinFile: "../../../../../test/best_practices/disallow_latest_tag.yaml",
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
Summary: policyreportv1alpha2.PolicyReportSummary{
Pass: 1,
Fail: 0,
Skip: 0,
Error: 0,
Warn: 1,
},
}},
},
stdinFile: "../../../../../test/resources/pod_with_latest_tag.yaml",
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
Summary: policyreportv1alpha2.PolicyReportSummary{
Pass: 1,
Fail: 0,
Skip: 0,
Error: 0,
Warn: 1,
{
config: ApplyCommandConfig{
PolicyPaths: []string{"../../../../../test/best_practices/disallow_latest_tag.yaml"},
ResourcePaths: []string{"-"},
PolicyReport: true,
AuditWarn: true,
},
}},
}, {
// TODO
stdinFile: "../../../../../test/resources/pod_with_latest_tag.yaml",
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
Summary: policyreportv1alpha2.PolicyReportSummary{
Pass: 1,
Fail: 0,
Skip: 0,
Error: 0,
Warn: 1,
},
}},
},
// {
// // TODO
// config: ApplyCommandConfig{
// PolicyPaths: []string{"https://github.com/kyverno/policies/openshift/team-validate-ns-name/"},
// ResourcePaths: []string{"../../../../../test/openshift/team-validate-ns-name.yaml"},
@ -151,146 +159,156 @@ func Test_Apply(t *testing.T) {
// Warn: 0,
// },
// }},
// }, {
config: ApplyCommandConfig{
PolicyPaths: []string{"../../../../../test/cli/apply/policies-set"},
ResourcePaths: []string{"../../../../../test/cli/apply/resources-set"},
Variables: []string{"request.operation=UPDATE"},
PolicyReport: true,
},
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
Summary: policyreportv1alpha2.PolicyReportSummary{
Pass: 2,
Fail: 0,
Skip: 4,
Error: 0,
Warn: 0,
// },
{
config: ApplyCommandConfig{
PolicyPaths: []string{"../../../../../test/cli/apply/policies-set"},
ResourcePaths: []string{"../../../../../test/cli/apply/resources-set"},
Variables: []string{"request.operation=UPDATE"},
PolicyReport: true,
},
}},
}, {
config: ApplyCommandConfig{
PolicyPaths: []string{"../../../../../test/cli/test-validating-admission-policy/check-deployments-replica/policy.yaml"},
ResourcePaths: []string{"../../../../../test/cli/test-validating-admission-policy/check-deployments-replica/deployment1.yaml"},
PolicyReport: true,
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
Summary: policyreportv1alpha2.PolicyReportSummary{
Pass: 2,
Fail: 0,
Skip: 0,
Error: 0,
Warn: 0,
},
}},
},
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
Summary: policyreportv1alpha2.PolicyReportSummary{
Pass: 1,
Fail: 0,
Skip: 0,
Error: 0,
Warn: 0,
{
config: ApplyCommandConfig{
PolicyPaths: []string{"../../../../../test/cli/test-validating-admission-policy/check-deployments-replica/policy.yaml"},
ResourcePaths: []string{"../../../../../test/cli/test-validating-admission-policy/check-deployments-replica/deployment1.yaml"},
PolicyReport: true,
},
}},
}, {
config: ApplyCommandConfig{
PolicyPaths: []string{"../../../../../test/cli/test-validating-admission-policy/check-deployments-replica/policy.yaml"},
ResourcePaths: []string{"../../../../../test/cli/test-validating-admission-policy/check-deployments-replica/deployment2.yaml"},
PolicyReport: true,
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
Summary: policyreportv1alpha2.PolicyReportSummary{
Pass: 1,
Fail: 0,
Skip: 0,
Error: 0,
Warn: 0,
},
}},
},
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
Summary: policyreportv1alpha2.PolicyReportSummary{
Pass: 0,
Fail: 1,
Skip: 0,
Error: 0,
Warn: 0,
{
config: ApplyCommandConfig{
PolicyPaths: []string{"../../../../../test/cli/test-validating-admission-policy/check-deployments-replica/policy.yaml"},
ResourcePaths: []string{"../../../../../test/cli/test-validating-admission-policy/check-deployments-replica/deployment2.yaml"},
PolicyReport: true,
},
}},
}, {
config: ApplyCommandConfig{
PolicyPaths: []string{"../../../../../test/cli/test-validating-admission-policy/disallow-host-path/policy.yaml"},
ResourcePaths: []string{"../../../../../test/cli/test-validating-admission-policy/disallow-host-path/pod1.yaml"},
PolicyReport: true,
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
Summary: policyreportv1alpha2.PolicyReportSummary{
Pass: 0,
Fail: 1,
Skip: 0,
Error: 0,
Warn: 0,
},
}},
},
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
Summary: policyreportv1alpha2.PolicyReportSummary{
Pass: 1,
Fail: 0,
Skip: 0,
Error: 0,
Warn: 0,
{
config: ApplyCommandConfig{
PolicyPaths: []string{"../../../../../test/cli/test-validating-admission-policy/disallow-host-path/policy.yaml"},
ResourcePaths: []string{"../../../../../test/cli/test-validating-admission-policy/disallow-host-path/pod1.yaml"},
PolicyReport: true,
},
}},
}, {
config: ApplyCommandConfig{
PolicyPaths: []string{"../../../../../test/cli/test-validating-admission-policy/disallow-host-path/policy.yaml"},
ResourcePaths: []string{"../../../../../test/cli/test-validating-admission-policy/disallow-host-path/pod2.yaml"},
PolicyReport: true,
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
Summary: policyreportv1alpha2.PolicyReportSummary{
Pass: 1,
Fail: 0,
Skip: 0,
Error: 0,
Warn: 0,
},
}},
},
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
Summary: policyreportv1alpha2.PolicyReportSummary{
Pass: 0,
Fail: 1,
Skip: 0,
Error: 0,
Warn: 0,
{
config: ApplyCommandConfig{
PolicyPaths: []string{"../../../../../test/cli/test-validating-admission-policy/disallow-host-path/policy.yaml"},
ResourcePaths: []string{"../../../../../test/cli/test-validating-admission-policy/disallow-host-path/pod2.yaml"},
PolicyReport: true,
},
}},
}, {
config: ApplyCommandConfig{
PolicyPaths: []string{"../../../../../test/cli/test-validating-admission-policy/check-deployment-labels/policy.yaml"},
ResourcePaths: []string{"../../../../../test/cli/test-validating-admission-policy/check-deployment-labels/deployment1.yaml"},
PolicyReport: true,
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
Summary: policyreportv1alpha2.PolicyReportSummary{
Pass: 0,
Fail: 1,
Skip: 0,
Error: 0,
Warn: 0,
},
}},
},
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
Summary: policyreportv1alpha2.PolicyReportSummary{
Pass: 1,
Fail: 0,
Skip: 0,
Error: 0,
Warn: 0,
{
config: ApplyCommandConfig{
PolicyPaths: []string{"../../../../../test/cli/test-validating-admission-policy/check-deployment-labels/policy.yaml"},
ResourcePaths: []string{"../../../../../test/cli/test-validating-admission-policy/check-deployment-labels/deployment1.yaml"},
PolicyReport: true,
},
}},
}, {
config: ApplyCommandConfig{
PolicyPaths: []string{"../../../../../test/cli/test-validating-admission-policy/check-deployment-labels/policy.yaml"},
ResourcePaths: []string{"../../../../../test/cli/test-validating-admission-policy/check-deployment-labels/deployment2.yaml"},
PolicyReport: true,
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
Summary: policyreportv1alpha2.PolicyReportSummary{
Pass: 1,
Fail: 0,
Skip: 0,
Error: 0,
Warn: 0,
},
}},
},
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
Summary: policyreportv1alpha2.PolicyReportSummary{
Pass: 0,
Fail: 1,
Skip: 0,
Error: 0,
Warn: 0,
{
config: ApplyCommandConfig{
PolicyPaths: []string{"../../../../../test/cli/test-validating-admission-policy/check-deployment-labels/policy.yaml"},
ResourcePaths: []string{"../../../../../test/cli/test-validating-admission-policy/check-deployment-labels/deployment2.yaml"},
PolicyReport: true,
},
}},
}, {
config: ApplyCommandConfig{
PolicyPaths: []string{"https://github.com/kyverno/policies/best-practices/require-labels/", "../../../../../test/best_practices/disallow_latest_tag.yaml"},
ResourcePaths: []string{"../../../../../test/resources/pod_with_version_tag.yaml"},
GitBranch: "main",
PolicyReport: true,
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
Summary: policyreportv1alpha2.PolicyReportSummary{
Pass: 0,
Fail: 1,
Skip: 0,
Error: 0,
Warn: 0,
},
}},
},
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
Summary: policyreportv1alpha2.PolicyReportSummary{
Pass: 2,
Fail: 1,
Skip: 2,
Error: 0,
Warn: 0,
{
config: ApplyCommandConfig{
PolicyPaths: []string{"https://github.com/kyverno/policies/best-practices/require-labels/", "../../../../../test/best_practices/disallow_latest_tag.yaml"},
ResourcePaths: []string{"../../../../../test/resources/pod_with_version_tag.yaml"},
GitBranch: "main",
PolicyReport: true,
},
}},
}, {
// Same as the above test case but the policy paths are reordered
config: ApplyCommandConfig{
PolicyPaths: []string{"../../../../../test/best_practices/disallow_latest_tag.yaml", "https://github.com/kyverno/policies/best-practices/require-labels/"},
ResourcePaths: []string{"../../../../../test/resources/pod_with_version_tag.yaml"},
GitBranch: "main",
PolicyReport: true,
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
Summary: policyreportv1alpha2.PolicyReportSummary{
Pass: 2,
Fail: 1,
Skip: 0,
Error: 0,
Warn: 0,
},
}},
},
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
Summary: policyreportv1alpha2.PolicyReportSummary{
Pass: 2,
Fail: 1,
Skip: 2,
Error: 0,
Warn: 0,
{
// Same as the above test case but the policy paths are reordered
config: ApplyCommandConfig{
PolicyPaths: []string{"../../../../../test/best_practices/disallow_latest_tag.yaml", "https://github.com/kyverno/policies/best-practices/require-labels/"},
ResourcePaths: []string{"../../../../../test/resources/pod_with_version_tag.yaml"},
GitBranch: "main",
PolicyReport: true,
},
}},
}}
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
Summary: policyreportv1alpha2.PolicyReportSummary{
Pass: 2,
Fail: 1,
Skip: 0,
Error: 0,
Warn: 0,
},
}},
},
}
compareSummary := func(expected policyreportv1alpha2.PolicyReportSummary, actual policyreportv1alpha2.PolicyReportSummary, desc string) {
assert.Equal(t, actual.Pass, expected.Pass, desc)

View file

@ -7,6 +7,7 @@ import (
"path/filepath"
"strings"
json_patch "github.com/evanphx/json-patch/v5"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1"
valuesapi "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/apis/values"
@ -22,9 +23,12 @@ import (
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
"github.com/kyverno/kyverno/pkg/engine/factories"
"github.com/kyverno/kyverno/pkg/engine/jmespath"
"github.com/kyverno/kyverno/pkg/engine/mutate/patch"
"github.com/kyverno/kyverno/pkg/engine/policycontext"
"github.com/kyverno/kyverno/pkg/imageverifycache"
"github.com/kyverno/kyverno/pkg/registryclient"
jsonutils "github.com/kyverno/kyverno/pkg/utils/json"
"gomodules.xyz/jsonpatch/v2"
yamlv2 "gopkg.in/yaml.v2"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
@ -113,8 +117,36 @@ func (p *PolicyProcessor) ApplyPoliciesOnResource() ([]engineapi.EngineResponse,
if err != nil {
return responses, err
}
// TODO annotation
verifyImageResponse, _ := eng.VerifyAndPatchImages(context.TODO(), policyContext)
verifyImageResponse, verifiedImageData := eng.VerifyAndPatchImages(context.TODO(), policyContext)
// update annotation to reflect verified images
var patches []jsonpatch.JsonPatchOperation
if !verifiedImageData.IsEmpty() {
annotationPatches, err := verifiedImageData.Patches(len(verifyImageResponse.PatchedResource.GetAnnotations()) != 0, log.Log)
if err != nil {
return responses, err
}
// add annotation patches first
patches = append(annotationPatches, patches...)
}
if len(patches) != 0 {
patch := jsonutils.JoinPatches(patch.ConvertPatches(patches...)...)
decoded, err := json_patch.DecodePatch(patch)
if err != nil {
return responses, err
}
options := &json_patch.ApplyOptions{SupportNegativeIndices: true, AllowMissingPathOnRemove: true, EnsurePathExistsOnAdd: true}
resourceBytes, err := verifyImageResponse.PatchedResource.MarshalJSON()
if err != nil {
return responses, err
}
patchedResourceBytes, err := decoded.ApplyWithOptions(resourceBytes, options)
if err != nil {
return responses, err
}
if err := verifyImageResponse.PatchedResource.UnmarshalJSON(patchedResourceBytes); err != nil {
return responses, err
}
}
responses = append(responses, verifyImageResponse)
resource = verifyImageResponse.PatchedResource
}