package validatingadmissionpolicy import ( "encoding/json" "testing" kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" yamlutils "github.com/kyverno/kyverno/pkg/utils/yaml" "gotest.tools/assert" ) func Test_Check_Resources(t *testing.T) { testCases := []struct { name string resource []byte expected bool }{ { name: "resource-with-namespaces", resource: []byte(` { "kinds": [ "Service" ], "namespaces": [ "prod" ], "operations": [ "CREATE" ] } `), expected: true, }, { name: "namespaces-with-wildcards", resource: []byte(` { "kinds": [ "Service" ], "namespaces": [ "prod-*" ], "operations": [ "CREATE" ] } `), expected: false, }, { name: "resource-names-with-wildcards", resource: []byte(` { "kinds": [ "Service" ], "names": [ "svc-*" ], "operations": [ "CREATE" ] } `), expected: false, }, { name: "resource-with-annotations", resource: []byte(` { "annotations": { "imageregistry": "https://hub.docker.com/" }, "kinds": [ "Pod" ], "operations": [ "CREATE", "UPDATE" ] } `), expected: false, }, { name: "resource-with-object-selector", resource: []byte(` { "kinds": [ "Pod" ], "operations": [ "CREATE", "UPDATE" ], "selector": { "matchLabels": { "app": "critical" } } } `), expected: true, }, { name: "resource-with-namespace-selector", resource: []byte(` { "kinds": [ "Pod" ], "operations": [ "CREATE", "UPDATE" ], "namespaceSelector": { "matchLabels": { "app": "critical" } } } `), expected: true, }, } for _, test := range testCases { t.Run(test.name, func(t *testing.T) { var res kyvernov1.ResourceDescription err := json.Unmarshal(test.resource, &res) assert.NilError(t, err) out, _ := checkResources(res) assert.Equal(t, out, test.expected) }) } } func Test_Can_Generate_ValidatingAdmissionPolicy(t *testing.T) { testCases := []struct { name string policy []byte expected bool }{ { name: "policy-with-two-rules", policy: []byte(` { "apiVersion": "kyverno.io/v1", "kind": "ClusterPolicy", "metadata": { "name": "disallow-latest-tag" }, "spec": { "rules": [ { "name": "require-image-tag", "match": { "any": [ { "resources": { "kinds": [ "Pod" ] } } ] }, "validate": { "cel": { "expressions": [ { "expression": "object.spec.containers.all(container, !container.image.matches('^[a-zA-Z]+:[0-9]*$'))" } ] } } }, { "name": "validate-image-tag", "match": { "any": [ { "resources": { "kinds": [ "Pod" ] } } ] }, "validate": { "cel": { "expressions": [ { "expression": "object.spec.containers.all(container, !container.image.contains('latest'))" } ] } } } ] } } `), expected: false, }, { name: "policy-with-mutate-rule", policy: []byte(` { "apiVersion": "kyverno.io/v1", "kind": "ClusterPolicy", "metadata": { "name": "set-image-pull-policy" }, "spec": { "rules": [ { "name": "set-image-pull-policy", "match": { "any": [ { "resources": { "kinds": [ "Pod" ] } } ] }, "mutate": { "patchStrategicMerge": { "spec": { "containers": [ { "(image)": "*:latest", "imagePullPolicy": "IfNotPresent" } ] } } } } ] } } `), expected: false, }, { name: "policy-with-non-CEL-validate-rule", policy: []byte(` { "apiVersion": "kyverno.io/v1", "kind": "ClusterPolicy", "metadata": { "name": "require-ns-purpose-label" }, "spec": { "validationFailureAction": "Enforce", "rules": [ { "name": "require-ns-purpose-label", "match": { "any": [ { "resources": { "kinds": [ "Namespace" ] } } ] }, "validate": { "pattern": { "metadata": { "labels": { "purpose": "production" } } } } } ] } } `), expected: false, }, { name: "policy-with-multiple-validationFailureActionOverrides", policy: []byte(` { "apiVersion": "kyverno.io/v1", "kind": "ClusterPolicy", "metadata": { "name": "disallow-host-path" }, "spec": { "validationFailureAction": "Enforce", "validationFailureActionOverrides": [ { "action": "Enforce", "namespaces": [ "default" ] }, { "action": "Audit", "namespaces": [ "test" ] } ], "rules": [ { "name": "host-path", "match": { "any": [ { "resources": { "kinds": [ "Pod" ] } } ] }, "validate": { "cel": { "expressions": [ { "expression": "!has(object.spec.volumes) || object.spec.volumes.all(volume, !has(volume.hostPath))" } ] } } } ] } } `), expected: false, }, { name: "policy-with-namespace-in-validationFailureActionOverrides", policy: []byte(` { "apiVersion": "kyverno.io/v1", "kind": "ClusterPolicy", "metadata": { "name": "disallow-host-path" }, "spec": { "validationFailureAction": "Enforce", "validationFailureActionOverrides": [ { "action": "Enforce", "namespaces": [ "test-ns" ] } ], "rules": [ { "name": "host-path", "match": { "any": [ { "resources": { "kinds": [ "Pod" ] } } ] }, "validate": { "cel": { "expressions": [ { "expression": "!has(object.spec.volumes) || object.spec.volumes.all(volume, !has(volume.hostPath))" } ] } } } ] } } `), expected: false, }, { name: "policy-with-subjects-and-clusterroles", policy: []byte(` { "apiVersion": "kyverno.io/v1", "kind": "ClusterPolicy", "metadata": { "name": "disallow-host-path" }, "spec": { "validationFailureAction": "Enforce", "rules": [ { "name": "host-path", "match": { "any": [ { "resources": { "kinds": [ "Deployment" ], "operations": [ "CREATE", "UPDATE" ] }, "subjects": [ { "kind": "User", "name": "mary@somecorp.com" } ], "clusterRoles": [ "cluster-admin" ] } ] }, "validate": { "cel": { "expressions": [ { "expression": "!has(object.spec.volumes) || object.spec.volumes.all(volume, !has(volume.hostPath))" } ] } } } ] } } `), expected: false, }, { name: "policy-with-object-selector", policy: []byte(` { "apiVersion": "kyverno.io/v1", "kind": "ClusterPolicy", "metadata": { "name": "disallow-host-path" }, "spec": { "validationFailureAction": "Enforce", "rules": [ { "name": "host-path", "match": { "any": [ { "resources": { "kinds": [ "Deployment" ], "operations": [ "CREATE", "UPDATE" ], "selector": { "matchLabels": { "app": "mongodb" }, "matchExpressions": [ { "key": "tier", "operator": "In", "values": [ "database" ] } ] } } } ] }, "validate": { "cel": { "expressions": [ { "expression": "!has(object.spec.volumes) || object.spec.volumes.all(volume, !has(volume.hostPath))" } ] } } } ] } } `), expected: true, }, } for _, test := range testCases { t.Run(test.name, func(t *testing.T) { policies, _, _, err := yamlutils.GetPolicy([]byte(test.policy)) assert.NilError(t, err) assert.Equal(t, 1, len(policies)) out, _ := CanGenerateVAP(policies[0].GetSpec()) assert.Equal(t, out, test.expected) }) } }