1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-14 11:57:48 +00:00

merge main and fmt

Signed-off-by: Jim Bugwadia <jim@nirmata.com>
This commit is contained in:
Jim Bugwadia 2021-10-29 09:18:47 -07:00
commit 4019d6b8b2
81 changed files with 845 additions and 2527 deletions

View file

@ -40,6 +40,16 @@ jobs:
exit 1
fi
- name: goimports
run: |
if [ "$(goimports -l . | wc -l)" -ne 0 ]
then
echo "The following files were found to have import formatting issues:"
goimports -l -l .
echo "Please run 'make fmt' to go format the above files."
exit 1
fi
- name: golangci-lint
uses: reviewdog/action-golangci-lint@v1
@ -115,7 +125,7 @@ jobs:
- name: Trivy Scan Image
uses: aquasecurity/trivy-action@master
with:
with:
image-ref: 'ghcr.io/kyverno/kyverno:latest'
format: 'table'
exit-code: '1'

View file

@ -68,7 +68,7 @@ docker-publish-sbom: docker-build-sbom docker-push-sbom
docker-build-sbom:
@docker buildx build --file $(PWD)/$(ALPINE_PATH)/Dockerfile --tag $(REPO)/$(SBOM_IMAGE):$(IMAGE_TAG) .
docker-push-signature:
docker-push-sbom:
@docker buildx build --file $(PWD)/$(ALPINE_PATH)/Dockerfile --push --tag $(REPO)/$(SBOM_IMAGE):$(IMAGE_TAG) .
@docker buildx build --file $(PWD)/$(ALPINE_PATH)/Dockerfile --push --tag $(REPO)/$(SBOM_IMAGE):latest .
@ -308,9 +308,20 @@ endif
deepcopy-autogen: controller-gen
$(CONTROLLER_GEN) object:headerFile="scripts/boilerplate.go.txt" paths="./..."
goimports:
ifeq (, $(shell which goimports))
@{ \
echo "goimports not found!";\
echo "installing goimports...";\
go get golang.org/x/tools/cmd/goimports;\
}
else
GO_IMPORTS=$(shell which goimports)
endif
# Run go fmt against code
fmt:
gofmt -s -w .
fmt: goimports
go fmt ./... && $(GO_IMPORTS) -w ./
vet:
go vet ./...

View file

@ -26,7 +26,7 @@ spec:
validate:
message: >-
Adding of additional capabilities beyond the default set is not allowed.
The fields spec.containers[*].securityContext.capabilities.add and
The fields spec.containers[*].securityContext.capabilities.add and
spec.initContainers[*].securityContext.capabilities.add must be empty.
pattern:
spec:

View file

@ -9,7 +9,7 @@ metadata:
{{- if .Values.podSecuritySeverity }}
policies.kyverno.io/severity: {{ .Values.podSecuritySeverity | quote }}
{{- end }}
policies.kyverno.io/description: >-
policies.kyverno.io/description: >-
Host namespaces (Process ID namespace, Inter-Process Communication namespace, and
network namespace) allow access to shared information and can be used to elevate
privileges. Pods should not be allowed access to host namespaces.
@ -33,4 +33,4 @@ spec:
=(hostPID): "false"
=(hostIPC): "false"
=(hostNetwork): "false"
{{- end -}}
{{- end -}}

View file

@ -31,4 +31,4 @@ spec:
spec:
=(volumes):
- X(hostPath): "null"
{{- end -}}
{{- end -}}

View file

@ -35,4 +35,4 @@ spec:
containers:
- =(ports):
- X(hostPort): 0
{{- end -}}
{{- end -}}

View file

@ -30,8 +30,8 @@ spec:
spec:
=(initContainers):
- =(securityContext):
=(privileged): "false"
=(privileged): "false"
containers:
- =(securityContext):
=(privileged): "false"
{{- end -}}
{{- end -}}

View file

@ -26,7 +26,7 @@ spec:
message: >-
Changing the proc mount from the default is not allowed. The fields
spec.containers[*].securityContext.procMount and
spec.initContainers[*].securityContext.procMount must not be changed
spec.initContainers[*].securityContext.procMount must not be changed
from `Default`.
pattern:
spec:
@ -36,4 +36,4 @@ spec:
containers:
- =(securityContext):
=(procMount): "Default"
{{- end -}}
{{- end -}}

View file

@ -38,4 +38,4 @@ spec:
containers:
- =(securityContext):
X(seLinuxOptions): "null"
{{- end -}}
{{- end -}}

View file

@ -11,8 +11,8 @@ metadata:
policies.kyverno.io/severity: {{ .Values.podSecuritySeverity | quote }}
{{- end }}
policies.kyverno.io/description: >-
On supported hosts, the 'runtime/default' AppArmor profile is applied by default.
The default policy should prevent overriding or disabling the policy, or restrict
On supported hosts, the 'runtime/default' AppArmor profile is applied by default.
The default policy should prevent overriding or disabling the policy, or restrict
overrides to an allowed set of profiles.
labels: {{ include "kyverno-policies.labels" . | nindent 4 }}
app: kyverno
@ -34,4 +34,4 @@ spec:
metadata:
=(annotations):
=(container.apparmor.security.beta.kubernetes.io/*): "runtime/default"
{{- end -}}
{{- end -}}

View file

@ -37,4 +37,4 @@ spec:
=(sysctls):
- name: "kernel.shm_rmid_forced | net.ipv4.ip_local_port_range | net.ipv4.tcp_syncookies | net.ipv4.ping_group_range"
value: "?*"
{{- end -}}
{{- end -}}

View file

@ -36,4 +36,4 @@ spec:
containers:
- =(securityContext):
=(allowPrivilegeEscalation): "false"
{{- end -}}
{{- end -}}

View file

@ -24,9 +24,9 @@ spec:
- Pod
validate:
message: >-
Running with root group IDs is disallowed. The fields
spec.securityContext.runAsGroup, spec.containers[*].securityContext.runAsGroup,
and spec.initContainers[*].securityContext.runAsGroup must be empty
Running with root group IDs is disallowed. The fields
spec.securityContext.runAsGroup, spec.containers[*].securityContext.runAsGroup,
and spec.initContainers[*].securityContext.runAsGroup must be empty
or greater than zero.
pattern:
spec:
@ -45,7 +45,7 @@ spec:
- Pod
validate:
message: >-
Adding of supplemental group IDs is not allowed. The field
Adding of supplemental group IDs is not allowed. The field
spec.securityContext.supplementalGroups must not be defined.
pattern:
spec:
@ -64,4 +64,4 @@ spec:
spec:
=(securityContext):
=(fsGroup): ">0"
{{- end -}}
{{- end -}}

View file

@ -35,12 +35,12 @@ spec:
=(runAsNonRoot): true
=(initContainers):
- =(securityContext):
=(runAsNonRoot): true
=(runAsNonRoot): true
- spec:
containers:
- securityContext:
runAsNonRoot: true
=(initContainers):
- securityContext:
runAsNonRoot: true
{{- end -}}
runAsNonRoot: true
{{- end -}}

View file

@ -44,4 +44,4 @@ spec:
- =(securityContext):
=(seccompProfile):
=(type): "runtime/default"
{{- end -}}
{{- end -}}

View file

@ -278,4 +278,4 @@ spec:
spec:
=(volumes):
- X(csi): "null"
{{- end -}}
{{- end -}}

View file

@ -36,7 +36,7 @@ rules:
verbs:
- create
- delete
- get
- get
- list
- patch
- update
@ -52,7 +52,7 @@ rules:
verbs:
- create
- delete
- get
- get
- update
- watch
- apiGroups:
@ -62,7 +62,7 @@ rules:
resourceNames:
- kubernetes.io/legacy-unknown
verbs:
- approve
- approve
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
@ -125,7 +125,7 @@ rules:
- customresourcedefinitions
verbs:
- delete
---
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
@ -226,4 +226,4 @@ rules:
- reportchangerequests
- clusterreportchangerequests
verbs:
- "*"
- "*"

View file

@ -11,4 +11,4 @@ spec:
selector:
matchLabels: {{ include "kyverno.matchLabels" . | nindent 6 }}
app: kyverno
{{- end }}
{{- end }}

View file

@ -34,4 +34,4 @@ spec:
tlsConfig:
{{- toYaml .Values.serviceMonitor.tlsConfig | nindent 8 }}
{{- end }}
{{- end }}
{{- end }}

View file

@ -1,8 +1,6 @@
package main
import (
"github.com/kyverno/kyverno/pkg/kyverno"
)
import "github.com/kyverno/kyverno/pkg/kyverno"
func main() {
kyverno.CLI()

1
go.mod
View file

@ -37,6 +37,7 @@ require (
github.com/spf13/cobra v1.2.1
github.com/stretchr/testify v1.7.0
github.com/xanzy/ssh-agent v0.3.0 // indirect
golang.org/x/tools v0.1.7 // indirect
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
gotest.tools v2.2.0+incompatible

5
go.sum
View file

@ -1721,6 +1721,7 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
@ -1990,6 +1991,7 @@ golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20210505214959-0714010a04ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f h1:w6wWR0H+nyVpbSAQbzVEIACVyr/h8l/BEkY6Sokc7Eg=
golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
@ -2156,6 +2158,7 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210903071746-97244b99971b h1:3Dq0eVHn0uaQJmPO+/aYPI/fRMqdrVDbu7MQcku54gg=
golang.org/x/sys v0.0.0-20210903071746-97244b99971b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -2278,6 +2281,8 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.7 h1:6j8CgantCy3yc8JGBqkDLMKWqZ0RDU2g1HVgacojGWQ=
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View file

@ -50,6 +50,7 @@ type GenerateRequestContext struct {
AdmissionRequestInfo AdmissionRequestInfoObject `json:"admissionRequestInfo,omitempty" yaml:"admissionRequestInfo,omitempty"`
}
// AdmissionRequestInfoObject stores the admission request and operation details
type AdmissionRequestInfoObject struct {
// +optional
AdmissionRequest string `json:"admissionRequest,omitempty" yaml:"admissionRequest,omitempty"`

View file

@ -136,7 +136,7 @@ const (
Fail FailurePolicyType = "Fail"
)
// AnyAllCondition consists of conditions wrapped denoting a logical criteria to be fulfilled.
// AnyAllConditions consists of conditions wrapped denoting a logical criteria to be fulfilled.
// AnyConditions get fulfilled when at least one of its sub-conditions passes.
// AllConditions get fulfilled only when all of its sub-conditions pass.
type AnyAllConditions struct {
@ -311,9 +311,10 @@ type ExcludeResources struct {
ResourceDescription `json:"resources,omitempty" yaml:"resources,omitempty"`
}
// ResourceFilters is a slice of ResourceFilter
type ResourceFilters []ResourceFilter
// ResourceFilters allow users to "AND" or "OR" between resources
// ResourceFilter allow users to "AND" or "OR" between resources
type ResourceFilter struct {
// UserInfo contains information about the user performing the operation.
// +optional
@ -411,12 +412,12 @@ type Mutation struct {
// +optional
PatchesJSON6902 string `json:"patchesJson6902,omitempty" yaml:"patchesJson6902,omitempty"`
// ForEach applies policy rule changes to nested elements.
// ForEachMutation applies policy rule changes to nested elements.
// +optional
ForEachMutation []*ForEachMutation `json:"foreach,omitempty" yaml:"foreach,omitempty"`
}
// ForEach applies policy rule changes to nested elements.
// ForEachMutation applies policy rule changes to nested elements.
type ForEachMutation struct {
// List specifies a JMESPath expression that results in one or more elements
@ -427,7 +428,7 @@ type ForEachMutation struct {
// +optional
Context []ContextEntry `json:"context,omitempty" yaml:"context,omitempty"`
// Preconditions are used to determine if a policy rule should be applied by evaluating a
// AnyAllConditions are used to determine if a policy rule should be applied by evaluating a
// set of conditions. The declaration can contain nested `any` or `all` statements.
// See: https://kyverno.io/docs/writing-policies/preconditions/
// +kubebuilder:validation:XPreserveUnknownFields
@ -498,7 +499,7 @@ type Deny struct {
AnyAllConditions apiextensions.JSON `json:"conditions,omitempty" yaml:"conditions,omitempty"`
}
// ForEach applies policy rule checks to nested elements.
// ForEachValidation applies policy rule checks to nested elements.
type ForEachValidation struct {
// List specifies a JMESPath expression that results in one or more elements
@ -509,7 +510,7 @@ type ForEachValidation struct {
// +optional
Context []ContextEntry `json:"context,omitempty" yaml:"context,omitempty"`
// Preconditions are used to determine if a policy rule should be applied by evaluating a
// AnyAllConditions are used to determine if a policy rule should be applied by evaluating a
// set of conditions. The declaration can contain nested `any` or `all` statements.
// See: https://kyverno.io/docs/writing-policies/preconditions/
// +kubebuilder:validation:XPreserveUnknownFields

View file

@ -19,7 +19,7 @@ func (p *ClusterPolicy) HasAutoGenAnnotation() bool {
return false
}
//HasMutateOrValidateOrGenerate checks for rule types
// HasMutateOrValidateOrGenerate checks for rule types
func (p *ClusterPolicy) HasMutateOrValidateOrGenerate() bool {
for _, rule := range p.Spec.Rules {
if rule.HasMutate() || rule.HasValidate() || rule.HasGenerate() {
@ -29,7 +29,7 @@ func (p *ClusterPolicy) HasMutateOrValidateOrGenerate() bool {
return false
}
//HasMutate checks for mutate rule types
// HasMutate checks for mutate rule types
func (p *ClusterPolicy) HasMutate() bool {
for _, rule := range p.Spec.Rules {
if rule.HasMutate() {
@ -62,7 +62,7 @@ func (p *ClusterPolicy) HasGenerate() bool {
return false
}
//HasVerifyImages checks for image verification rule types
// HasVerifyImages checks for image verification rule types
func (p *ClusterPolicy) HasVerifyImages() bool {
for _, rule := range p.Spec.Rules {
if rule.HasVerifyImages() {
@ -102,6 +102,7 @@ func (r Rule) HasGenerate() bool {
return !reflect.DeepEqual(r.Generation, Generation{})
}
// MatchKinds returns a slice of all kinds to match
func (r Rule) MatchKinds() []string {
matchKinds := r.MatchResources.ResourceDescription.Kinds
for _, value := range r.MatchResources.All {
@ -114,6 +115,7 @@ func (r Rule) MatchKinds() []string {
return matchKinds
}
// ExcludeKinds returns a slice of all kinds to exclude
func (r Rule) ExcludeKinds() []string {
excludeKinds := r.ExcludeResources.ResourceDescription.Kinds
for _, value := range r.ExcludeResources.All {
@ -243,20 +245,20 @@ func (in *Rule) DeepCopyInto(out *Rule) {
// }
}
//ToKey generates the key string used for adding label to polivy violation
// ToKey generates the key string used for adding label to polivy violation
func (rs ResourceSpec) ToKey() string {
return rs.Kind + "." + rs.Name
}
// ViolatedRule stores the information regarding the rule.
type ViolatedRule struct {
// Specifies violated rule name.
// Name specifies violated rule name.
Name string `json:"name" yaml:"name"`
// Specifies violated rule type.
// Type specifies violated rule type.
Type string `json:"type" yaml:"type"`
// Specifies violation message.
// Message specifies violation message.
// +optional
Message string `json:"message" yaml:"message"`

View file

@ -22,8 +22,10 @@ import (
// Policy Reporting Modes
const (
Enforce = "enforce" // blocks the request on failure
Audit = "audit" // dont block the request on failure, but report failiures as policy violations
// Enforce blocks the request on failure
Enforce = "enforce"
// Audit indicates not to block the request on failure, but report failiures as policy violations
Audit = "audit"
)
// Policy Reporting Types
@ -118,6 +120,7 @@ func VariableToJSON(key, value string) []byte {
return jsonData
}
// RetryFunc allows retrying a function on error within a given timeout
func RetryFunc(retryInterval, timeout time.Duration, run func() error, logger logr.Logger) func() error {
return func() error {
registerTimeout := time.After(timeout)

View file

@ -104,12 +104,14 @@ func (cd *ConfigData) FilterNamespaces(namespaces []string) []string {
return results
}
// GetWebhooks returns the webhook configs
func (cd *ConfigData) GetWebhooks() []WebhookConfig {
cd.mux.RLock()
defer cd.mux.RUnlock()
return cd.webhooks
}
// GetInitConfigMapName returns the init configmap name
func (cd *ConfigData) GetInitConfigMapName() string {
return cd.cmName
}
@ -170,7 +172,7 @@ func NewConfigData(rclient kubernetes.Interface, cmInformer informers.ConfigMapI
return &cd
}
//Run checks syncing
// Run checks syncing
func (cd *ConfigData) Run(stopCh <-chan struct{}) {
logger := cd.log
// wait for cache to populate first time

View file

@ -24,6 +24,7 @@ type MetricsConfigData struct {
log logr.Logger
}
// MetricsConfig stores the config for metrics
type MetricsConfig struct {
namespaces namespacesConfig
metricsRefreshInterval time.Duration
@ -44,10 +45,12 @@ func (mcd *MetricsConfigData) GetIncludeNamespaces() []string {
return mcd.metricsConfig.namespaces.IncludeNamespaces
}
// GetMetricsRefreshInterval returns the refresh interval for the metrics
func (mcd *MetricsConfigData) GetMetricsRefreshInterval() time.Duration {
return mcd.metricsConfig.metricsRefreshInterval
}
// GetMetricsConfigMapName returns the configmap name for the metric
func (mcd *MetricsConfigData) GetMetricsConfigMapName() string {
return mcd.cmName
}

View file

@ -2,6 +2,7 @@ package cosign
import (
"context"
"github.com/google/go-containerregistry/pkg/name"
"github.com/sigstore/cosign/pkg/cosign"
)

View file

@ -49,6 +49,7 @@ func Initialize(client kubernetes.Interface, namespace, serviceAccount string, i
return nil
}
// VerifySignature verifies that the image has the expected key
func VerifySignature(imageRef string, key []byte, repository string, log logr.Logger) (digest string, err error) {
pubKey, err := decodePEM(key)
if err != nil {

View file

@ -1,10 +1,11 @@
package cosign
import (
"testing"
"github.com/go-logr/logr"
"github.com/sigstore/cosign/pkg/cosign"
"gotest.tools/assert"
"testing"
)
const cosignPayload = `{

View file

@ -3,6 +3,7 @@ package cosign
import (
"context"
"fmt"
"github.com/google/go-containerregistry/pkg/name"
"github.com/sigstore/cosign/pkg/cosign"
)

View file

@ -10,14 +10,14 @@ import (
"sigs.k8s.io/controller-runtime/pkg/log"
)
//ValidationHandler for element processes
// ValidationHandler for element processes
type ValidationHandler interface {
Handle(handler resourceElementHandler, resourceMap map[string]interface{}, originPattern interface{}, ac *common.AnchorKey) (string, error)
}
type resourceElementHandler = func(log logr.Logger, resourceElement, patternElement, originPattern interface{}, path string, ac *common.AnchorKey) (string, error)
//CreateElementHandler factory to process elements
// CreateElementHandler factory to process elements
func CreateElementHandler(element string, pattern interface{}, path string) ValidationHandler {
switch {
case commonAnchors.IsConditionAnchor(element):
@ -35,7 +35,7 @@ func CreateElementHandler(element string, pattern interface{}, path string) Vali
}
}
//NewNegationHandler returns instance of negation handler
// NewNegationHandler returns instance of negation handler
func NewNegationHandler(anchor string, pattern interface{}, path string) ValidationHandler {
return NegationHandler{
anchor: anchor,
@ -44,14 +44,14 @@ func NewNegationHandler(anchor string, pattern interface{}, path string) Validat
}
}
//NegationHandler provides handler for check if the tag in anchor is not defined
// NegationHandler provides handler for check if the tag in anchor is not defined
type NegationHandler struct {
anchor string
pattern interface{}
path string
}
//Handle process negation handler
// Handle process negation handler
func (nh NegationHandler) Handle(handler resourceElementHandler, resourceMap map[string]interface{}, originPattern interface{}, ac *common.AnchorKey) (string, error) {
anchorKey, _ := commonAnchors.RemoveAnchor(nh.anchor)
currentPath := nh.path + anchorKey + "/"
@ -64,7 +64,7 @@ func (nh NegationHandler) Handle(handler resourceElementHandler, resourceMap map
return "", nil
}
//NewEqualityHandler returens instance of equality handler
// NewEqualityHandler returens instance of equality handler
func NewEqualityHandler(anchor string, pattern interface{}, path string) ValidationHandler {
return EqualityHandler{
anchor: anchor,
@ -73,14 +73,14 @@ func NewEqualityHandler(anchor string, pattern interface{}, path string) Validat
}
}
//EqualityHandler provides handler for non anchor element
// EqualityHandler provides handler for non anchor element
type EqualityHandler struct {
anchor string
pattern interface{}
path string
}
//Handle processed condition anchor
// Handle processed condition anchor
func (eh EqualityHandler) Handle(handler resourceElementHandler, resourceMap map[string]interface{}, originPattern interface{}, ac *common.AnchorKey) (string, error) {
anchorKey, _ := commonAnchors.RemoveAnchor(eh.anchor)
currentPath := eh.path + anchorKey + "/"
@ -96,7 +96,7 @@ func (eh EqualityHandler) Handle(handler resourceElementHandler, resourceMap map
return "", nil
}
//NewDefaultHandler returns handler for non anchor elements
// NewDefaultHandler returns handler for non anchor elements
func NewDefaultHandler(element string, pattern interface{}, path string) ValidationHandler {
return DefaultHandler{
element: element,
@ -105,14 +105,14 @@ func NewDefaultHandler(element string, pattern interface{}, path string) Validat
}
}
//DefaultHandler provides handler for non anchor element
// DefaultHandler provides handler for non anchor element
type DefaultHandler struct {
element string
pattern interface{}
path string
}
//Handle process non anchor element
// Handle process non anchor element
func (dh DefaultHandler) Handle(handler resourceElementHandler, resourceMap map[string]interface{}, originPattern interface{}, ac *common.AnchorKey) (string, error) {
currentPath := dh.path + dh.element + "/"
if dh.pattern == "*" && resourceMap[dh.element] != nil {
@ -128,7 +128,7 @@ func (dh DefaultHandler) Handle(handler resourceElementHandler, resourceMap map[
return "", nil
}
//NewConditionAnchorHandler returns an instance of condition acnhor handler
// NewConditionAnchorHandler returns an instance of condition acnhor handler
func NewConditionAnchorHandler(anchor string, pattern interface{}, path string) ValidationHandler {
return ConditionAnchorHandler{
anchor: anchor,
@ -137,14 +137,14 @@ func NewConditionAnchorHandler(anchor string, pattern interface{}, path string)
}
}
//ConditionAnchorHandler provides handler for condition anchor
// ConditionAnchorHandler provides handler for condition anchor
type ConditionAnchorHandler struct {
anchor string
pattern interface{}
path string
}
//Handle processed condition anchor
// Handle processed condition anchor
func (ch ConditionAnchorHandler) Handle(handler resourceElementHandler, resourceMap map[string]interface{}, originPattern interface{}, ac *common.AnchorKey) (string, error) {
anchorKey, _ := commonAnchors.RemoveAnchor(ch.anchor)
currentPath := ch.path + anchorKey + "/"
@ -162,7 +162,7 @@ func (ch ConditionAnchorHandler) Handle(handler resourceElementHandler, resource
return "", nil
}
//NewGlobalAnchorHandler returns an instance of condition acnhor handler
// NewGlobalAnchorHandler returns an instance of condition acnhor handler
func NewGlobalAnchorHandler(anchor string, pattern interface{}, path string) ValidationHandler {
return GlobalAnchorHandler{
anchor: anchor,
@ -171,14 +171,14 @@ func NewGlobalAnchorHandler(anchor string, pattern interface{}, path string) Val
}
}
//GlobalAnchorHandler provides handler for global condition anchor
// GlobalAnchorHandler provides handler for global condition anchor
type GlobalAnchorHandler struct {
anchor string
pattern interface{}
path string
}
//Handle processed global condition anchor
// Handle processed global condition anchor
func (gh GlobalAnchorHandler) Handle(handler resourceElementHandler, resourceMap map[string]interface{}, originPattern interface{}, ac *common.AnchorKey) (string, error) {
anchorKey, _ := commonAnchors.RemoveAnchor(gh.anchor)
currentPath := gh.path + anchorKey + "/"
@ -195,7 +195,7 @@ func (gh GlobalAnchorHandler) Handle(handler resourceElementHandler, resourceMap
return "", nil
}
//NewExistenceHandler returns existence handler
// NewExistenceHandler returns existence handler
func NewExistenceHandler(anchor string, pattern interface{}, path string) ValidationHandler {
return ExistenceHandler{
anchor: anchor,
@ -204,14 +204,14 @@ func NewExistenceHandler(anchor string, pattern interface{}, path string) Valida
}
}
//ExistenceHandler provides handlers to process exitence anchor handler
// ExistenceHandler provides handlers to process exitence anchor handler
type ExistenceHandler struct {
anchor string
pattern interface{}
path string
}
//Handle processes the existence anchor handler
// Handle processes the existence anchor handler
func (eh ExistenceHandler) Handle(handler resourceElementHandler, resourceMap map[string]interface{}, originPattern interface{}, ac *common.AnchorKey) (string, error) {
// skip is used by existence anchor to not process further if condition is not satisfied
anchorKey, _ := commonAnchors.RemoveAnchor(eh.anchor)
@ -261,7 +261,7 @@ func validateExistenceListResource(handler resourceElementHandler, resourceList
return path, fmt.Errorf("existence anchor validation failed at path %s", path)
}
//GetAnchorsResourcesFromMap returns map of anchors
// GetAnchorsResourcesFromMap returns map of anchors
func GetAnchorsResourcesFromMap(patternMap map[string]interface{}) (map[string]interface{}, map[string]interface{}) {
anchors := map[string]interface{}{}
resources := map[string]interface{}{}

View file

@ -26,7 +26,7 @@ func NewConditionalAnchorError(msg string) ValidateAnchorError {
}
}
// IsConditionAnchorError ...
// IsConditionAnchorError checks if the error is a conditional anchor error
func (e ValidateAnchorError) IsConditionAnchorError() bool {
return e.Err == ConditionalAnchorErr
}
@ -39,16 +39,17 @@ func NewGlobalAnchorError(msg string) ValidateAnchorError {
}
}
// IsConditionAnchorError ...
// IsGlobalAnchorError checks if the error is a global anchor error
func (e ValidateAnchorError) IsGlobalAnchorError() bool {
return e.Err == GlobalAnchorErr
}
// IsNil ...
// IsNil checks if the error isn't populated
func (e ValidateAnchorError) IsNil() bool {
return e == ValidateAnchorError{}
}
// Error returns an error instance of the anchor error
func (e ValidateAnchorError) Error() error {
return errors.New(e.Message)
}

View file

@ -99,7 +99,7 @@ func (ctx *Context) AddJSON(dataRaw []byte) error {
return nil
}
// AddJSON merges json data
// AddJSONObject merges json data
func (ctx *Context) AddJSONObject(jsonData interface{}) error {
jsonBytes, err := json.Marshal(jsonData)
if err != nil {

View file

@ -2,13 +2,14 @@ package engine
import (
"encoding/json"
"testing"
kyverno "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/cosign"
"github.com/kyverno/kyverno/pkg/engine/context"
"github.com/kyverno/kyverno/pkg/engine/response"
"github.com/kyverno/kyverno/pkg/engine/utils"
"gotest.tools/assert"
"testing"
)
var test_policy_good = `{

View file

@ -336,7 +336,14 @@ func jpfSplit(arguments []interface{}) (interface{}, error) {
return nil, err
}
return strings.Split(str.String(), sep.String()), nil
split := strings.Split(str.String(), sep.String())
arr := make([]interface{}, len(split))
for i, v := range split {
arr[i] = v
}
return arr, nil
}
func jpRegexReplaceAll(arguments []interface{}) (interface{}, error) {

View file

@ -128,7 +128,7 @@ func TestJMESPathFunctions_Split(t *testing.T) {
result, err := jp.Search("")
assert.NilError(t, err)
split, ok := result.([]string)
split, ok := result.([]interface{})
assert.Assert(t, ok)
assert.Equal(t, split[0], "Hello")
assert.Equal(t, split[1], "Gophers")

View file

@ -3,8 +3,9 @@ package engine
import (
"bytes"
"encoding/json"
"gotest.tools/assert"
"testing"
"gotest.tools/assert"
)
func Test_parseMultilineBlockBody(t *testing.T) {

View file

@ -1,4 +1,4 @@
package json_utils
package jsonutils
import (
"fmt"

View file

@ -1,4 +1,4 @@
package json_utils
package jsonutils
import (
"encoding/json"

View file

@ -2,9 +2,10 @@ package mutate
import (
"fmt"
"github.com/kyverno/kyverno/pkg/engine/response"
"testing"
"github.com/kyverno/kyverno/pkg/engine/response"
"github.com/ghodss/yaml"
assert "github.com/stretchr/testify/assert"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"

View file

@ -1,9 +1,10 @@
package mutate
import (
"github.com/kyverno/kyverno/pkg/engine/response"
"testing"
"github.com/kyverno/kyverno/pkg/engine/response"
"gotest.tools/assert"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"sigs.k8s.io/controller-runtime/pkg/log"

View file

@ -99,7 +99,7 @@ func Mutate(policyContext *PolicyContext) (resp *response.EngineResponse) {
if rule.Mutation.ForEachMutation != nil {
ruleResp, patchedResource = mutateForEachResource(ruleCopy, policyContext, patchedResource, logger)
} else {
err, mutateResp := mutateResource(ruleCopy, policyContext.JSONContext, patchedResource, logger, 0)
mutateResp, err := mutateResource(ruleCopy, policyContext.JSONContext, patchedResource, logger, 0)
if err != nil {
if mutateResp.skip {
ruleResp = ruleResponse(&policy.Spec.Rules[i], utils.Mutation, err.Error(), response.RuleStatusSkip)
@ -174,7 +174,7 @@ func mutateForEachResource(rule *kyverno.Rule, ctx *PolicyContext, resource unst
}
var skip = false
err, mutateResp := mutateResource(rule, ctx.JSONContext, patchedResource, logger, foreachIndex)
mutateResp, err := mutateResource(rule, ctx.JSONContext, patchedResource, logger, foreachIndex)
if err != nil && !skip {
return ruleResponse(rule, utils.Mutation, err.Error(), response.RuleStatusError), resource
}
@ -204,7 +204,7 @@ type mutateResponse struct {
message string
}
func mutateResource(rule *kyverno.Rule, ctx *context.Context, resource unstructured.Unstructured, logger logr.Logger, foreachIndex int) (error, *mutateResponse) {
func mutateResource(rule *kyverno.Rule, ctx *context.Context, resource unstructured.Unstructured, logger logr.Logger, foreachIndex int) (*mutateResponse, error) {
mutateResp := &mutateResponse{false, unstructured.Unstructured{}, nil, ""}
// Pre-conditions checks for the list of foreach rules should ideally be performed once.
@ -212,22 +212,22 @@ func mutateResource(rule *kyverno.Rule, ctx *context.Context, resource unstructu
// Also, the foreach index parameter should be removed and a set of patches should be passed in.
anyAllConditions, err := variables.SubstituteAllInPreconditions(logger, ctx, rule.AnyAllConditions)
if err != nil {
return errors.Wrapf(err, "failed to substitute vars in preconditions"), mutateResp
return mutateResp, errors.Wrapf(err, "failed to substitute vars in preconditions")
}
copyConditions, err := transformConditions(anyAllConditions)
if err != nil {
return errors.Wrapf(err, "failed to load context"), mutateResp
return mutateResp, errors.Wrapf(err, "failed to load context")
}
if !variables.EvaluateConditions(logger, ctx, copyConditions) {
mutateResp.skip = true
return fmt.Errorf("preconditions mismatch"), mutateResp
return mutateResp, fmt.Errorf("preconditions mismatch")
}
updatedRule, err := variables.SubstituteAllInRule(logger, ctx, *rule)
if err != nil {
return errors.Wrapf(err, "variable substitution failed"), mutateResp
return mutateResp, errors.Wrapf(err, "variable substitution failed")
}
mutation := updatedRule.Mutation.DeepCopy()
@ -238,7 +238,7 @@ func mutateResource(rule *kyverno.Rule, ctx *context.Context, resource unstructu
// - overlay pattern does not match the resource conditions
if resp.Patches == nil {
mutateResp.skip = true
return fmt.Errorf("resource does not match pattern"), mutateResp
return mutateResp, fmt.Errorf("resource does not match pattern")
}
mutateResp.skip = false
@ -252,7 +252,7 @@ func mutateResource(rule *kyverno.Rule, ctx *context.Context, resource unstructu
logger.Error(err, "failed to update resource in the JSON context")
}
return nil, mutateResp
return mutateResp, nil
}
func startMutateResultResponse(resp *response.EngineResponse, policy kyverno.ClusterPolicy, resource unstructured.Unstructured) {

View file

@ -1,5 +1,9 @@
package operator
import (
"regexp"
)
// Operator is string alias that represents selection operators enum
type Operator string
@ -16,6 +20,10 @@ const (
More Operator = ">"
// Less stands for <
Less Operator = "<"
// InRange stands for -
InRange Operator = "-"
// NotInRange stands for !-
NotInRange Operator = "!-"
)
//ReferenceSign defines the operator for anchor reference
@ -47,5 +55,13 @@ func GetOperatorFromStringPattern(pattern string) Operator {
return NotEqual
}
if match, _ := regexp.Match(`^(\d+(\.\d+)?)([^-]*)!-(\d+(\.\d+)?)([^-]*)$`, []byte(pattern)); match {
return NotInRange
}
if match, _ := regexp.Match(`^(\d+(\.\d+)?)([^-]*)-(\d+(\.\d+)?)([^-]*)$`, []byte(pattern)); match {
return InRange
}
return Equal
}

View file

@ -1,8 +1,9 @@
package operator
import (
"gotest.tools/assert"
"testing"
"gotest.tools/assert"
)
func TestGetOperatorFromStringPattern_OneChar(t *testing.T) {
@ -16,3 +17,19 @@ func TestGetOperatorFromStringPattern_EmptyString(t *testing.T) {
func TestGetOperatorFromStringPattern_OnlyOperator(t *testing.T) {
assert.Equal(t, GetOperatorFromStringPattern(">="), MoreEqual)
}
func TestGetOperatorFromStringPattern_RangeOperator(t *testing.T) {
assert.Equal(t, GetOperatorFromStringPattern("0-1"), InRange)
assert.Equal(t, GetOperatorFromStringPattern("0Mi-1024Mi"), InRange)
assert.Equal(t, GetOperatorFromStringPattern("0!-1"), NotInRange)
assert.Equal(t, GetOperatorFromStringPattern("0Mi!-1024Mi"), NotInRange)
assert.Equal(t, GetOperatorFromStringPattern("text1024Mi-2048Mi"), Equal)
assert.Equal(t, GetOperatorFromStringPattern("test-value"), Equal)
assert.Equal(t, GetOperatorFromStringPattern("value-*"), Equal)
assert.Equal(t, GetOperatorFromStringPattern("text1024Mi!-2048Mi"), Equal)
assert.Equal(t, GetOperatorFromStringPattern("test!-value"), Equal)
assert.Equal(t, GetOperatorFromStringPattern("value!-*"), Equal)
}

View file

@ -1,9 +1,10 @@
package response
import (
"testing"
"gopkg.in/yaml.v2"
"gotest.tools/assert"
"testing"
)
var sourceYAML = `

View file

@ -81,17 +81,17 @@ func getRuleStatus(s string) (*RuleStatus, error) {
return nil, fmt.Errorf("invalid status: %s", s)
}
func (v *RuleStatus) UnmarshalYAML(unmarshal func(interface{}) error) error {
var s string
if err := unmarshal(&s); err != nil {
func (s *RuleStatus) UnmarshalYAML(unmarshal func(interface{}) error) error {
var str string
if err := unmarshal(&str); err != nil {
return err
}
statusVal, err := getRuleStatus(s)
statusVal, err := getRuleStatus(str)
if err != nil {
return err
}
*v = *statusVal
*s = *statusVal
return nil
}

View file

@ -2,12 +2,13 @@ package utils
import (
"fmt"
"strconv"
"strings"
jsonpatch "github.com/evanphx/json-patch/v5"
commonAnchor "github.com/kyverno/kyverno/pkg/engine/anchor/common"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"sigs.k8s.io/controller-runtime/pkg/log"
"strconv"
"strings"
)
//RuleType defines the type for rule

View file

@ -177,17 +177,45 @@ func checkForAndConditionsAndValidate(log logr.Logger, value interface{}, patter
// Handler for single pattern value during validation process
// Detects if pattern has a number
func validateValueWithStringPattern(log logr.Logger, value interface{}, pattern string) bool {
operatorVariable := operator.GetOperatorFromStringPattern(pattern)
operator := operator.GetOperatorFromStringPattern(pattern)
pattern = pattern[len(operator):]
// Upon encountering InRange operator split the string by `-` and basically
// verify the result of (x >= leftEndpoint & x <= rightEndpoint)
if operatorVariable == operator.InRange {
endpoints := strings.Split(pattern, "-")
leftEndpoint, rightEndpoint := endpoints[0], endpoints[1]
gt := validateValueWithStringPattern(log, value, fmt.Sprintf(">=%s", leftEndpoint))
if !gt {
return false
}
pattern = fmt.Sprintf("<=%s", rightEndpoint)
operatorVariable = operator.LessEqual
}
// Upon encountering NotInRange operator split the string by `!-` and basically
// verify the result of (x < leftEndpoint | x > rightEndpoint)
if operatorVariable == operator.NotInRange {
endpoints := strings.Split(pattern, "!-")
leftEndpoint, rightEndpoint := endpoints[0], endpoints[1]
lt := validateValueWithStringPattern(log, value, fmt.Sprintf("<%s", leftEndpoint))
if lt {
return true
}
pattern = fmt.Sprintf(">%s", rightEndpoint)
operatorVariable = operator.More
}
pattern = pattern[len(operatorVariable):]
pattern = strings.TrimSpace(pattern)
number, str := getNumberAndStringPartsFromPattern(pattern)
if number == "" {
return validateString(log, value, str, operator)
return validateString(log, value, str, operatorVariable)
}
return validateNumberWithStr(log, value, pattern, operator)
return validateNumberWithStr(log, value, pattern, operatorVariable)
}
// Handler for string values

View file

@ -310,6 +310,42 @@ func TestValidateValueWithStringPattern_WithSpace(t *testing.T) {
assert.Assert(t, validateValueWithStringPattern(log.Log, 4, ">= 3"))
}
func TestValidateValueWithStringPattern_Ranges(t *testing.T) {
assert.Assert(t, validateValueWithStringPattern(log.Log, 0, "0-2"))
assert.Assert(t, validateValueWithStringPattern(log.Log, 1, "0-2"))
assert.Assert(t, validateValueWithStringPattern(log.Log, 2, "0-2"))
assert.Assert(t, !validateValueWithStringPattern(log.Log, 3, "0-2"))
assert.Assert(t, validateValueWithStringPattern(log.Log, 0, "10!-20"))
assert.Assert(t, !validateValueWithStringPattern(log.Log, 15, "10!-20"))
assert.Assert(t, validateValueWithStringPattern(log.Log, 25, "10!-20"))
assert.Assert(t, !validateValueWithStringPattern(log.Log, 0, "0.00001-2.00001"))
assert.Assert(t, validateValueWithStringPattern(log.Log, 1, "0.00001-2.00001"))
assert.Assert(t, validateValueWithStringPattern(log.Log, 2, "0.00001-2.00001"))
assert.Assert(t, !validateValueWithStringPattern(log.Log, 2.0001, "0.00001-2.00001"))
assert.Assert(t, validateValueWithStringPattern(log.Log, 0, "0.00001!-2.00001"))
assert.Assert(t, !validateValueWithStringPattern(log.Log, 1, "0.00001!-2.00001"))
assert.Assert(t, !validateValueWithStringPattern(log.Log, 2, "0.00001!-2.00001"))
assert.Assert(t, validateValueWithStringPattern(log.Log, 2.0001, "0.00001!-2.00001"))
assert.Assert(t, validateValueWithStringPattern(log.Log, 2, "2-2"))
assert.Assert(t, !validateValueWithStringPattern(log.Log, 2, "2!-2"))
assert.Assert(t, validateValueWithStringPattern(log.Log, 2.99999, "2.99998-3"))
assert.Assert(t, validateValueWithStringPattern(log.Log, 2.99997, "2.99998!-3"))
assert.Assert(t, validateValueWithStringPattern(log.Log, 3.00001, "2.99998!-3"))
assert.Assert(t, validateValueWithStringPattern(log.Log, "256Mi", "128Mi-512Mi"))
assert.Assert(t, !validateValueWithStringPattern(log.Log, "1024Mi", "128Mi-512Mi"))
assert.Assert(t, !validateValueWithStringPattern(log.Log, "64Mi", "128Mi-512Mi"))
assert.Assert(t, !validateValueWithStringPattern(log.Log, "256Mi", "128Mi!-512Mi"))
assert.Assert(t, validateValueWithStringPattern(log.Log, "1024Mi", "128Mi!-512Mi"))
assert.Assert(t, validateValueWithStringPattern(log.Log, "64Mi", "128Mi!-512Mi"))
}
func TestValidateNumberWithStr_LessFloatAndInt(t *testing.T) {
assert.Assert(t, validateNumberWithStr(log.Log, 7.00001, "7.000001", operator.More))
assert.Assert(t, validateNumberWithStr(log.Log, 7.00001, "7", operator.NotEqual))

View file

@ -425,7 +425,8 @@ func isSameRuleResponse(r1 *response.RuleResponse, r2 *response.RuleResponse) bo
func (v *validator) validatePatterns(resource unstructured.Unstructured) *response.RuleResponse {
if v.pattern != nil {
if err := validate.MatchPattern(v.log, resource.Object, v.pattern); err != nil {
if pe, ok := err.(*validate.PatternError); ok {
pe, ok := err.(*validate.PatternError)
if ok {
v.log.V(3).Info("validation error", "path", pe.Path, "error", err.Error())
if pe.Skip {
@ -437,9 +438,9 @@ func (v *validator) validatePatterns(resource unstructured.Unstructured) *respon
}
return ruleResponse(v.rule, utils.Validation, v.buildErrorMessage(err, pe.Path), response.RuleStatusFail)
} else {
return ruleResponse(v.rule, utils.Validation, v.buildErrorMessage(err, pe.Path), response.RuleStatusError)
}
return ruleResponse(v.rule, utils.Validation, v.buildErrorMessage(err, pe.Path), response.RuleStatusError)
}
v.log.V(4).Info("successfully processed rule")

File diff suppressed because it is too large Load diff

View file

@ -31,7 +31,7 @@ func (allin AllInHandler) Evaluate(key, value interface{}) bool {
case []interface{}:
var stringSlice []string
for _, v := range typedKey {
stringSlice = append(stringSlice, v.(string))
stringSlice = append(stringSlice, fmt.Sprint(v))
}
return allin.validateValueWithStringSetPattern(stringSlice, value)
default:
@ -70,11 +70,7 @@ func allSetExistsInArray(key []string, value interface{}, log logr.Logger, allNo
case []interface{}:
var valueSlice []string
for _, val := range valuesAvailable {
v, ok := val.(string)
if !ok {
return true, false
}
valueSlice = append(valueSlice, v)
valueSlice = append(valueSlice, fmt.Sprint(val))
}
if allNotIn {
return false, isAllNotIn(key, valueSlice)
@ -114,12 +110,7 @@ func isAllIn(key []string, value []string) bool {
}
}
}
if found == len(key) {
return true
} else {
return false
}
return found == len(key)
}
// isAllNotIn checks if all the values in S1 are not in S2

View file

@ -26,10 +26,12 @@ func (allnin AllNotInHandler) Evaluate(key, value interface{}) bool {
switch typedKey := key.(type) {
case string:
return allnin.validateValueWithStringPattern(typedKey, value)
case int, int32, int64, float32, float64:
return allnin.validateValueWithStringPattern(fmt.Sprint(typedKey), value)
case []interface{}:
var stringSlice []string
for _, v := range typedKey {
stringSlice = append(stringSlice, v.(string))
stringSlice = append(stringSlice, fmt.Sprint(v))
}
return allnin.validateValueWithStringSetPattern(stringSlice, value)
default:

View file

@ -28,10 +28,12 @@ func (anyin AnyInHandler) Evaluate(key, value interface{}) bool {
switch typedKey := key.(type) {
case string:
return anyin.validateValueWithStringPattern(typedKey, value)
case int, int32, int64, float32, float64:
return anyin.validateValueWithStringPattern(fmt.Sprint(typedKey), value)
case []interface{}:
var stringSlice []string
for _, v := range typedKey {
stringSlice = append(stringSlice, v.(string))
stringSlice = append(stringSlice, fmt.Sprint(v))
}
return anyin.validateValueWithStringSetPattern(stringSlice, value)
default:
@ -70,11 +72,7 @@ func anySetExistsInArray(key []string, value interface{}, log logr.Logger, anyNo
case []interface{}:
var valueSlice []string
for _, val := range valuesAvailable {
v, ok := val.(string)
if !ok {
return true, false
}
valueSlice = append(valueSlice, v)
valueSlice = append(valueSlice, fmt.Sprint(val))
}
if anyNotIn {
return false, isAnyNotIn(key, valueSlice)
@ -126,11 +124,7 @@ func isAnyNotIn(key []string, value []string) bool {
}
}
}
if found < len(key) {
return true
} else {
return false
}
return found < len(key)
}
func (anyin AnyInHandler) validateValueWithBoolPattern(_ bool, _ interface{}) bool {

View file

@ -26,10 +26,12 @@ func (anynin AnyNotInHandler) Evaluate(key, value interface{}) bool {
switch typedKey := key.(type) {
case string:
return anynin.validateValueWithStringPattern(typedKey, value)
case int, int32, int64, float32, float64:
return anynin.validateValueWithStringPattern(fmt.Sprint(typedKey), value)
case []interface{}:
var stringSlice []string
for _, v := range typedKey {
stringSlice = append(stringSlice, v.(string))
stringSlice = append(stringSlice, fmt.Sprint(v))
}
return anynin.validateValueWithStringSetPattern(stringSlice, value)
default:

View file

@ -7,6 +7,7 @@ import (
"strconv"
"github.com/minio/pkg/wildcard"
"k8s.io/apimachinery/pkg/api/resource"
"github.com/go-logr/logr"
"github.com/kyverno/kyverno/pkg/engine/context"
@ -67,6 +68,26 @@ func (eh EqualHandler) validateValueWithMapPattern(key map[string]interface{}, v
}
func (eh EqualHandler) validateValueWithStringPattern(key string, value interface{}) bool {
// We need to check duration first as it's the only type that can be compared to a different type.
durationKey, durationValue, err := parseDuration(key, value)
if err == nil {
return durationKey.Seconds() == durationValue.Seconds()
}
// Attempt to extract resource quantity from string.
resourceKey, err := resource.ParseQuantity(key)
if err == nil {
switch typedValue := value.(type) {
case string:
resourceValue, err := resource.ParseQuantity(typedValue)
if err != nil {
eh.log.Error(fmt.Errorf("parse error: "), "Failed to parse value type doesn't match key type")
return false
}
return resourceKey.Equal(resourceValue)
}
}
if val, ok := value.(string); ok {
return wildcard.Match(val, key)
}

View file

@ -10,8 +10,9 @@ import (
"github.com/kyverno/kyverno/pkg/engine/context"
)
// deprecated
//NewInHandler returns handler to manage In operations
// NewInHandler returns handler to manage In operations
//
// Deprecated: Use `NewAllInHandler` or `NewAnyInHandler` instead
func NewInHandler(log logr.Logger, ctx context.EvalInterface) OperatorHandler {
return InHandler{
ctx: ctx,
@ -19,17 +20,19 @@ func NewInHandler(log logr.Logger, ctx context.EvalInterface) OperatorHandler {
}
}
//InHandler provides implementation to handle In Operator
// InHandler provides implementation to handle In Operator
type InHandler struct {
ctx context.EvalInterface
log logr.Logger
}
//Evaluate evaluates expression with In Operator
// Evaluate evaluates expression with In Operator
func (in InHandler) Evaluate(key, value interface{}) bool {
switch typedKey := key.(type) {
case string:
return in.validateValueWithStringPattern(typedKey, value)
case int, int32, int64, float32, float64:
return in.validateValueWithStringPattern(fmt.Sprint(typedKey), value)
case []interface{}:
var stringSlice []string
for _, v := range typedKey {
@ -60,18 +63,12 @@ func keyExistsInArray(key string, value interface{}, log logr.Logger) (invalidTy
case []interface{}:
for _, val := range valuesAvailable {
v, ok := val.(string)
if !ok {
return true, false
}
if ok && wildcard.Match(key, v) {
if wildcard.Match(key, fmt.Sprint(val)) {
return false, true
}
}
case string:
if wildcard.Match(valuesAvailable, key) {
return false, true
}

View file

@ -7,6 +7,7 @@ import (
"strconv"
"github.com/minio/pkg/wildcard"
"k8s.io/apimachinery/pkg/api/resource"
"github.com/go-logr/logr"
"github.com/kyverno/kyverno/pkg/engine/context"
@ -55,7 +56,7 @@ func (neh NotEqualHandler) validateValueWithSlicePattern(key []interface{}, valu
return !reflect.DeepEqual(key, val)
}
neh.log.Info("Expected type []interface{}", "value", value, "type", fmt.Sprintf("%T", value))
return false
return true
}
func (neh NotEqualHandler) validateValueWithMapPattern(key map[string]interface{}, value interface{}) bool {
@ -63,16 +64,36 @@ func (neh NotEqualHandler) validateValueWithMapPattern(key map[string]interface{
return !reflect.DeepEqual(key, val)
}
neh.log.Info("Expected type map[string]interface{}", "value", value, "type", fmt.Sprintf("%T", value))
return false
return true
}
func (neh NotEqualHandler) validateValueWithStringPattern(key string, value interface{}) bool {
// We need to check duration first as it's the only type that can be compared to a different type.
durationKey, durationValue, err := parseDuration(key, value)
if err == nil {
return durationKey.Seconds() != durationValue.Seconds()
}
// Attempt to extract resource quantity from string.
resourceKey, err := resource.ParseQuantity(key)
if err == nil {
switch typedValue := value.(type) {
case string:
resourceValue, err := resource.ParseQuantity(typedValue)
if err != nil {
neh.log.Error(fmt.Errorf("parse error: "), "Failed to parse value type doesn't match key type")
return false
}
return !resourceKey.Equal(resourceValue)
}
}
if val, ok := value.(string); ok {
return !wildcard.Match(val, key)
}
neh.log.Info("Expected type string", "value", value, "type", fmt.Sprintf("%T", value))
return false
return true
}
func (neh NotEqualHandler) validateValueWithFloatPattern(key float64, value interface{}) bool {
@ -96,21 +117,21 @@ func (neh NotEqualHandler) validateValueWithFloatPattern(key float64, value inte
float64Num, err := strconv.ParseFloat(typedValue, 64)
if err != nil {
neh.log.Error(err, "Failed to parse float64 from string")
return false
return true
}
return float64Num != key
default:
neh.log.Info("Expected type float", "value", value, "type", fmt.Sprintf("%T", value))
return false
return true
}
return false
return true
}
func (neh NotEqualHandler) validateValueWithBoolPattern(key bool, value interface{}) bool {
typedValue, ok := value.(bool)
if !ok {
neh.log.Info("Expected type bool", "value", value, "type", fmt.Sprintf("%T", value))
return false
return true
}
return key != typedValue
}
@ -133,11 +154,11 @@ func (neh NotEqualHandler) validateValueWithIntPattern(key int64, value interfac
int64Num, err := strconv.ParseInt(typedValue, 10, 64)
if err != nil {
neh.log.Error(err, "Failed to parse int64 from string")
return false
return true
}
return int64Num != key
default:
neh.log.Info("Expected type int", "value", value, "type", fmt.Sprintf("%T", value))
return false
return true
}
}

View file

@ -7,8 +7,9 @@ import (
"github.com/kyverno/kyverno/pkg/engine/context"
)
// deprecated
//NewNotInHandler returns handler to manage NotIn operations
//
// Deprecated: Use `NewAllNotInHandler` or `NewAnyNotInHandler` instead
func NewNotInHandler(log logr.Logger, ctx context.EvalInterface) OperatorHandler {
return NotInHandler{
ctx: ctx,
@ -16,17 +17,19 @@ func NewNotInHandler(log logr.Logger, ctx context.EvalInterface) OperatorHandler
}
}
//NotInHandler provides implementation to handle NotIn Operator
// NotInHandler provides implementation to handle NotIn Operator
type NotInHandler struct {
ctx context.EvalInterface
log logr.Logger
}
//Evaluate evaluates expression with NotIn Operator
// Evaluate evaluates expression with NotIn Operator
func (nin NotInHandler) Evaluate(key, value interface{}) bool {
switch typedKey := key.(type) {
case string:
return nin.validateValueWithStringPattern(typedKey, value)
case int, int32, int64, float32, float64:
return nin.validateValueWithStringPattern(fmt.Sprint(typedKey), value)
case []interface{}:
var stringSlice []string
for _, v := range typedKey {

View file

@ -3,7 +3,6 @@ package operator
import (
"fmt"
"strconv"
"time"
"github.com/go-logr/logr"
kyverno "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
@ -38,8 +37,16 @@ func compareByCondition(key float64, value float64, op kyverno.ConditionOperator
return key <= value
case kyverno.LessThan:
return key < value
case kyverno.Equals:
return key == value
case kyverno.Equal:
return key == value
case kyverno.NotEquals:
return key != value
case kyverno.NotEqual:
return key != value
default:
(*log).Info(fmt.Sprintf("Expected operator, one of [GreaterThanOrEquals, GreaterThan, LessThanOrEquals, LessThan], found %s", op))
(*log).Info(fmt.Sprintf("Expected operator, one of [GreaterThanOrEquals, GreaterThan, LessThanOrEquals, LessThan, Equals, NotEquals], found %s", op))
return false
}
}
@ -69,6 +76,10 @@ func (noh NumericOperatorHandler) validateValueWithIntPattern(key int64, value i
case float64:
return compareByCondition(float64(key), typedValue, noh.condition, &noh.log)
case string:
durationKey, durationValue, err := parseDuration(key, value)
if err == nil {
return compareByCondition(float64(durationKey.Seconds()), float64(durationValue.Seconds()), noh.condition, &noh.log)
}
// extract float64 and (if that fails) then, int64 from the string
float64val, err := strconv.ParseFloat(typedValue, 64)
if err == nil {
@ -95,6 +106,10 @@ func (noh NumericOperatorHandler) validateValueWithFloatPattern(key float64, val
case float64:
return compareByCondition(key, typedValue, noh.condition, &noh.log)
case string:
durationKey, durationValue, err := parseDuration(key, value)
if err == nil {
return compareByCondition(float64(durationKey.Seconds()), float64(durationValue.Seconds()), noh.condition, &noh.log)
}
float64val, err := strconv.ParseFloat(typedValue, 64)
if err == nil {
return compareByCondition(key, float64val, noh.condition, &noh.log)
@ -128,7 +143,7 @@ func (noh NumericOperatorHandler) validateValueWithResourcePattern(key resource.
func (noh NumericOperatorHandler) validateValueWithStringPattern(key string, value interface{}) bool {
// We need to check duration first as it's the only type that can be compared to a different type
durationKey, durationValue, err := noh.parseDuration(key, value)
durationKey, durationValue, err := parseDuration(key, value)
if err == nil {
return compareByCondition(float64(durationKey.Seconds()), float64(durationValue.Seconds()), noh.condition, &noh.log)
}
@ -152,67 +167,6 @@ func (noh NumericOperatorHandler) validateValueWithStringPattern(key string, val
return false
}
func (noh NumericOperatorHandler) parseDuration(key, value interface{}) (*time.Duration, *time.Duration, error) {
var keyDuration *time.Duration
var valueDuration *time.Duration
var err error
// We need to first ensure at least one of the values is actually a duration string
switch typedKey := key.(type) {
case string:
duration, err := time.ParseDuration(typedKey)
if err == nil && key != "0" {
keyDuration = &duration
}
}
switch typedValue := value.(type) {
case string:
duration, err := time.ParseDuration(typedValue)
if err == nil && value != "0" {
valueDuration = &duration
}
}
if keyDuration == nil && valueDuration == nil {
return keyDuration, valueDuration, fmt.Errorf("neither value is a duration")
}
if keyDuration == nil {
var duration time.Duration
switch typedKey := key.(type) {
case int:
duration = time.Duration(typedKey) * time.Second
case int64:
duration = time.Duration(typedKey) * time.Second
case float64:
duration = time.Duration(typedKey) * time.Second
default:
return keyDuration, valueDuration, fmt.Errorf("no valid duration value")
}
keyDuration = &duration
}
if valueDuration == nil {
var duration time.Duration
switch typedValue := value.(type) {
case int:
duration = time.Duration(typedValue) * time.Second
case int64:
duration = time.Duration(typedValue) * time.Second
case float64:
duration = time.Duration(typedValue) * time.Second
default:
return keyDuration, valueDuration, fmt.Errorf("no valid duration value")
}
valueDuration = &duration
}
return keyDuration, valueDuration, err
}
// the following functions are unreachable because the key is strictly supposed to be numeric
// still the following functions are just created to make NumericOperatorHandler struct implement OperatorHandler interface
func (noh NumericOperatorHandler) validateValueWithBoolPattern(key bool, value interface{}) bool {

View file

@ -1,7 +1,9 @@
package operator
import (
"fmt"
"strings"
"time"
"github.com/go-logr/logr"
kyverno "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
@ -74,3 +76,64 @@ func CreateOperatorHandler(log logr.Logger, ctx context.EvalInterface, op kyvern
return nil
}
func parseDuration(key, value interface{}) (*time.Duration, *time.Duration, error) {
var keyDuration *time.Duration
var valueDuration *time.Duration
var err error
// We need to first ensure at least one of the values is actually a duration string.
switch typedKey := key.(type) {
case string:
duration, err := time.ParseDuration(typedKey)
if err == nil && key != "0" {
keyDuration = &duration
}
}
switch typedValue := value.(type) {
case string:
duration, err := time.ParseDuration(typedValue)
if err == nil && value != "0" {
valueDuration = &duration
}
}
if keyDuration == nil && valueDuration == nil {
return keyDuration, valueDuration, fmt.Errorf("neither value is a duration")
}
if keyDuration == nil {
var duration time.Duration
switch typedKey := key.(type) {
case int:
duration = time.Duration(typedKey) * time.Second
case int64:
duration = time.Duration(typedKey) * time.Second
case float64:
duration = time.Duration(typedKey) * time.Second
default:
return keyDuration, valueDuration, fmt.Errorf("no valid duration value")
}
keyDuration = &duration
}
if valueDuration == nil {
var duration time.Duration
switch typedValue := value.(type) {
case int:
duration = time.Duration(typedValue) * time.Second
case int64:
duration = time.Duration(typedValue) * time.Second
case float64:
duration = time.Duration(typedValue) * time.Second
default:
return keyDuration, valueDuration, fmt.Errorf("no valid duration value")
}
valueDuration = &duration
}
return keyDuration, valueDuration, err
}

View file

@ -13,7 +13,7 @@ import (
kyverno "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/engine/anchor/common"
"github.com/kyverno/kyverno/pkg/engine/context"
jsonUtils "github.com/kyverno/kyverno/pkg/engine/json-utils"
jsonUtils "github.com/kyverno/kyverno/pkg/engine/jsonutils"
"github.com/kyverno/kyverno/pkg/engine/operator"
)
@ -21,10 +21,10 @@ var RegexVariables = regexp.MustCompile(`^\{\{[^{}]*\}\}|[^\\]\{\{[^{}]*\}\}`)
var RegexEscpVariables = regexp.MustCompile(`\\\{\{[^{}]*\}\}`)
// Regex for '$(...)' at the beginning of the string, and 'x$(...)' where 'x' is not '\'
// RegexReferences is the Regex for '$(...)' at the beginning of the string, and 'x$(...)' where 'x' is not '\'
var RegexReferences = regexp.MustCompile(`^\$\(.[^\ ]*\)|[^\\]\$\(.[^\ ]*\)`)
// Regex for '\$(...)'
// RegexEscpReferences is the Regex for '\$(...)'
var RegexEscpReferences = regexp.MustCompile(`\\\$\(.[^\ ]*\)`)
var regexVariableInit = regexp.MustCompile(`^\{\{[^{}]*\}\}`)
@ -298,7 +298,7 @@ func substituteReferencesIfAny(log logr.Logger) jsonUtils.Action {
for _, v := range RegexReferences.FindAllString(value, -1) {
initial := v[:2] == `$(`
v_old := v
old := v
if !initial {
v = v[1:]
@ -321,15 +321,15 @@ func substituteReferencesIfAny(log logr.Logger) jsonUtils.Action {
log.V(3).Info("reference resolved", "reference", v, "value", resolvedReference, "path", data.Path)
if val, ok := resolvedReference.(string); ok {
replace_with := ""
replacement := ""
if !initial {
replace_with = string(v_old[0])
replacement = string(old[0])
}
replace_with += val
replacement += val
value = strings.Replace(value, v_old, replace_with, 1)
value = strings.Replace(value, old, replacement, 1)
continue
}
@ -370,7 +370,7 @@ func substituteVariablesIfAny(log logr.Logger, ctx context.EvalInterface, vr Var
for _, v := range vars {
initial := len(regexVariableInit.FindAllString(v, -1)) > 0
v_old := v
old := v
if !initial {
v = v[1:]
@ -406,7 +406,7 @@ func substituteVariablesIfAny(log logr.Logger, ctx context.EvalInterface, vr Var
prefix := ""
if !initial {
prefix = string(v_old[0])
prefix = string(old[0])
}
if value, err = substituteVarInPattern(prefix, originalPattern, v, substitutedVar); err != nil {
@ -524,7 +524,7 @@ func valFromReferenceToString(value interface{}, operator string) (string, error
func FindAndShiftReferences(log logr.Logger, value, shift, pivot string) string {
for _, reference := range RegexReferences.FindAllString(value, -1) {
initial := reference[:2] == `$(`
reference_old := reference
oldReference := reference
if !initial {
reference = reference[1:]
@ -542,15 +542,15 @@ func FindAndShiftReferences(log logr.Logger, value, shift, pivot string) string
}
shiftedReference := strings.Replace(reference, pivot, pivot+"/"+shift, -1)
replace_with := ""
replacement := ""
if !initial {
replace_with = string(reference_old[0])
replacement = string(oldReference[0])
}
replace_with += shiftedReference
replacement += shiftedReference
value = strings.Replace(value, reference_old, replace_with, 1)
value = strings.Replace(value, oldReference, replacement, 1)
}
return value

View file

@ -9,7 +9,7 @@ import (
v1 "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/engine/context"
ju "github.com/kyverno/kyverno/pkg/engine/json-utils"
ju "github.com/kyverno/kyverno/pkg/engine/jsonutils"
"gotest.tools/assert"
"sigs.k8s.io/controller-runtime/pkg/log"
)

View file

@ -435,7 +435,15 @@ func applyRule(log logr.Logger, client *dclient.Client, rule kyverno.Rule, resou
label["policy.kyverno.io/synchronize"] = "enable"
newResource.SetLabels(label)
if _, err := ValidateResourceWithPattern(logger, generatedObj.Object, rdata); err != nil {
if genAPIVersion == "" {
generatedResourceAPIVersion := generatedObj.GetAPIVersion()
newResource.SetAPIVersion(generatedResourceAPIVersion)
}
if genNamespace == "" {
newResource.SetNamespace("default")
}
if _, err := ValidateResourceWithPattern(logger, generatedObj.Object, newResource.Object); err != nil {
_, err = client.UpdateResource(genAPIVersion, genKind, genNamespace, newResource, false)
if err != nil {
logger.Error(err, "failed to update resource")

View file

@ -336,30 +336,37 @@ func checkMutateLogPath(mutateLogPath string) (mutateLogPathIsDir bool, err erro
// printReportOrViolation - printing policy report/violations
func printReportOrViolation(policyReport bool, rc *common.ResultCounts, resourcePaths []string, resourcesLen int, skipInvalidPolicies SkippedInvalidPolicies, stdin bool, pvInfos []policyreport.Info) {
divider := "----------------------------------------------------------------------"
if len(skipInvalidPolicies.skipped) > 0 {
fmt.Println("----------------------------------------------------------------------\nPolicies Skipped(as required variables are not provided by the users):")
fmt.Println(divider)
fmt.Println("Policies Skipped (as required variables are not provided by the user):")
for i, policyName := range skipInvalidPolicies.skipped {
fmt.Println(i+1, ". ", policyName)
fmt.Printf("%d. %s\n", i+1, policyName)
}
fmt.Println("----------------------------------------------------------------------")
fmt.Println(divider)
}
if len(skipInvalidPolicies.invalid) > 0 {
fmt.Println("----------------------------------------------------------------------\nInvalid Policies:")
fmt.Println(divider)
fmt.Println("Invalid Policies:")
for i, policyName := range skipInvalidPolicies.invalid {
fmt.Println(i+1, ". ", policyName)
fmt.Printf("%d. %s\n", i+1, policyName)
}
fmt.Println("----------------------------------------------------------------------")
fmt.Println(divider)
}
if policyReport {
resps := buildPolicyReports(pvInfos)
if len(resps) > 0 || resourcesLen == 0 {
fmt.Println("\n----------------------------------------------------------------------\nPOLICY REPORT:\n----------------------------------------------------------------------")
fmt.Println(divider)
fmt.Println("POLICY REPORT:")
fmt.Println(divider)
report, _ := generateCLIRaw(resps)
yamlReport, _ := yaml1.Marshal(report)
fmt.Println(string(yamlReport))
} else {
fmt.Println("----------------------------------------------------------------------\nPOLICY REPORT: skip generating policy report (no validate policy found/resource skipped)")
fmt.Println(divider)
fmt.Println("POLICY REPORT: skip generating policy report (no validate policy found/resource skipped)")
}
} else {
if !stdin {

View file

@ -81,10 +81,10 @@ func GetPolicies(paths []string) (policies []*v1.ClusterPolicy, errors []error)
err error
)
isHttpPath := IsHttpRegex.MatchString(path)
isHTTPPath := IsHTTPRegex.MatchString(path)
// path clean and retrieving file info can be possible if it's not an HTTP URL
if !isHttpPath {
if !isHTTPPath {
path = filepath.Clean(path)
fileDesc, err = os.Stat(path)
if err != nil {
@ -95,7 +95,7 @@ func GetPolicies(paths []string) (policies []*v1.ClusterPolicy, errors []error)
}
// apply file from a directory is possible only if the path is not HTTP URL
if !isHttpPath && fileDesc.IsDir() {
if !isHTTPPath && fileDesc.IsDir() {
files, err := ioutil.ReadDir(path)
if err != nil {
err := fmt.Errorf("failed to process %v: %v", path, err.Error())
@ -117,7 +117,7 @@ func GetPolicies(paths []string) (policies []*v1.ClusterPolicy, errors []error)
} else {
var fileBytes []byte
if isHttpPath {
if isHTTPPath {
// We accept here that a random URL might be called based on user provided input.
resp, err := http.Get(path) // #nosec
if err != nil {
@ -258,7 +258,7 @@ func PolicyHasNonAllowedVariables(policy v1.ClusterPolicy) error {
matchesAll := RegexVariables.FindAllStringSubmatch(string(ruleJSON), -1)
matchesAllowed := AllowedVariables.FindAllStringSubmatch(string(ruleJSON), -1)
if (len(matchesAll) > len(matchesAllowed)) && len(rule.Context) == 0 {
allowed := "{{request.*}}, {{element.*}}, {{serviceAccountName}}, {{serviceAccountNamespace}}, {{@}}, and context variables"
allowed := "{{request.*}}, {{element.*}}, {{serviceAccountName}}, {{serviceAccountNamespace}}, {{@}}, {{images.*}} and context variables"
return fmt.Errorf("rule \"%s\" has forbidden variables. Allowed variables are: %s", rule.Name, allowed)
}
}

View file

@ -220,7 +220,7 @@ func getFileBytes(path string) ([]byte, error) {
err error
)
if IsHttpRegex.MatchString(path) {
if IsHTTPRegex.MatchString(path) {
// We accept here that a random URL might be called based on user provided input.
resp, err := http.Get(path) // #nosec
if err != nil {

View file

@ -7,11 +7,11 @@ import (
// RegexVariables represents regex for '{{}}'
var RegexVariables = regexp.MustCompile(`\{\{[^{}]*\}\}`)
// AllowedVariables represents regex for {{request.}}, {{serviceAccountName}}, {{serviceAccountNamespace}}, {{@}} and functions e.g. {{divide(<num>,<num>))}}
var AllowedVariables = regexp.MustCompile(`\{\{\s*(request\.|serviceAccountName|serviceAccountNamespace|element\.|@|([a-z_0-9]+\())[^{}]*\}\}`)
// AllowedVariables represents regex for {{request.}}, {{serviceAccountName}}, {{serviceAccountNamespace}}, {{@}}, {{element.}}, {{images.}}
var AllowedVariables = regexp.MustCompile(`\{\{\s*(request\.|serviceAccountName|serviceAccountNamespace|element\.|@|images\.|([a-z_0-9]+\())[^{}]*\}\}`)
// AllowedVariables represents regex for {{request.}}, {{serviceAccountName}}, {{serviceAccountNamespace}}
// WildCardAllowedVariables represents regex for the allowed fields in wildcards
var WildCardAllowedVariables = regexp.MustCompile(`\{\{\s*(request\.|serviceAccountName|serviceAccountNamespace)[^{}]*\}\}`)
// IsHttpRegex represents regex for starts with http:// or https://
var IsHttpRegex = regexp.MustCompile("^(http|https)://")
// IsHTTPRegex represents regex for starts with http:// or https://
var IsHTTPRegex = regexp.MustCompile("^(http|https)://")

View file

@ -81,7 +81,7 @@ For validate policies
rule: <name>
resource: <name>
namespace: <name> (OPTIONAL)
kind: <name>
kind: <name>
result: <pass|fail|skip>
@ -101,7 +101,7 @@ Policy (Namespaced)
rule: <name>
resource: <name>
namespace: <name> (OPTIONAL)
kind: <name>
kind: <name>
patchedResource: <path>
result: <pass|fail|skip>
@ -126,7 +126,7 @@ ClusterPolicy (Cluster-wide)
Result descriptions:
pass --> The patched resource generated by Kyverno equals the patched resource provided by the user.
fail --> The patched resource generated by Kyverno is not equal to the patched resource provided by the user.
fail --> The patched resource generated by Kyverno is not equal to the patched resource provided by the user.
skip --> The rule is not applied.
For more information visit https://kyverno.io/docs/kyverno-cli/#test
@ -521,9 +521,9 @@ func isNamespacedPolicy(policyNames string) (bool, error) {
func getUserDefinedPolicyNameAndNamespace(policyName string) (string, string) {
if strings.Contains(policyName, "/") {
policy_n_ns := strings.Split(policyName, "/")
namespace := policy_n_ns[0]
policy := policy_n_ns[1]
parts := strings.Split(policyName, "/")
namespace := parts[0]
policy := parts[1]
return namespace, policy
}
return "", policyName

View file

@ -197,11 +197,11 @@ func addCacheHelper(rmr kyverno.ResourceFilter, m *pMap, rule kyverno.Rule, muta
}
}
func (pc *pMap) get(key PolicyType, gvk, namespace string) (names []string) {
pc.RLock()
defer pc.RUnlock()
func (m *pMap) get(key PolicyType, gvk, namespace string) (names []string) {
m.RLock()
defer m.RUnlock()
_, kind := common.GetKindFromGVK(gvk)
for _, policyName := range pc.kindDataMap[kind][key] {
for _, policyName := range m.kindDataMap[kind][key] {
ns, key, isNamespacedPolicy := policy2.ParseNamespacedPolicy(policyName)
if !isNamespacedPolicy && namespace == "" {
names = append(names, key)
@ -262,19 +262,19 @@ func removeCacheHelper(rmr kyverno.ResourceFilter, m *pMap, pName string) {
}
}
func (m *policyCache) getPolicyObject(key PolicyType, gvk string, nspace string) (policyObject []*kyverno.ClusterPolicy) {
func (pc *policyCache) getPolicyObject(key PolicyType, gvk string, nspace string) (policyObject []*kyverno.ClusterPolicy) {
_, kind := common.GetKindFromGVK(gvk)
policyNames := m.pMap.get(key, kind, nspace)
wildcardPolicies := m.pMap.get(key, "*", nspace)
policyNames := pc.pMap.get(key, kind, nspace)
wildcardPolicies := pc.pMap.get(key, "*", nspace)
policyNames = append(policyNames, wildcardPolicies...)
for _, policyName := range policyNames {
var policy *kyverno.ClusterPolicy
ns, key, isNamespacedPolicy := policy2.ParseNamespacedPolicy(policyName)
if !isNamespacedPolicy {
policy, _ = m.pLister.Get(key)
policy, _ = pc.pLister.Get(key)
} else {
if ns == nspace {
nspolicy, _ := m.npLister.Policies(ns).Get(key)
nspolicy, _ := pc.npLister.Policies(ns).Get(key)
policy = policy2.ConvertPolicyToClusterPolicy(nspolicy)
}
}

View file

@ -35,7 +35,7 @@ const (
deletedAnnotationResourceName string = "kyverno.io/delete.resource.name"
deletedAnnotationResourceKind string = "kyverno.io/delete.resource.kind"
// static value for PolicyReportResult.Source
// SourceValue is the static value for PolicyReportResult.Source
SourceValue = "Kyverno"
)

View file

@ -32,13 +32,13 @@ type Scenario struct {
TestCases []TestCase
}
//CaseT defines input and output for a case
// TestCase defines input and output for a case
type TestCase struct {
Input Input `yaml:"input"`
Expected Expected `yaml:"expected"`
}
//Input defines input for a test scenario
// Input defines input for a test scenario
type Input struct {
Policy string `yaml:"policy"`
Resource string `yaml:"resource"`

View file

@ -1,11 +1,12 @@
package testrunner
import (
"io/ioutil"
"testing"
"github.com/kyverno/kyverno/pkg/engine/response"
"gopkg.in/yaml.v3"
"gotest.tools/assert"
"io/ioutil"
"testing"
)
var sourceYAML = `

View file

@ -24,7 +24,7 @@ import (
var regexVersion = regexp.MustCompile(`v(\d+).(\d+).(\d+)\.*`)
//Contains Check if strint is contained in a list of string
// Contains checks if a string is contained in a list of string
func contains(list []string, element string, fn func(string, string) bool) bool {
for _, e := range list {
if fn(e, element) {
@ -44,12 +44,12 @@ func ContainsPod(list []string, element string) bool {
return false
}
//ContainsNamepace check if namespace satisfies any list of pattern(regex)
// ContainsNamepace check if namespace satisfies any list of pattern(regex)
func ContainsNamepace(patterns []string, ns string) bool {
return contains(patterns, ns, compareNamespaces)
}
//ContainsString check if the string is contains in a list
// ContainsString checks if the string is contained in the list
func ContainsString(list []string, element string) bool {
return contains(list, element, compareString)
}
@ -62,7 +62,7 @@ func compareString(str, name string) bool {
return str == name
}
//NewKubeClient returns a new kubernetes client
// NewKubeClient returns a new kubernetes client
func NewKubeClient(config *rest.Config) (kubernetes.Interface, error) {
kclient, err := kubernetes.NewForConfig(config)
if err != nil {
@ -160,7 +160,7 @@ func HigherThanKubernetesVersion(client *client.Client, log logr.Logger, major,
b, err := isVersionHigher(serverVersion.String(), major, minor, patch)
if err != nil {
logger.Error(err, "serverVersion", serverVersion)
logger.Error(err, "serverVersion", serverVersion.String())
return false
}
@ -168,27 +168,29 @@ func HigherThanKubernetesVersion(client *client.Client, log logr.Logger, major,
}
func isVersionHigher(version string, major int, minor int, patch int) (bool, error) {
groups := regexVersion.FindAllStringSubmatch(version, -1)
if len(groups) != 1 || len(groups[0]) != 4 {
groups := regexVersion.FindStringSubmatch(version)
if len(groups) != 4 {
return false, fmt.Errorf("invalid version %s. Expected {major}.{minor}.{patch}", version)
}
currentMajor, err := strconv.Atoi(groups[0][1])
currentMajor, err := strconv.Atoi(groups[1])
if err != nil {
return false, fmt.Errorf("failed to extract major version from %s", version)
}
currentMinor, err := strconv.Atoi(groups[0][2])
currentMinor, err := strconv.Atoi(groups[2])
if err != nil {
return false, fmt.Errorf("failed to extract minor version from %s", version)
}
currentPatch, err := strconv.Atoi(groups[0][3])
currentPatch, err := strconv.Atoi(groups[3])
if err != nil {
return false, fmt.Errorf("failed to extract minor version from %s", version)
}
if currentMajor <= major && currentMinor <= minor && currentPatch <= patch {
if currentMajor < major ||
(currentMajor == major && currentMinor < minor) ||
(currentMajor == major && currentMinor == minor && currentPatch <= patch) {
return false, nil
}
@ -212,7 +214,7 @@ func SliceContains(slice []string, values ...string) bool {
return false
}
// ApiextensionsJsonTOKyvernoConditions takes in user-provided conditions in abstract apiextensions.JSON form
// ApiextensionsJsonToKyvernoConditions takes in user-provided conditions in abstract apiextensions.JSON form
// and converts it into []kyverno.Condition or kyverno.AnyAllConditions according to its content.
// it also helps in validating the condtions as it returns an error when the conditions are provided wrongfully by the user.
func ApiextensionsJsonToKyvernoConditions(original apiextensions.JSON) (interface{}, error) {

View file

@ -91,6 +91,15 @@ func Test_higherVersion(t *testing.T) {
v, err = isVersionHigher("v1.5.9-rc2", 1, 5, 9)
assert.Assert(t, v == false && err == nil)
v, err = isVersionHigher("v1.5.9", 2, 1, 0)
assert.Assert(t, v == false && err == nil)
v, err = isVersionHigher("v2.1.0", 1, 5, 9)
assert.Assert(t, v == true && err == nil)
v, err = isVersionHigher("v1.5.9-x-v1.5.9.x", 1, 5, 8)
assert.Assert(t, v == true && err == nil)
}
func Test_ConvertResource(t *testing.T) {

View file

@ -175,11 +175,11 @@ func (wrc *Register) Remove(cleanUp chan<- struct{}) {
}
// +deprecated
// UpdateWebhookConfigurations updates resource webhook configurations dynamically
// base on the UPDATEs of Kyverno init-config ConfigMap
//
// it currently updates namespaceSelector only, can be extend to update other fields
// +deprecated
func (wrc *Register) UpdateWebhookConfigurations(configHandler config.Interface) {
logger := wrc.log.WithName("UpdateWebhookConfigurations")
for {
@ -622,12 +622,12 @@ func (wrc *Register) checkEndpoint() error {
}
kyverno := pods.Items[0]
podIp, _, err := unstructured.NestedString(kyverno.UnstructuredContent(), "status", "podIP")
podIP, _, err := unstructured.NestedString(kyverno.UnstructuredContent(), "status", "podIP")
if err != nil {
return fmt.Errorf("failed to extract pod IP: %v", err)
}
if podIp == "" {
if podIP == "" {
return fmt.Errorf("pod is not assigned to any node yet")
}
@ -637,7 +637,7 @@ func (wrc *Register) checkEndpoint() error {
}
for _, addr := range subset.Addresses {
if addr.IP == podIp {
if addr.IP == podIP {
wrc.log.Info("Endpoint ready", "ns", config.KyvernoNamespace, "name", config.KyvernoServiceName)
return nil
}

View file

@ -300,20 +300,14 @@ func (ws *WebhookServer) resourceMutation(request *v1beta1.AdmissionRequest) *v1
requestTime := time.Now().Unix()
kind := request.Kind.Kind
mutatePolicies := ws.pCache.GetPolicies(policycache.Mutate, kind, request.Namespace)
generatePolicies := ws.pCache.GetPolicies(policycache.Generate, kind, request.Namespace)
verifyImagesPolicies := ws.pCache.GetPolicies(policycache.VerifyImages, kind, request.Namespace)
if len(mutatePolicies) == 0 && len(generatePolicies) == 0 && len(verifyImagesPolicies) == 0 {
if len(mutatePolicies) == 0 && len(verifyImagesPolicies) == 0 {
logger.V(4).Info("no policies matched admission request")
if request.Operation == v1beta1.Update {
// handle generate source resource updates
go ws.handleUpdatesForGenerateRules(request, []*v1.ClusterPolicy{})
}
return successResponse(nil)
}
addRoles := containsRBACInfo(mutatePolicies, generatePolicies)
addRoles := containsRBACInfo(mutatePolicies)
policyContext, err := ws.buildPolicyContext(request, addRoles)
if err != nil {
logger.Error(err, "failed to build policy context")
@ -334,9 +328,6 @@ func (ws *WebhookServer) resourceMutation(request *v1beta1.AdmissionRequest) *v1
return failureResponse(err.Error())
}
newRequest = patchRequest(imagePatches, newRequest, logger)
ws.applyGeneratePolicies(newRequest, policyContext, generatePolicies, requestTime, logger)
var patches = append(mutatePatches, imagePatches...)
return successResponse(patches)
}
@ -355,11 +346,10 @@ func (ws *WebhookServer) buildPolicyContext(request *v1beta1.AdmissionRequest, a
}
if addRoles {
if roles, clusterRoles, err := userinfo.GetRoleRef(ws.rbLister, ws.crbLister, request, ws.configHandler); err != nil {
var err error
userRequestInfo.Roles, userRequestInfo.ClusterRoles, err = userinfo.GetRoleRef(ws.rbLister, ws.crbLister, request, ws.configHandler)
if err != nil {
return nil, errors.Wrap(err, "failed to fetch RBAC information for request")
} else {
userRequestInfo.Roles = roles
userRequestInfo.ClusterRoles = clusterRoles
}
}
@ -488,6 +478,7 @@ func (ws *WebhookServer) resourceValidation(request *v1beta1.AdmissionRequest) *
if request.Operation == v1beta1.Delete {
ws.handleDelete(request)
}
if excludeKyvernoResources(request.Kind.Kind) {
return successResponse(nil)
}
@ -500,9 +491,15 @@ func (ws *WebhookServer) resourceValidation(request *v1beta1.AdmissionRequest) *
// Get namespace policies from the cache for the requested resource namespace
nsPolicies := ws.pCache.GetPolicies(policycache.ValidateEnforce, kind, request.Namespace)
policies = append(policies, nsPolicies...)
generatePolicies := ws.pCache.GetPolicies(policycache.Generate, kind, request.Namespace)
if len(generatePolicies) == 0 && request.Operation == v1beta1.Update {
// handle generate source resource updates
go ws.handleUpdatesForGenerateRules(request, []*v1.ClusterPolicy{})
}
var roles, clusterRoles []string
if containsRBACInfo(policies) {
if containsRBACInfo(policies, generatePolicies) {
var err error
roles, clusterRoles, err = userinfo.GetRoleRef(ws.rbLister, ws.crbLister, request, ws.configHandler)
if err != nil {
@ -561,6 +558,9 @@ func (ws *WebhookServer) resourceValidation(request *v1beta1.AdmissionRequest) *
// push admission request to audit handler, this won't block the admission request
ws.auditHandler.Add(request.DeepCopy())
// process generate policies
ws.applyGeneratePolicies(request, policyContext, generatePolicies, admissionRequestTimestamp, logger)
return successResponse(nil)
}

View file

@ -1,11 +1,12 @@
package webhooks
import (
"github.com/kyverno/kyverno/pkg/engine"
"github.com/kyverno/kyverno/pkg/utils"
"strings"
"time"
"github.com/kyverno/kyverno/pkg/engine"
"github.com/kyverno/kyverno/pkg/utils"
"github.com/pkg/errors"
"github.com/kyverno/kyverno/pkg/common"

View file

@ -2,6 +2,7 @@ package webhooks
import (
"errors"
"github.com/go-logr/logr"
v1 "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/engine"