diff --git a/pkg/engine/anchor/error.go b/pkg/engine/anchor/error.go index 732a2d1400..801c40a937 100644 --- a/pkg/engine/anchor/error.go +++ b/pkg/engine/anchor/error.go @@ -83,7 +83,7 @@ func IsConditionalAnchorError(err error) bool { return isError(err, conditionalAnchorErr, conditionalAnchorErrMsg) } -// IsGlobalAnchorError checks if error is a global global anchor error +// IsGlobalAnchorError checks if error is a global anchor error func IsGlobalAnchorError(err error) bool { return isError(err, globalAnchorErr, globalAnchorErrMsg) } diff --git a/pkg/engine/anchor/handlers.go b/pkg/engine/anchor/handlers.go index fa9cfe4a56..150338d5c2 100644 --- a/pkg/engine/anchor/handlers.go +++ b/pkg/engine/anchor/handlers.go @@ -92,7 +92,7 @@ func newEqualityHandler(anchor Anchor, pattern interface{}, path string) Validat } } -// Handle processed condition anchor +// Handle processed equality anchor func (eh equalityHandler) Handle(handler resourceElementHandler, resourceMap map[string]interface{}, originPattern interface{}, ac *AnchorMap) (string, error) { anchorKey := eh.anchor.Key() currentPath := eh.path + anchorKey + "/" diff --git a/pkg/engine/validate/validate.go b/pkg/engine/validate/validate.go index 58cb965f2d..5768de5934 100644 --- a/pkg/engine/validate/validate.go +++ b/pkg/engine/validate/validate.go @@ -129,6 +129,8 @@ func validateMap(log logr.Logger, resourceMap, patternMap map[string]interface{} sort.Strings(keys) // Evaluate anchors + var skipErrors []error + var applyCount int for _, key := range keys { patternElement := anchors[key] // get handler for each pattern in the pattern @@ -137,12 +139,24 @@ func validateMap(log logr.Logger, resourceMap, patternMap map[string]interface{} // - Equality handler := anchor.CreateElementHandler(key, patternElement, path) handlerPath, err := handler.Handle(validateResourceElement, resourceMap, origPattern, ac) - // if there are resource values at same level, then anchor acts as conditional instead of a strict check - // but if there are none then it's an if-then check if err != nil { - // If global anchor fails then we don't process the resource + if skip(err) { + skipErrors = append(skipErrors, err) + continue + } + return handlerPath, err } + + applyCount++ + } + + if applyCount == 0 && len(skipErrors) > 0 { + return path, &PatternError{ + Err: multierr.Combine(skipErrors...), + Path: path, + Skip: true, + } } // Evaluate resources diff --git a/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-fail/README.md b/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-fail/README.md new file mode 100644 index 0000000000..4cae30b2d4 --- /dev/null +++ b/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-fail/README.md @@ -0,0 +1,11 @@ +## Description + +This test ensures that when the policy uses a pattern with conditional anchor, the creation of some resources should be failed instead of skipped. + +## Expected Behavior + +Resource failed to be created due to validate failure. + +## Reference Issue(s) + +https://github.com/kyverno/kyverno/issues/8731 \ No newline at end of file diff --git a/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-fail/chainsaw-test.yaml b/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-fail/chainsaw-test.yaml new file mode 100644 index 0000000000..c96cded9ad --- /dev/null +++ b/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-fail/chainsaw-test.yaml @@ -0,0 +1,24 @@ +apiVersion: chainsaw.kyverno.io/v1alpha1 +kind: Test +metadata: + creationTimestamp: null + name: validate-pattern-should-fail +spec: + steps: + - name: step-01 + try: + - apply: + file: policy.yaml + - assert: + file: policy-assert.yaml + - name: step-02 + try: + - apply: + expect: + - check: + ($error != null): true + file: resource.yaml + - name: step-03 + try: + - assert: + file: event-assert.yaml \ No newline at end of file diff --git a/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-fail/event-assert.yaml b/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-fail/event-assert.yaml new file mode 100644 index 0000000000..cf2d7e7e99 --- /dev/null +++ b/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-fail/event-assert.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Event +metadata: + namespace: default +involvedObject: + apiVersion: kyverno.io/v1 + kind: ClusterPolicy + name: priv +reason: PolicyViolation +reportingComponent: kyverno-admission \ No newline at end of file diff --git a/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-fail/policy-assert.yaml b/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-fail/policy-assert.yaml new file mode 100644 index 0000000000..a695a5250c --- /dev/null +++ b/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-fail/policy-assert.yaml @@ -0,0 +1,10 @@ +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: priv +spec: {} +status: + conditions: + - reason: Succeeded + status: "True" + type: Ready \ No newline at end of file diff --git a/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-fail/policy.yaml b/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-fail/policy.yaml new file mode 100644 index 0000000000..62582deadc --- /dev/null +++ b/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-fail/policy.yaml @@ -0,0 +1,31 @@ +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: priv +spec: + validationFailureAction: Enforce + background: true + rules: + - name: priv-esc + match: + any: + - resources: + kinds: + - Pod + validate: + message: >- + Lorem ipse + pattern: + spec: + =(ephemeralContainers): + - (image): "!*/foo:*.*.*" + securityContext: + allowPrivilegeEscalation: "false" + =(initContainers): + - (image): "!*/foo:*.*.*" + securityContext: + allowPrivilegeEscalation: "false" + =(containers): + - (image): "!*/foo:*.*.*" + securityContext: + allowPrivilegeEscalation: "false" diff --git a/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-fail/resource.yaml b/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-fail/resource.yaml new file mode 100644 index 0000000000..2817a65766 --- /dev/null +++ b/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-fail/resource.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: test-pod + labels: + app: test-app +spec: + containers: + - name: side + image: test/foo:1.2.3 + initContainers: + - name: init + image: test/bar:1.2.3 + securityContext: + allowPrivilegeEscalation: true \ No newline at end of file diff --git a/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-pass/README.md b/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-pass/README.md new file mode 100644 index 0000000000..29bdd37655 --- /dev/null +++ b/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-pass/README.md @@ -0,0 +1,11 @@ +## Description + +This test ensures that when the policy uses a pattern with conditional anchor, the creation of some resources should be passes instead of skipped. + +## Expected Behavior + +The creation of resource should be passes instead of skipped. + +## Reference Issue(s) + +https://github.com/kyverno/kyverno/issues/8731 \ No newline at end of file diff --git a/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-pass/chainsaw-test.yaml b/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-pass/chainsaw-test.yaml new file mode 100644 index 0000000000..80aeadec71 --- /dev/null +++ b/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-pass/chainsaw-test.yaml @@ -0,0 +1,25 @@ +apiVersion: chainsaw.kyverno.io/v1alpha1 +kind: Test +metadata: + creationTimestamp: null + name: validate-pattern-should-pass +spec: + steps: + - name: step-01 + try: + - apply: + file: policy.yaml + - assert: + file: policy-assert.yaml + - name: step-02 + try: + - apply: + file: resource.yaml + - name: step-03 + try: + - assert: + file: event-assert.yaml + - name: step-04 + try: + - assert: + file: report-pass-assert.yaml \ No newline at end of file diff --git a/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-pass/event-assert.yaml b/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-pass/event-assert.yaml new file mode 100644 index 0000000000..8860cfcb62 --- /dev/null +++ b/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-pass/event-assert.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Event +metadata: + namespace: default +involvedObject: + apiVersion: kyverno.io/v1 + kind: ClusterPolicy + name: priv +type: Normal +reason: PolicyApplied +action: Resource Passed +reportingComponent: kyverno-admission \ No newline at end of file diff --git a/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-pass/policy-assert.yaml b/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-pass/policy-assert.yaml new file mode 100644 index 0000000000..a695a5250c --- /dev/null +++ b/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-pass/policy-assert.yaml @@ -0,0 +1,10 @@ +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: priv +spec: {} +status: + conditions: + - reason: Succeeded + status: "True" + type: Ready \ No newline at end of file diff --git a/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-pass/policy.yaml b/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-pass/policy.yaml new file mode 100644 index 0000000000..62582deadc --- /dev/null +++ b/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-pass/policy.yaml @@ -0,0 +1,31 @@ +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: priv +spec: + validationFailureAction: Enforce + background: true + rules: + - name: priv-esc + match: + any: + - resources: + kinds: + - Pod + validate: + message: >- + Lorem ipse + pattern: + spec: + =(ephemeralContainers): + - (image): "!*/foo:*.*.*" + securityContext: + allowPrivilegeEscalation: "false" + =(initContainers): + - (image): "!*/foo:*.*.*" + securityContext: + allowPrivilegeEscalation: "false" + =(containers): + - (image): "!*/foo:*.*.*" + securityContext: + allowPrivilegeEscalation: "false" diff --git a/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-pass/report-pass-assert.yaml b/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-pass/report-pass-assert.yaml new file mode 100644 index 0000000000..cc33a5566e --- /dev/null +++ b/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-pass/report-pass-assert.yaml @@ -0,0 +1,23 @@ +apiVersion: wgpolicyk8s.io/v1alpha2 +kind: PolicyReport +metadata: + ownerReferences: + - apiVersion: v1 + kind: Pod + name: test-pod +scope: + apiVersion: v1 + kind: Pod + name: test-pod +results: +- message: validation rule 'priv-esc' passed. + policy: priv + result: pass + rule: priv-esc + source: kyverno +summary: + error: 0 + fail: 0 + pass: 1 + skip: 0 + warn: 0 \ No newline at end of file diff --git a/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-pass/resource.yaml b/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-pass/resource.yaml new file mode 100644 index 0000000000..91381030a4 --- /dev/null +++ b/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-pass/resource.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Pod +metadata: + name: test-pod + labels: + app: test-app +spec: + containers: + - name: main + image: test/bar:1.2.3 + securityContext: + allowPrivilegeEscalation: false + - name: side + image: test/foo:1.2.3 + initContainers: + - name: init + image: test/foo:1.2.3 + securityContext: + allowPrivilegeEscalation: true \ No newline at end of file diff --git a/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-skip/README.md b/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-skip/README.md new file mode 100644 index 0000000000..3746ec186b --- /dev/null +++ b/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-skip/README.md @@ -0,0 +1,11 @@ +## Description + +This test ensures that when the policy uses a pattern with conditional anchor, the creation of some resources should be skipped. + +## Expected Behavior + +The creation of resource should be skipped. + +## Reference Issue(s) + +https://github.com/kyverno/kyverno/issues/8731 \ No newline at end of file diff --git a/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-skip/chainsaw-test.yaml b/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-skip/chainsaw-test.yaml new file mode 100644 index 0000000000..35300d9953 --- /dev/null +++ b/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-skip/chainsaw-test.yaml @@ -0,0 +1,21 @@ +apiVersion: chainsaw.kyverno.io/v1alpha1 +kind: Test +metadata: + creationTimestamp: null + name: validate-pattern-should-skip +spec: + steps: + - name: step-01 + try: + - apply: + file: policy.yaml + - assert: + file: policy-assert.yaml + - name: step-02 + try: + - apply: + file: resource.yaml + - name: step-03 + try: + - assert: + file: report-skip-assert.yaml \ No newline at end of file diff --git a/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-skip/policy-assert.yaml b/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-skip/policy-assert.yaml new file mode 100644 index 0000000000..a695a5250c --- /dev/null +++ b/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-skip/policy-assert.yaml @@ -0,0 +1,10 @@ +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: priv +spec: {} +status: + conditions: + - reason: Succeeded + status: "True" + type: Ready \ No newline at end of file diff --git a/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-skip/policy.yaml b/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-skip/policy.yaml new file mode 100644 index 0000000000..8d58e00bdc --- /dev/null +++ b/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-skip/policy.yaml @@ -0,0 +1,27 @@ +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: priv +spec: + validationFailureAction: Enforce + background: true + rules: + - name: priv-esc + match: + any: + - resources: + kinds: + - Pod + validate: + message: >- + Lorem ipse + pattern: + spec: + =(initContainers): + - (image): "!*/foo:*.*.*" + securityContext: + allowPrivilegeEscalation: "false" + =(containers): + - (image): "!*/foo:*.*.*" + securityContext: + allowPrivilegeEscalation: "false" diff --git a/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-skip/report-skip-assert.yaml b/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-skip/report-skip-assert.yaml new file mode 100644 index 0000000000..918a3809e8 --- /dev/null +++ b/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-skip/report-skip-assert.yaml @@ -0,0 +1,23 @@ +apiVersion: wgpolicyk8s.io/v1alpha2 +kind: PolicyReport +metadata: + ownerReferences: + - apiVersion: v1 + kind: Pod + name: test-pod +scope: + apiVersion: v1 + kind: Pod + name: test-pod +results: +- message: 'conditional anchor mismatch: resource value ''test/foo:1.2.3'' does not match ''!*/foo:*.*.*'' at path /spec/containers/0/image/; conditional anchor mismatch: resource value ''test/foo:1.2.3'' does not match ''!*/foo:*.*.*'' at path /spec/containers/1/image/; conditional anchor mismatch: resource value ''test/foo:1.2.3'' does not match ''!*/foo:*.*.*'' at path /spec/initContainers/0/image/' + policy: priv + result: skip + rule: priv-esc + source: kyverno +summary: + error: 0 + fail: 0 + pass: 0 + skip: 1 + warn: 0 \ No newline at end of file diff --git a/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-skip/resource.yaml b/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-skip/resource.yaml new file mode 100644 index 0000000000..4e02559819 --- /dev/null +++ b/test/conformance/chainsaw/validate/clusterpolicy/cornercases/validate-pattern-should-skip/resource.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Pod +metadata: + name: test-pod + labels: + app: test-app +spec: + containers: + - name: main + image: test/foo:1.2.3 + securityContext: + allowPrivilegeEscalation: false + - name: side + image: test/foo:1.2.3 + initContainers: + - name: init + image: test/foo:1.2.3 + securityContext: + allowPrivilegeEscalation: true \ No newline at end of file