Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package konstraint for openSUSE:Factory checked in at 2022-05-19 22:49:03 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/konstraint (Old) and /work/SRC/openSUSE:Factory/.konstraint.new.1538 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "konstraint" Thu May 19 22:49:03 2022 rev:5 rq:977921 version:0.20.0 Changes: -------- --- /work/SRC/openSUSE:Factory/konstraint/konstraint.changes 2022-05-05 23:08:16.293697972 +0200 +++ /work/SRC/openSUSE:Factory/.konstraint.new.1538/konstraint.changes 2022-05-19 22:49:10.854324192 +0200 @@ -1,0 +2,10 @@ +Wed May 18 09:13:43 UTC 2022 - [email protected] + +- Update to version 0.20.0: + * Parse parameter names from rule header values in addition to the body (#279) + * Allow adding description to parameters - fix #273 (#275) + * Group and sort kind matchers by apiGroup (fix #269) (#272) + * Merge repeated @matchlabels and @kinds annotations (#271) + * Fix violation rule return format in test policy (#274) + +------------------------------------------------------------------- Old: ---- konstraint-0.19.1.tar.gz New: ---- konstraint-0.20.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ konstraint.spec ++++++ --- /var/tmp/diff_new_pack.lpOoFz/_old 2022-05-19 22:49:11.614325166 +0200 +++ /var/tmp/diff_new_pack.lpOoFz/_new 2022-05-19 22:49:11.618325171 +0200 @@ -19,7 +19,7 @@ %define __arch_install_post export NO_BRP_STRIP_DEBUG=true Name: konstraint -Version: 0.19.1 +Version: 0.20.0 Release: 0 Summary: A policy management tool for interacting with Gatekeeper License: Apache-2.0 ++++++ _service ++++++ --- /var/tmp/diff_new_pack.lpOoFz/_old 2022-05-19 22:49:11.646325207 +0200 +++ /var/tmp/diff_new_pack.lpOoFz/_new 2022-05-19 22:49:11.650325212 +0200 @@ -3,7 +3,7 @@ <param name="url">https://github.com/plexsystems/konstraint</param> <param name="scm">git</param> <param name="exclude">.git</param> - <param name="revision">v0.19.1</param> + <param name="revision">v0.20.0</param> <param name="versionformat">@PARENT_TAG@</param> <param name="changesgenerate">enable</param> <param name="versionrewrite-pattern">v(.*)</param> @@ -16,7 +16,7 @@ <param name="compression">gz</param> </service> <service name="go_modules" mode="disabled"> - <param name="archive">konstraint-0.19.1.tar.gz</param> + <param name="archive">konstraint-0.20.0.tar.gz</param> </service> </services> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.lpOoFz/_old 2022-05-19 22:49:11.666325232 +0200 +++ /var/tmp/diff_new_pack.lpOoFz/_new 2022-05-19 22:49:11.670325238 +0200 @@ -1,6 +1,6 @@ <servicedata> <service name="tar_scm"> <param name="url">https://github.com/plexsystems/konstraint</param> - <param name="changesrevision">fa7c92b1528b9a1c6cdb6a7177f2b11862b38f6b</param></service></servicedata> + <param name="changesrevision">34583df96caee09df52a9ff512f6c0d018b9431a</param></service></servicedata> (No newline at EOF) ++++++ konstraint-0.19.1.tar.gz -> konstraint-0.20.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/konstraint-0.19.1/docs/constraint_creation.md new/konstraint-0.20.0/docs/constraint_creation.md --- old/konstraint-0.19.1/docs/constraint_creation.md 2022-05-02 03:50:26.000000000 +0200 +++ new/konstraint-0.20.0/docs/constraint_creation.md 2022-05-11 05:10:56.000000000 +0200 @@ -103,6 +103,14 @@ # @excludedNamespaces kube-system gatekeeper-system ``` +Multiple instances of annotations for matching of the same type are merged: +``` +# @kinds apps/Deployment +# @kinds core/Pod +# is equivalent to +# @kinds apps/Deployment core/Pod +``` + ### Skipping generation of the Constraint resource In some scenarios, you may wish for Konstraint to skip the generation of the `Constraint` resource for a policy and manage that externally. To do so, add the `@skip-constraint` tag in the header comment block. @@ -113,12 +121,15 @@ To use parameters, add one or more `@parameter <name> <type>` statements where `<name>` is the name of the parameter and `<type>` is the OpenAPI v3 type of the parameter (string, integer, etc.). Arrays are supported via `@parameter <name> array <type>`. Each parameter tag must be on its own line. Each parameter in the rule body must have a `@parameter` tag in the comment block header. +Parameter description can be added after `--`, and and can fold on multiple lines following the parameter. + ```rego # @title Required Labels # # This policy allows you to require certain labels are set on a resource. # -# @parameter labels array string +# @parameter labels array string -- +# -- array of required label keys package required_labels import data.lib.core diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/konstraint-0.19.1/examples/container-deny-added-caps/constraint.yaml new/konstraint-0.20.0/examples/container-deny-added-caps/constraint.yaml --- old/konstraint-0.19.1/examples/container-deny-added-caps/constraint.yaml 2022-05-02 03:50:26.000000000 +0200 +++ new/konstraint-0.20.0/examples/container-deny-added-caps/constraint.yaml 2022-05-11 05:10:56.000000000 +0200 @@ -6,10 +6,12 @@ match: kinds: - apiGroups: - - apps - "" kinds: + - Pod + - apiGroups: + - apps + kinds: - DaemonSet - Deployment - StatefulSet - - Pod diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/konstraint-0.19.1/examples/container-deny-escalation/constraint.yaml new/konstraint-0.20.0/examples/container-deny-escalation/constraint.yaml --- old/konstraint-0.19.1/examples/container-deny-escalation/constraint.yaml 2022-05-02 03:50:26.000000000 +0200 +++ new/konstraint-0.20.0/examples/container-deny-escalation/constraint.yaml 2022-05-11 05:10:56.000000000 +0200 @@ -6,10 +6,12 @@ match: kinds: - apiGroups: - - apps - "" kinds: + - Pod + - apiGroups: + - apps + kinds: - DaemonSet - Deployment - StatefulSet - - Pod diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/konstraint-0.19.1/examples/container-deny-latest-tag/constraint.yaml new/konstraint-0.20.0/examples/container-deny-latest-tag/constraint.yaml --- old/konstraint-0.19.1/examples/container-deny-latest-tag/constraint.yaml 2022-05-02 03:50:26.000000000 +0200 +++ new/konstraint-0.20.0/examples/container-deny-latest-tag/constraint.yaml 2022-05-11 05:10:56.000000000 +0200 @@ -6,10 +6,12 @@ match: kinds: - apiGroups: - - apps - "" kinds: + - Pod + - apiGroups: + - apps + kinds: - DaemonSet - Deployment - StatefulSet - - Pod diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/konstraint-0.19.1/examples/container-deny-privileged/constraint.yaml new/konstraint-0.20.0/examples/container-deny-privileged/constraint.yaml --- old/konstraint-0.19.1/examples/container-deny-privileged/constraint.yaml 2022-05-02 03:50:26.000000000 +0200 +++ new/konstraint-0.20.0/examples/container-deny-privileged/constraint.yaml 2022-05-11 05:10:56.000000000 +0200 @@ -6,10 +6,12 @@ match: kinds: - apiGroups: - - apps - "" kinds: + - Pod + - apiGroups: + - apps + kinds: - DaemonSet - Deployment - StatefulSet - - Pod diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/konstraint-0.19.1/examples/container-deny-privileged-if-tenant/constraint.yaml new/konstraint-0.20.0/examples/container-deny-privileged-if-tenant/constraint.yaml --- old/konstraint-0.19.1/examples/container-deny-privileged-if-tenant/constraint.yaml 2022-05-02 03:50:26.000000000 +0200 +++ new/konstraint-0.20.0/examples/container-deny-privileged-if-tenant/constraint.yaml 2022-05-11 05:10:56.000000000 +0200 @@ -6,13 +6,15 @@ match: kinds: - apiGroups: - - apps - "" kinds: + - Pod + - apiGroups: + - apps + kinds: - DaemonSet - Deployment - StatefulSet - - Pod labelSelector: matchLabels: is-tenant: "true" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/konstraint-0.19.1/examples/container-deny-without-resource-constraints/constraint.yaml new/konstraint-0.20.0/examples/container-deny-without-resource-constraints/constraint.yaml --- old/konstraint-0.19.1/examples/container-deny-without-resource-constraints/constraint.yaml 2022-05-02 03:50:26.000000000 +0200 +++ new/konstraint-0.20.0/examples/container-deny-without-resource-constraints/constraint.yaml 2022-05-11 05:10:56.000000000 +0200 @@ -6,10 +6,12 @@ match: kinds: - apiGroups: - - apps - "" kinds: + - Pod + - apiGroups: + - apps + kinds: - DaemonSet - Deployment - StatefulSet - - Pod diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/konstraint-0.19.1/examples/pod-deny-host-alias/constraint.yaml new/konstraint-0.20.0/examples/pod-deny-host-alias/constraint.yaml --- old/konstraint-0.19.1/examples/pod-deny-host-alias/constraint.yaml 2022-05-02 03:50:26.000000000 +0200 +++ new/konstraint-0.20.0/examples/pod-deny-host-alias/constraint.yaml 2022-05-11 05:10:56.000000000 +0200 @@ -6,10 +6,12 @@ match: kinds: - apiGroups: - - apps - "" kinds: + - Pod + - apiGroups: + - apps + kinds: - DaemonSet - Deployment - StatefulSet - - Pod diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/konstraint-0.19.1/examples/pod-deny-host-ipc/constraint.yaml new/konstraint-0.20.0/examples/pod-deny-host-ipc/constraint.yaml --- old/konstraint-0.19.1/examples/pod-deny-host-ipc/constraint.yaml 2022-05-02 03:50:26.000000000 +0200 +++ new/konstraint-0.20.0/examples/pod-deny-host-ipc/constraint.yaml 2022-05-11 05:10:56.000000000 +0200 @@ -6,10 +6,12 @@ match: kinds: - apiGroups: - - apps - "" kinds: + - Pod + - apiGroups: + - apps + kinds: - DaemonSet - Deployment - StatefulSet - - Pod diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/konstraint-0.19.1/examples/pod-deny-host-network/constraint.yaml new/konstraint-0.20.0/examples/pod-deny-host-network/constraint.yaml --- old/konstraint-0.19.1/examples/pod-deny-host-network/constraint.yaml 2022-05-02 03:50:26.000000000 +0200 +++ new/konstraint-0.20.0/examples/pod-deny-host-network/constraint.yaml 2022-05-11 05:10:56.000000000 +0200 @@ -6,10 +6,12 @@ match: kinds: - apiGroups: - - apps - "" kinds: + - Pod + - apiGroups: + - apps + kinds: - DaemonSet - Deployment - StatefulSet - - Pod diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/konstraint-0.19.1/examples/pod-deny-host-pid/constraint.yaml new/konstraint-0.20.0/examples/pod-deny-host-pid/constraint.yaml --- old/konstraint-0.19.1/examples/pod-deny-host-pid/constraint.yaml 2022-05-02 03:50:26.000000000 +0200 +++ new/konstraint-0.20.0/examples/pod-deny-host-pid/constraint.yaml 2022-05-11 05:10:56.000000000 +0200 @@ -6,10 +6,12 @@ match: kinds: - apiGroups: - - apps - "" kinds: + - Pod + - apiGroups: + - apps + kinds: - DaemonSet - Deployment - StatefulSet - - Pod diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/konstraint-0.19.1/examples/pod-deny-without-runasnonroot/constraint.yaml new/konstraint-0.20.0/examples/pod-deny-without-runasnonroot/constraint.yaml --- old/konstraint-0.19.1/examples/pod-deny-without-runasnonroot/constraint.yaml 2022-05-02 03:50:26.000000000 +0200 +++ new/konstraint-0.20.0/examples/pod-deny-without-runasnonroot/constraint.yaml 2022-05-11 05:10:56.000000000 +0200 @@ -6,10 +6,12 @@ match: kinds: - apiGroups: - - apps - "" kinds: + - Pod + - apiGroups: + - apps + kinds: - DaemonSet - Deployment - StatefulSet - - Pod diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/konstraint-0.19.1/examples/policies-no-rego.md new/konstraint-0.20.0/examples/policies-no-rego.md --- old/konstraint-0.19.1/examples/policies-no-rego.md 2022-05-02 03:50:26.000000000 +0200 +++ new/konstraint-0.20.0/examples/policies-no-rego.md 2022-05-11 05:10:56.000000000 +0200 @@ -37,8 +37,8 @@ **Parameters:** -* labels: array of string - +* labels: array of string<br> + array of required label keys This policy allows you to require certain labels are set on a resource. Adapted from https://github.com/open-policy-agent/gatekeeper/blob/master/example/templates/k8srequiredlabels_template.yaml @@ -50,7 +50,7 @@ **Severity:** Violation -**Resources:** apps/DaemonSet apps/Deployment apps/StatefulSet core/Pod +**Resources:** core/Pod apps/DaemonSet apps/Deployment apps/StatefulSet Granting containers privileged capabilities on the node makes it easier for containers to escalate their privileges. As such, this is not allowed @@ -63,7 +63,7 @@ **Severity:** Violation -**Resources:** apps/DaemonSet apps/Deployment apps/StatefulSet core/Pod +**Resources:** core/Pod apps/DaemonSet apps/Deployment apps/StatefulSet Privileged containers can much more easily obtain root on the node. As such, they are not allowed. @@ -75,7 +75,7 @@ **Severity:** Violation -**Resources:** apps/DaemonSet apps/Deployment apps/StatefulSet core/Pod +**Resources:** core/Pod apps/DaemonSet apps/Deployment apps/StatefulSet Privileged containers can easily escalate to root privileges on the node. As such containers running as privileged or with sufficient capabilities granted @@ -88,7 +88,7 @@ **Severity:** Violation -**Resources:** apps/DaemonSet apps/Deployment apps/StatefulSet core/Pod +**Resources:** core/Pod apps/DaemonSet apps/Deployment apps/StatefulSet Pods that can change aliases in the host's /etc/hosts file can redirect traffic to malicious servers. @@ -100,7 +100,7 @@ **Severity:** Violation -**Resources:** apps/DaemonSet apps/Deployment apps/StatefulSet core/Pod +**Resources:** core/Pod apps/DaemonSet apps/Deployment apps/StatefulSet Pods that are allowed to access the host IPC can read memory of the other containers, breaking that security boundary. @@ -112,7 +112,7 @@ **Severity:** Violation -**Resources:** apps/DaemonSet apps/Deployment apps/StatefulSet core/Pod +**Resources:** core/Pod apps/DaemonSet apps/Deployment apps/StatefulSet Pods that can access the host's network interfaces can potentially access and tamper with traffic the pod should not have access to. @@ -124,7 +124,7 @@ **Severity:** Violation -**Resources:** apps/DaemonSet apps/Deployment apps/StatefulSet core/Pod +**Resources:** core/Pod apps/DaemonSet apps/Deployment apps/StatefulSet Pods that can access the host's process tree can view and attempt to modify processes outside of their namespace, breaking that security @@ -137,7 +137,7 @@ **Severity:** Violation -**Resources:** apps/DaemonSet apps/Deployment apps/StatefulSet core/Pod +**Resources:** core/Pod apps/DaemonSet apps/Deployment apps/StatefulSet Pods running as root (uid of 0) can much more easily escalate privileges to root on the node. As such, they are not allowed. @@ -236,7 +236,7 @@ **Severity:** Violation -**Resources:** apps/DaemonSet apps/Deployment apps/StatefulSet core/Pod +**Resources:** core/Pod apps/DaemonSet apps/Deployment apps/StatefulSet Using the latest tag on images can cause unexpected problems in production. By specifying a pinned version we can have higher confidence that our applications are immutable and do not change unexpectedly. @@ -263,7 +263,7 @@ **Severity:** Violation -**Resources:** apps/DaemonSet apps/Deployment apps/StatefulSet core/Pod +**Resources:** core/Pod apps/DaemonSet apps/Deployment apps/StatefulSet Resource constraints on containers ensure that a given workload does not take up more resources than it requires and potentially starve other applications that need to run. @@ -286,7 +286,7 @@ **Severity:** Violation -**Resources:** apps/DaemonSet apps/Deployment apps/StatefulSet core/Pod +**Resources:** core/Pod apps/DaemonSet apps/Deployment apps/StatefulSet **MatchLabels:** is-tenant=true @@ -317,7 +317,7 @@ **Severity:** Warning -**Resources:** apps/DaemonSet apps/Deployment apps/StatefulSet core/Pod +**Resources:** core/Pod apps/DaemonSet apps/Deployment apps/StatefulSet In order to prevent persistence in the case of a compromise, it is important to make the root filesystem read-only. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/konstraint-0.19.1/examples/policies.md new/konstraint-0.20.0/examples/policies.md --- old/konstraint-0.19.1/examples/policies.md 2022-05-02 03:50:26.000000000 +0200 +++ new/konstraint-0.20.0/examples/policies.md 2022-05-11 05:10:56.000000000 +0200 @@ -37,8 +37,8 @@ **Parameters:** -* labels: array of string - +* labels: array of string<br> + array of required label keys This policy allows you to require certain labels are set on a resource. Adapted from https://github.com/open-policy-agent/gatekeeper/blob/master/example/templates/k8srequiredlabels_template.yaml @@ -72,7 +72,7 @@ **Severity:** Violation -**Resources:** apps/DaemonSet apps/Deployment apps/StatefulSet core/Pod +**Resources:** core/Pod apps/DaemonSet apps/Deployment apps/StatefulSet Granting containers privileged capabilities on the node makes it easier for containers to escalate their privileges. As such, this is not allowed @@ -107,7 +107,7 @@ **Severity:** Violation -**Resources:** apps/DaemonSet apps/Deployment apps/StatefulSet core/Pod +**Resources:** core/Pod apps/DaemonSet apps/Deployment apps/StatefulSet Privileged containers can much more easily obtain root on the node. As such, they are not allowed. @@ -144,7 +144,7 @@ **Severity:** Violation -**Resources:** apps/DaemonSet apps/Deployment apps/StatefulSet core/Pod +**Resources:** core/Pod apps/DaemonSet apps/Deployment apps/StatefulSet Privileged containers can easily escalate to root privileges on the node. As such containers running as privileged or with sufficient capabilities granted @@ -183,7 +183,7 @@ **Severity:** Violation -**Resources:** apps/DaemonSet apps/Deployment apps/StatefulSet core/Pod +**Resources:** core/Pod apps/DaemonSet apps/Deployment apps/StatefulSet Pods that can change aliases in the host's /etc/hosts file can redirect traffic to malicious servers. @@ -215,7 +215,7 @@ **Severity:** Violation -**Resources:** apps/DaemonSet apps/Deployment apps/StatefulSet core/Pod +**Resources:** core/Pod apps/DaemonSet apps/Deployment apps/StatefulSet Pods that are allowed to access the host IPC can read memory of the other containers, breaking that security boundary. @@ -247,7 +247,7 @@ **Severity:** Violation -**Resources:** apps/DaemonSet apps/Deployment apps/StatefulSet core/Pod +**Resources:** core/Pod apps/DaemonSet apps/Deployment apps/StatefulSet Pods that can access the host's network interfaces can potentially access and tamper with traffic the pod should not have access to. @@ -279,7 +279,7 @@ **Severity:** Violation -**Resources:** apps/DaemonSet apps/Deployment apps/StatefulSet core/Pod +**Resources:** core/Pod apps/DaemonSet apps/Deployment apps/StatefulSet Pods that can access the host's process tree can view and attempt to modify processes outside of their namespace, breaking that security @@ -312,7 +312,7 @@ **Severity:** Violation -**Resources:** apps/DaemonSet apps/Deployment apps/StatefulSet core/Pod +**Resources:** core/Pod apps/DaemonSet apps/Deployment apps/StatefulSet Pods running as root (uid of 0) can much more easily escalate privileges to root on the node. As such, they are not allowed. @@ -579,7 +579,7 @@ **Severity:** Violation -**Resources:** apps/DaemonSet apps/Deployment apps/StatefulSet core/Pod +**Resources:** core/Pod apps/DaemonSet apps/Deployment apps/StatefulSet Using the latest tag on images can cause unexpected problems in production. By specifying a pinned version we can have higher confidence that our applications are immutable and do not change unexpectedly. @@ -631,7 +631,7 @@ **Severity:** Violation -**Resources:** apps/DaemonSet apps/Deployment apps/StatefulSet core/Pod +**Resources:** core/Pod apps/DaemonSet apps/Deployment apps/StatefulSet Resource constraints on containers ensure that a given workload does not take up more resources than it requires and potentially starve other applications that need to run. @@ -715,7 +715,7 @@ **Severity:** Violation -**Resources:** apps/DaemonSet apps/Deployment apps/StatefulSet core/Pod +**Resources:** core/Pod apps/DaemonSet apps/Deployment apps/StatefulSet **MatchLabels:** is-tenant=true @@ -789,7 +789,7 @@ **Severity:** Warning -**Resources:** apps/DaemonSet apps/Deployment apps/StatefulSet core/Pod +**Resources:** core/Pod apps/DaemonSet apps/Deployment apps/StatefulSet In order to prevent persistence in the case of a compromise, it is important to make the root filesystem read-only. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/konstraint-0.19.1/examples/required-labels/src.rego new/konstraint-0.20.0/examples/required-labels/src.rego --- old/konstraint-0.19.1/examples/required-labels/src.rego 2022-05-02 03:50:26.000000000 +0200 +++ new/konstraint-0.20.0/examples/required-labels/src.rego 2022-05-11 05:10:56.000000000 +0200 @@ -3,7 +3,8 @@ # This policy allows you to require certain labels are set on a resource. # Adapted from https://github.com/open-policy-agent/gatekeeper/blob/master/example/templates/k8srequiredlabels_template.yaml # -# @parameter labels array string +# @parameter labels array string -- +# -- array of required label keys package required_labels diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/konstraint-0.19.1/examples/required-labels/template.yaml new/konstraint-0.20.0/examples/required-labels/template.yaml --- old/konstraint-0.19.1/examples/required-labels/template.yaml 2022-05-02 03:50:26.000000000 +0200 +++ new/konstraint-0.20.0/examples/required-labels/template.yaml 2022-05-11 05:10:56.000000000 +0200 @@ -12,6 +12,7 @@ openAPIV3Schema: properties: labels: + description: array of required label keys items: type: string type: array diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/konstraint-0.19.1/internal/commands/create.go new/konstraint-0.20.0/internal/commands/create.go --- old/konstraint-0.19.1/internal/commands/create.go 2022-05-02 03:50:26.000000000 +0200 +++ new/konstraint-0.20.0/internal/commands/create.go 2022-05-11 05:10:56.000000000 +0200 @@ -6,7 +6,6 @@ "io/ioutil" "os" "path/filepath" - "strings" "github.com/plexsystems/konstraint/internal/rego" @@ -233,13 +232,17 @@ for _, p := range r.Parameters() { if p.IsArray { properties[p.Name] = apiextensionsv1.JSONSchemaProps{ - Type: "array", + Type: "array", + Description: p.Description, Items: &apiextensionsv1.JSONSchemaPropsOrArray{ Schema: &apiextensionsv1.JSONSchemaProps{Type: p.Type}, }, } } else { - properties[p.Name] = apiextensionsv1.JSONSchemaProps{Type: p.Type} + properties[p.Name] = apiextensionsv1.JSONSchemaProps{ + Type: p.Type, + Description: p.Description, + } } } @@ -327,42 +330,22 @@ return nil } -func setKindMatcher(constraint *unstructured.Unstructured, kindMatchers []rego.KindMatcher) error { - var kinds []string - var apiGroups []string - for _, kindMatcher := range kindMatchers { - kinds = appendIfNotExists(kinds, kindMatcher.Kind) - } +func setKindMatcher(constraint *unstructured.Unstructured, kindMatchers rego.KindMatchers) error { + constraintMatchers := make([]interface{}, len(kindMatchers)) - for _, kindMatcher := range kindMatchers { - apiGroup := kindMatcher.APIGroup - if kindMatcher.APIGroup == "core" { - apiGroup = "" + for i, matcher := range kindMatchers { + constraintMatchers[i] = map[string]interface{}{ + "apiGroups": toInterfaceSlice([]string{matcher.APIGroup}), + "kinds": toInterfaceSlice(matcher.Kinds), } - - apiGroups = appendIfNotExists(apiGroups, apiGroup) - } - - constraintMatcher := map[string]interface{}{ - "apiGroups": toInterfaceSlice(apiGroups), - "kinds": toInterfaceSlice(kinds), } - if err := unstructured.SetNestedSlice(constraint.Object, []interface{}{constraintMatcher}, "spec", "match", "kinds"); err != nil { + if err := unstructured.SetNestedSlice(constraint.Object, constraintMatchers, "spec", "match", "kinds"); err != nil { return fmt.Errorf("set constraint kinds matchers: %w", err) } return nil } -func appendIfNotExists(currentItems []string, newItem string) []string { - for _, item := range currentItems { - if strings.EqualFold(newItem, item) { - return currentItems - } - } - return append(currentItems, newItem) -} - func toInterfaceSlice(input []string) []interface{} { res := make([]interface{}, len(input)) for i, v := range input { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/konstraint-0.19.1/internal/commands/create_test.go new/konstraint-0.20.0/internal/commands/create_test.go --- old/konstraint-0.19.1/internal/commands/create_test.go 2022-05-02 03:50:26.000000000 +0200 +++ new/konstraint-0.20.0/internal/commands/create_test.go 2022-05-11 05:10:56.000000000 +0200 @@ -5,36 +5,6 @@ "testing" ) -func TestAppendIfNotExists(t *testing.T) { - type args struct { - currentItems []string - newItem string - } - tests := []struct { - name string - args args - want []string - }{ - { - "append", - args{currentItems: []string{"a"}, newItem: "b"}, - []string{"a", "b"}, - }, - { - "skip", - args{currentItems: []string{"a"}, newItem: "a"}, - []string{"a"}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := appendIfNotExists(tt.args.currentItems, tt.args.newItem); !reflect.DeepEqual(got, tt.want) { - t.Errorf("appendIfNotExists() = %v, want %v", got, tt.want) - } - }) - } -} - func TestToInterfaceSlice(t *testing.T) { type args struct { input []string diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/konstraint-0.19.1/internal/commands/document_template.go new/konstraint-0.20.0/internal/commands/document_template.go --- old/konstraint-0.19.1/internal/commands/document_template.go 2022-05-02 03:50:26.000000000 +0200 +++ new/konstraint-0.20.0/internal/commands/document_template.go 2022-05-11 05:10:56.000000000 +0200 @@ -26,7 +26,10 @@ **Parameters:** -{{ range .Header.Parameters }}* {{ .Name }}: {{ if .IsArray }}array of {{ end }}{{ .Type }} +{{ range .Header.Parameters -}} +* {{ .Name }}: {{ if .IsArray }}array of {{ end }}{{ .Type }} +{{- if .Description }}<br> + {{ .Description }}{{- end -}} {{ end }} {{- end }} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/konstraint-0.19.1/internal/rego/matchers.go new/konstraint-0.20.0/internal/rego/matchers.go --- old/konstraint-0.19.1/internal/rego/matchers.go 2022-05-02 03:50:26.000000000 +0200 +++ new/konstraint-0.20.0/internal/rego/matchers.go 2022-05-11 05:10:56.000000000 +0200 @@ -2,6 +2,7 @@ import ( "fmt" + "sort" "strings" ) @@ -14,23 +15,69 @@ ExcludedNamespaceMatcher []string } -// KindMatchers is the slice of KindMatcher +// kindMatchersMap is internal representation of KindMatchers +// it maps apiGroup to a slice of kinds +type kindMatchersMap map[string][]string + +// KindMatcher is the matcher to generate `constraints.spec.match.kinds` +type KindMatcher struct { + APIGroup string + Kinds []string +} + +// KindMatchers is a slice of KindMatcher type KindMatchers []KindMatcher func (k KindMatchers) String() string { var result string for _, kindMatcher := range k { - result += kindMatcher.APIGroup + "/" + kindMatcher.Kind + " " + apiGroup := kindMatcher.APIGroup + if apiGroup == "" { + apiGroup = "core" + } + for _, kind := range kindMatcher.Kinds { + result += apiGroup + "/" + kind + " " + } } result = strings.TrimSpace(result) return result } -// KindMatcher are the matchers that are applied to constraints. -type KindMatcher struct { - APIGroup string - Kind string +// addIfNotPresent adds an apiGroup/kind matcher to the map +// unless it's already present +// +// it also transforms apiGroup `"core"` to `""` +func (k kindMatchersMap) addIfNotPreset(apiGroup, kind string) { + if apiGroup == "core" { + apiGroup = "" + } + for _, item := range k[apiGroup] { + if strings.EqualFold(kind, item) { + return + } + } + k[apiGroup] = append(k[apiGroup], kind) +} + +// convert converts kindMatchersMap to KindMatchers, +// sorted by apiGroup, with kinds in each apiGroupKinds sorted +func (k kindMatchersMap) convert() KindMatchers { + apiGroups := make([]string, 0, len(k)) + for apiGroup := range k { + apiGroups = append(apiGroups, apiGroup) + } + sort.Strings(apiGroups) + + result := make(KindMatchers, len(apiGroups)) + for i, apiGroup := range apiGroups { + result[i].APIGroup = apiGroup + kinds := k[apiGroup] + sort.Strings(kinds) + result[i].Kinds = kinds + } + + return result } // MatchLabelsMatcher is the matcher to generate `constraints.spec.match.labelSelector.matchLabels`. @@ -54,14 +101,18 @@ // Matchers returns all of the matchers found in the rego file. func (r Rego) Matchers() (Matchers, error) { - var matchers Matchers + matchers := Matchers{ + MatchLabelsMatcher: make(MatchLabelsMatcher), + } + + kindMatchers := make(kindMatchersMap) + for _, comment := range r.headerComments { if commentStartsWith(comment, "@kinds") { - m, err := getKindMatchers(comment) + err := appendKindMatchers(kindMatchers, comment) if err != nil { return Matchers{}, fmt.Errorf("get kind matchers: %w", err) } - matchers.KindMatchers = m } if commentStartsWith(comment, "@matchlabels") { @@ -69,7 +120,9 @@ if err != nil { return Matchers{}, fmt.Errorf("get match labels matcher: %w", err) } - matchers.MatchLabelsMatcher = m + for k, v := range m { + matchers.MatchLabelsMatcher[k] = v + } } if commentStartsWith(comment, "@matchExpression") { @@ -97,29 +150,26 @@ } } + if len(kindMatchers) > 0 { + matchers.KindMatchers = kindMatchers.convert() + } + return matchers, nil } -func getKindMatchers(comment string) ([]KindMatcher, error) { +func appendKindMatchers(kindMatchersMap kindMatchersMap, comment string) error { kindMatcherText := strings.TrimSpace(strings.SplitAfter(comment, "@kinds")[1]) kindMatcherGroups := strings.Split(kindMatcherText, " ") - var kindMatchers []KindMatcher for _, kindMatcherGroup := range kindMatcherGroups { kindMatcherSegments := strings.Split(kindMatcherGroup, "/") if len(kindMatcherSegments) != 2 { - return nil, fmt.Errorf("invalid @kinds: %s", kindMatcherGroup) + return fmt.Errorf("invalid @kinds: %s", kindMatcherGroup) } - - kindMatcher := KindMatcher{ - APIGroup: kindMatcherSegments[0], - Kind: kindMatcherSegments[1], - } - - kindMatchers = append(kindMatchers, kindMatcher) + kindMatchersMap.addIfNotPreset(kindMatcherSegments[0], kindMatcherSegments[1]) } - return kindMatchers, nil + return nil } func getMatchLabelsMatcher(comment string) (MatchLabelsMatcher, error) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/konstraint-0.19.1/internal/rego/matchers_test.go new/konstraint-0.20.0/internal/rego/matchers_test.go --- old/konstraint-0.19.1/internal/rego/matchers_test.go 2022-05-02 03:50:26.000000000 +0200 +++ new/konstraint-0.20.0/internal/rego/matchers_test.go 2022-05-11 05:10:56.000000000 +0200 @@ -5,17 +5,18 @@ "testing" ) -func TestGetKindMatchers(t *testing.T) { +func TestMultilineKindMatchers(t *testing.T) { comments := []string{ "@kinds core/Pod apps/Deployment", + "@kinds apps/StatefulSet", } rego := Rego{ headerComments: comments, } expected := KindMatchers{ - {APIGroup: "core", Kind: "Pod"}, - {APIGroup: "apps", Kind: "Deployment"}, + {APIGroup: "", Kinds: []string{"Pod"}}, + {APIGroup: "apps", Kinds: []string{"Deployment", "StatefulSet"}}, } matchers, err := rego.Matchers() @@ -29,9 +30,67 @@ } } +func TestKindMatchers(t *testing.T) { + tests := []struct { + name string + comment string + want KindMatchers + }{ + { + "core_Pod", + "@kinds core/Pod", + KindMatchers{{APIGroup: "", Kinds: []string{"Pod"}}}, + }, + { + "core_Pod,core_Pod", + "@kinds core/Pod core/Pod", + KindMatchers{{APIGroup: "", Kinds: []string{"Pod"}}}, + }, + { + "apps_Deployment,apps_StatefulSet", + "@kinds apps/Deployment apps/StatefulSet", + KindMatchers{{APIGroup: "apps", Kinds: []string{"Deployment", "StatefulSet"}}}, + }, + { + "apps_StatefulSet,apps_Deployment", + "@kinds apps/StatefulSet apps/Deployment", + KindMatchers{{APIGroup: "apps", Kinds: []string{"Deployment", "StatefulSet"}}}, + }, + { + "apps_Deployment,apps_StatefulSet,core_Pod", + "@kinds apps/Deployment apps/StatefulSet core/Pod", + KindMatchers{ + {APIGroup: "", Kinds: []string{"Pod"}}, + {APIGroup: "apps", Kinds: []string{"Deployment", "StatefulSet"}}, + }, + }, + { + "apps_Deployment,core_Pod,apps_StatefulSet", + "@kinds apps/Deployment core/Pod apps/StatefulSet", + KindMatchers{ + {APIGroup: "", Kinds: []string{"Pod"}}, + {APIGroup: "apps", Kinds: []string{"Deployment", "StatefulSet"}}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + matchers, err := Rego{headerComments: []string{tt.comment}}.Matchers() + if err != nil { + t.Fatal(err) + } + actual := matchers.KindMatchers + if !reflect.DeepEqual(tt.want, actual) { + t.Errorf("Unexpected KindMatchers. expected %v, actual %v.", tt.want, actual) + } + }) + } +} + func TestGetMatchLabelsMatcher(t *testing.T) { comments := []string{ "@matchlabels team=a app.kubernetes.io/name=test", + "@matchlabels example.com/env=production", } rego := Rego{ headerComments: comments, @@ -40,6 +99,7 @@ expected := MatchLabelsMatcher{ "team": "a", "app.kubernetes.io/name": "test", + "example.com/env": "production", } matchers, err := rego.Matchers() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/konstraint-0.19.1/internal/rego/rego.go new/konstraint-0.20.0/internal/rego/rego.go --- old/konstraint-0.19.1/internal/rego/rego.go 2022-05-02 03:50:26.000000000 +0200 +++ new/konstraint-0.20.0/internal/rego/rego.go 2022-05-11 05:10:56.000000000 +0200 @@ -42,9 +42,10 @@ // Parameter represents a parameter that the policy uses type Parameter struct { - Name string - Type string - IsArray bool + Name string + Type string + IsArray bool + Description string } // GetAllSeverities gets all of the rego files found in the given @@ -194,8 +195,16 @@ func (r Rego) Description() string { var description string var handlingCodeBlock bool + var handlingParamDescription bool + for _, comment := range r.headerComments { - if commentStartsWith(comment, "@") { + if !handlingCodeBlock && !handlingParamDescription && commentStartsWith(comment, "@parameter") && strings.Contains(comment, "--") { + handlingParamDescription = true + } else if !handlingCodeBlock && handlingParamDescription && !commentStartsWith(comment, "--") { + handlingParamDescription = false + } + + if handlingParamDescription || commentStartsWith(comment, "@") { continue } @@ -327,7 +336,7 @@ } } - bodyParams := getBodyParamNames(file.Parsed.Rules) + bodyParams := getRuleParamNames(file.Parsed.Rules) headerParams, err := getHeaderParams(headerComments) if err != nil { return nil, fmt.Errorf("parse header parameters: %w", err) @@ -375,29 +384,33 @@ return regos, nil } -func getBodyParamNames(rules []*ast.Rule) []string { - r := regexp.MustCompile(`(core|input)\.parameters\.([a-zA-Z0-9_-]+)`) - var bodyParams []string - for _, rule := range rules { - matches := r.FindAllStringSubmatch(rule.Body.String(), -1) +func getRuleParamNames(rules []*ast.Rule) []string { + re := regexp.MustCompile(`input\.parameters\.([a-zA-Z0-9_-]+)`) + var ruleParams []string + for _, r := range rules { + matches := re.FindAllStringSubmatch(r.String(), -1) for _, match := range matches { - if !contains(bodyParams, match[2]) { - bodyParams = append(bodyParams, match[2]) + if !contains(ruleParams, match[1]) { + ruleParams = append(ruleParams, match[1]) } } } - return bodyParams + return ruleParams } func getHeaderParams(comments []string) ([]Parameter, error) { var parameters []Parameter - for _, comment := range comments { + for i := 0; i < len(comments); i++ { + comment := comments[i] + if !commentStartsWith(comment, "@parameter ") { continue } params := strings.SplitAfter(comment, "@parameter ")[1] + paramsDesc := strings.SplitN(params, " --", 2) + params = paramsDesc[0] paramsSplit := strings.Split(params, " ") if len(paramsSplit) == 0 { return nil, fmt.Errorf("parameter name and type must be specified") @@ -417,6 +430,22 @@ p.Type = paramsSplit[1] } + if len(paramsDesc) > 1 { + p.Description = strings.TrimSpace(paramsDesc[1]) + + for i++; i != len(comments); i++ { + extraComment := strings.TrimSpace(comments[i]) + if !strings.HasPrefix(extraComment, "--") { + i-- + break + } + extraComment = strings.TrimSpace(extraComment[2:]) + p.Description += " " + extraComment + } + + p.Description = strings.TrimSpace(p.Description) + } + parameters = append(parameters, p) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/konstraint-0.19.1/internal/rego/rego_test.go new/konstraint-0.20.0/internal/rego/rego_test.go --- old/konstraint-0.19.1/internal/rego/rego_test.go 2022-05-02 03:50:26.000000000 +0200 +++ new/konstraint-0.20.0/internal/rego/rego_test.go 2022-05-11 05:10:56.000000000 +0200 @@ -162,43 +162,52 @@ } } -func TestGetBodyParamNamesFromInput(t *testing.T) { - ruleString := `violation[msg] { - foo := "bar" - bar := input.parameters.baz - baz := input.parameters.foobars[_] - box := input.parameters.baz -}` - - rule, err := ast.ParseRule(ruleString) - if err != nil { - t.Fatalf("parse rule: %s", err) - } - - expected := []string{"baz", "foobars"} - actual := getBodyParamNames([]*ast.Rule{rule}) - if !(reflect.DeepEqual(expected, actual)) { - t.Errorf("unexpected bodyParams. expected %+v, actual %+v", expected, actual) - } -} - -func TestGetBodyParamNamesFromCore(t *testing.T) { - ruleString := `violation[msg] { - foo := "bar" - bar := core.parameters.baz - baz := core.parameters.foobars[_] - box := core.parameters.baz -}` - - rule, err := ast.ParseRule(ruleString) - if err != nil { - t.Fatalf("parse rule: %s", err) +func TestGetRuleParamNamesFromInput(t *testing.T) { + testCases := []struct { + desc string + rule string + want []string + }{ + { + desc: "No Parameters", + rule: `foo = "bar" { true }`, + }, + { + desc: "Parameters in rule body", + rule: `violation[msg] { + foo := "bar" + bar := input.parameters.baz + baz := input.parameters.foobars[_] + box := input.parameters.baz + }`, + want: []string{"baz", "foobars"}, + }, + { + desc: "Parameters in rule value", + rule: `foo = input.parameters.bar { true }`, + want: []string{"bar"}, + }, + { + desc: "Parameters in body and value", + rule: `foo = input.parameters.bar { + x := input.parameters.baz + }`, + want: []string{"bar", "baz"}, + }, } - expected := []string{"baz", "foobars"} - actual := getBodyParamNames([]*ast.Rule{rule}) - if !(reflect.DeepEqual(expected, actual)) { - t.Errorf("unexpected bodyParams. expected %+v, actual %+v", expected, actual) + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + rule, err := ast.ParseRule(tc.rule) + if err != nil { + t.Fatalf("parse rule: %s", err) + } + + actual := getRuleParamNames([]*ast.Rule{rule}) + if !(reflect.DeepEqual(tc.want, actual)) { + t.Errorf("unexpected bodyParams. expected %+v, actual %+v", tc.want, actual) + } + }) } } @@ -206,21 +215,30 @@ comments := []string{ "@title Title", "Description", - "@parameter foo string", + "@parameter foo string -- with description", "@parameter bar array string", + "@parameter baz array string -- with multiline", + "-- description", "@kinds another/thing", } expected := []Parameter{ { - Name: "foo", - Type: "string", + Name: "foo", + Type: "string", + Description: "with description", }, { Name: "bar", Type: "string", IsArray: true, }, + { + Name: "baz", + Type: "string", + IsArray: true, + Description: "with multiline description", + }, } actual, err := getHeaderParams(comments) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/konstraint-0.19.1/test/constraint_Test.yaml new/konstraint-0.20.0/test/constraint_Test.yaml --- old/konstraint-0.19.1/test/constraint_Test.yaml 2022-05-02 03:50:26.000000000 +0200 +++ new/konstraint-0.20.0/test/constraint_Test.yaml 2022-05-11 05:10:56.000000000 +0200 @@ -9,13 +9,15 @@ - gatekeeper-system kinds: - apiGroups: - - apps - "" kinds: + - Pod + - apiGroups: + - apps + kinds: - DaemonSet - Deployment - StatefulSet - - Pod labelSelector: matchExpressions: - key: foo diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/konstraint-0.19.1/test/src.rego new/konstraint-0.20.0/test/src.rego --- old/konstraint-0.19.1/test/src.rego 2022-05-02 03:50:26.000000000 +0200 +++ new/konstraint-0.20.0/test/src.rego 2022-05-11 05:10:56.000000000 +0200 @@ -7,12 +7,14 @@ # @matchExpression doggos Exists # @namespaces dev stage prod # @excludedNamespaces kube-system gatekeeper-system +# @parameter super string -- super duper cool parameter +# -- with a description on two strings package test import data.lib.libraryA policyID := "P123456" -violation["msg"] { +violation[{"msg": "msg"}] { true # some comment with a trailing space } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/konstraint-0.19.1/test/template_Test.yaml new/konstraint-0.20.0/test/template_Test.yaml --- old/konstraint-0.19.1/test/template_Test.yaml 2022-05-02 03:50:26.000000000 +0200 +++ new/konstraint-0.20.0/test/template_Test.yaml 2022-05-11 05:10:56.000000000 +0200 @@ -8,6 +8,12 @@ spec: names: kind: Test + validation: + openAPIV3Schema: + properties: + super: + description: super duper cool parameter with a description on two strings + type: string targets: - libs: - |- @@ -22,7 +28,7 @@ policyID := "P123456" - violation["msg"] { + violation[{"msg": "msg"}] { true # some comment with a trailing space } target: admission.k8s.gatekeeper.sh ++++++ vendor.tar.gz ++++++
