From 2b17c3195752c8fa33ec61ac4d1bcb9c9622cda6 Mon Sep 17 00:00:00 2001 From: jdesouza Date: Thu, 24 Jul 2025 13:46:37 -0300 Subject: [PATCH] INS-1251: Polaris: upgrade github.com/qri-io/jsonschema to v0.2.1 (#1135) * Bump lins * Code refactoring * Fixign issues * Fixing issues * Fixing issues * Fixing issues * [WIP] * [WIP] * [WIP] * Trying to fix tests * Trying to fix tests * Fixing issues * Fixing issues * Fixing issues * Fixing issues * Fixing issues * Fixing issues * Revert go mod * Revert go mod * Revert go mod * Revert go mod * Fixing issues * Fixing issue * Code refactoring * Updating json schema version * Updating json schema version --- cmd/polaris/audit.go | 2 +- cmd/polaris/dashboard.go | 3 +- cmd/polaris/fix.go | 3 +- cmd/polaris/webhook.go | 3 +- docs/customization/custom-checks.md | 10 +- go.mod | 8 +- go.sum | 13 ++- .../checks/automountServiceAccountToken.yaml | 2 +- .../checks/clusterrolePodExecAttach.yaml | 2 +- .../clusterrolebindingClusterAdmin.yaml | 2 +- .../clusterrolebindingPodExecAttach.yaml | 2 +- pkg/config/checks/cpuLimitsMissing.yaml | 2 +- pkg/config/checks/cpuRequestsMissing.yaml | 2 +- pkg/config/checks/dangerousCapabilities.yaml | 2 +- .../checks/deploymentMissingReplicas.yaml | 2 +- pkg/config/checks/hostIPCSet.yaml | 2 +- pkg/config/checks/hostNetworkSet.yaml | 2 +- pkg/config/checks/hostPIDSet.yaml | 2 +- pkg/config/checks/hostPathSet.yaml | 2 +- pkg/config/checks/hostPortSet.yaml | 2 +- pkg/config/checks/hostProcess.yaml | 2 +- pkg/config/checks/hpaMaxAvailability.yaml | 2 +- pkg/config/checks/hpaMinAvailability.yaml | 2 +- pkg/config/checks/insecureCapabilities.yaml | 2 +- pkg/config/checks/linuxHardening.yaml | 10 +- pkg/config/checks/livenessProbeMissing.yaml | 2 +- pkg/config/checks/memoryLimitsMissing.yaml | 2 +- pkg/config/checks/memoryRequestsMissing.yaml | 2 +- .../checks/metadataAndInstanceMismatched.yaml | 2 +- pkg/config/checks/missingNetworkPolicy.yaml | 2 +- .../checks/missingPodDisruptionBudget.yaml | 2 +- .../checks/notReadOnlyRootFilesystem.yaml | 10 +- pkg/config/checks/pdbDisruptionsIsZero.yaml | 2 +- pkg/config/checks/priorityClassNotSet.yaml | 2 +- .../checks/privilegeEscalationAllowed.yaml | 10 +- pkg/config/checks/procMount.yaml | 2 +- pkg/config/checks/pullPolicyNotAlways.yaml | 2 +- pkg/config/checks/readinessProbeMissing.yaml | 2 +- pkg/config/checks/rolePodExecAttach.yaml | 2 +- .../rolebindingClusterAdminClusterRole.yaml | 2 +- .../checks/rolebindingClusterAdminRole.yaml | 2 +- .../rolebindingClusterRolePodExecAttach.yaml | 2 +- .../checks/rolebindingRolePodExecAttach.yaml | 2 +- pkg/config/checks/runAsPrivileged.yaml | 8 +- pkg/config/checks/runAsRootAllowed.yaml | 10 +- .../checks/sensitiveConfigmapContent.yaml | 2 +- .../checks/sensitiveContainerEnvVar.yaml | 2 +- pkg/config/checks/tagNotSpecified.yaml | 2 +- pkg/config/checks/tlsSettingsMissing.yaml | 2 +- .../checks/topologySpreadConstraint.yaml | 2 +- pkg/config/config_test.go | 14 +-- pkg/config/examples/config-full.yaml | 4 +- pkg/config/schema.go | 95 +++++++++++-------- pkg/dashboard/dashboard.go | 7 +- pkg/fix/fix.go | 5 +- pkg/validator/arbitrary_test.go | 5 +- pkg/validator/container_test.go | 15 +-- pkg/validator/controller_test.go | 18 ++-- pkg/validator/custom.go | 2 +- pkg/validator/fullaudit.go | 5 +- pkg/validator/fullaudit_test.go | 2 +- pkg/validator/pdb_hpa_validator.go | 4 +- pkg/validator/pod_test.go | 11 ++- pkg/validator/schema.go | 78 ++++++++------- pkg/validator/schema_test.go | 9 +- pkg/webhook/mutate.go | 8 +- pkg/webhook/webhook.go | 10 +- test/checks/nakedPod/check.yaml | 2 +- test/checks/oneController/check.yaml | 2 +- test/checks/resourceRange/check.yaml | 2 +- test/mutation_test.go | 3 +- test/schema_test.go | 3 +- 72 files changed, 256 insertions(+), 214 deletions(-) diff --git a/cmd/polaris/audit.go b/cmd/polaris/audit.go index c3ee684b..02930d97 100644 --- a/cmd/polaris/audit.go +++ b/cmd/polaris/audit.go @@ -145,7 +145,7 @@ var auditCmd = &cobra.Command{ os.Exit(1) } - auditData, err := validator.RunAudit(config, k) + auditData, err := validator.RunAudit(context.Background(), config, k) if err != nil { logrus.Errorf("Error while running audit on resources: %v", err) os.Exit(1) diff --git a/cmd/polaris/dashboard.go b/cmd/polaris/dashboard.go index 639b70d6..275bd719 100644 --- a/cmd/polaris/dashboard.go +++ b/cmd/polaris/dashboard.go @@ -15,6 +15,7 @@ package cmd import ( + "context" "fmt" "net/http" @@ -54,7 +55,7 @@ var dashboardCmd = &cobra.Command{ auditData := validator.ReadAuditFromFile(loadAuditFile) auditDataPtr = &auditData } - router, err := dashboard.GetRouter(config, auditPath, serverPort, basePath, auditDataPtr) + router, err := dashboard.GetRouter(context.Background(), config, auditPath, serverPort, basePath, auditDataPtr) if err != nil { logrus.Fatalf("error creating router: %v", err) } diff --git a/cmd/polaris/fix.go b/cmd/polaris/fix.go index 87f9df14..7c00897d 100644 --- a/cmd/polaris/fix.go +++ b/cmd/polaris/fix.go @@ -15,6 +15,7 @@ package cmd import ( + "context" "errors" "os" @@ -43,7 +44,7 @@ var fixCommand = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { logrus.Debug("Setting up controller manager") - err := fix.Execute(config, filesPath, isTemplate, checksToFix...) + err := fix.Execute(context.Background(), config, filesPath, isTemplate, checksToFix...) if err != nil { if errors.Is(err, fix.ErrFilesPathRequired) { logrus.Error("Please specify a files-path flag") diff --git a/cmd/polaris/webhook.go b/cmd/polaris/webhook.go index 5e79417f..38e606d9 100644 --- a/cmd/polaris/webhook.go +++ b/cmd/polaris/webhook.go @@ -15,6 +15,7 @@ package cmd import ( + "context" "os" "github.com/sirupsen/logrus" @@ -76,7 +77,7 @@ var webhookCmd = &cobra.Command{ fwebhook.NewValidateWebhook(mgr, config) } if enableMutations { - fwebhook.NewMutateWebhook(mgr, config) + fwebhook.NewMutateWebhook(context.Background(), mgr, config) } logrus.Infof("Polaris webhook server listening on port %d", webhookPort) if err := mgr.Start(signals.SetupSignalHandler()); err != nil { diff --git a/docs/customization/custom-checks.md b/docs/customization/custom-checks.md index 3bb32702..c1cbaff0 100644 --- a/docs/customization/custom-checks.md +++ b/docs/customization/custom-checks.md @@ -25,7 +25,7 @@ customChecks: category: Security target: Container schema: - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object properties: image: @@ -73,7 +73,7 @@ customChecks: category: Resources target: Container schema: - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object required: - resources @@ -120,7 +120,7 @@ successMessage: Label app.kubernetes.io/name matches metadata.name failureMessage: Label app.kubernetes.io/name must match metadata.name target: Controller schema: - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object properties: metadata: @@ -193,7 +193,7 @@ controllers: include: - Deployment schema: - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object properties: metadata: @@ -233,7 +233,7 @@ customChecks: foo: jsonSchema: | { - "$schema": "http://json-schema.org/draft-07/schema", + "$schema": "https://json-schema.org/draft/2019-09/schema", "type": "object" } ``` diff --git a/go.mod b/go.mod index e2901bc2..66b4e403 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,8 @@ require ( github.com/fatih/color v1.18.0 github.com/gorilla/mux v1.8.1 github.com/pkg/errors v0.9.1 - github.com/qri-io/jsonschema v0.1.2 + github.com/qri-io/jsonpointer v0.1.1 + github.com/qri-io/jsonschema v0.2.1 github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.9.1 github.com/stretchr/testify v1.10.0 @@ -56,14 +57,13 @@ require ( github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.64.0 // indirect github.com/prometheus/procfs v0.16.1 // indirect - github.com/qri-io/jsonpointer v0.1.1 // indirect github.com/samber/lo v1.51.0 // indirect github.com/spf13/pflag v1.0.6 // indirect github.com/x448/float16 v0.8.4 // indirect golang.org/x/net v0.41.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect - golang.org/x/sys v0.33.0 // indirect - golang.org/x/term v0.32.0 // indirect + golang.org/x/sys v0.34.0 // indirect + golang.org/x/term v0.33.0 // indirect golang.org/x/text v0.26.0 // indirect golang.org/x/time v0.12.0 // indirect google.golang.org/protobuf v1.36.6 // indirect diff --git a/go.sum b/go.sum index 6ab75ee7..bbc78833 100644 --- a/go.sum +++ b/go.sum @@ -119,11 +119,10 @@ github.com/prometheus/common v0.64.0 h1:pdZeA+g617P7oGv1CzdTzyeShxAGrTBsolKNOLQP github.com/prometheus/common v0.64.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= -github.com/qri-io/jsonpointer v0.1.0/go.mod h1:DnJPaYgiKu56EuDp8TU5wFLdZIcAnb/uH9v37ZaMV64= github.com/qri-io/jsonpointer v0.1.1 h1:prVZBZLL6TW5vsSB9fFHFAMBLI4b0ri5vribQlTJiBA= github.com/qri-io/jsonpointer v0.1.1/go.mod h1:DnJPaYgiKu56EuDp8TU5wFLdZIcAnb/uH9v37ZaMV64= -github.com/qri-io/jsonschema v0.1.2 h1:JlI7JAlxBbxh5Y641ctf+3kxcwYM6QbKDiqiQRkIBr0= -github.com/qri-io/jsonschema v0.1.2/go.mod h1:SiF7DGMMKfw3cPrKZErviQKaUGserd2PYMOGm0vrELU= +github.com/qri-io/jsonschema v0.2.1 h1:NNFoKms+kut6ABPf6xiKNM5214jzxAhDBrPHCJ97Wg0= +github.com/qri-io/jsonschema v0.2.1/go.mod h1:g7DPkiOsK1xv6T/Ao5scXRkd+yTFygcANPBaaqW+VrI= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -192,12 +191,12 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= +golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= -golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= +golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg= +golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= diff --git a/pkg/config/checks/automountServiceAccountToken.yaml b/pkg/config/checks/automountServiceAccountToken.yaml index e1a80283..2438bcf9 100644 --- a/pkg/config/checks/automountServiceAccountToken.yaml +++ b/pkg/config/checks/automountServiceAccountToken.yaml @@ -3,7 +3,7 @@ failureMessage: The ServiceAccount will be automounted category: Security target: PodSpec schema: - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object properties: serviceAccountName: diff --git a/pkg/config/checks/clusterrolePodExecAttach.yaml b/pkg/config/checks/clusterrolePodExecAttach.yaml index 44a419f9..00446003 100644 --- a/pkg/config/checks/clusterrolePodExecAttach.yaml +++ b/pkg/config/checks/clusterrolePodExecAttach.yaml @@ -3,7 +3,7 @@ failureMessage: The ClusterRole allows Pods/exec or pods/attach category: Security target: rbac.authorization.k8s.io/ClusterRole schemaString: | - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object required: ["metadata", "rules"] anyOf: diff --git a/pkg/config/checks/clusterrolebindingClusterAdmin.yaml b/pkg/config/checks/clusterrolebindingClusterAdmin.yaml index 68d99ca3..1822918c 100644 --- a/pkg/config/checks/clusterrolebindingClusterAdmin.yaml +++ b/pkg/config/checks/clusterrolebindingClusterAdmin.yaml @@ -3,7 +3,7 @@ failureMessage: The ClusterRoleBinding references the default cluster-admin Clus category: Security target: rbac.authorization.k8s.io/ClusterRoleBinding schemaString: | - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object anyOf: # Do not alert on default ClusterRoleBindings. diff --git a/pkg/config/checks/clusterrolebindingPodExecAttach.yaml b/pkg/config/checks/clusterrolebindingPodExecAttach.yaml index de698b51..29c214f9 100644 --- a/pkg/config/checks/clusterrolebindingPodExecAttach.yaml +++ b/pkg/config/checks/clusterrolebindingPodExecAttach.yaml @@ -3,7 +3,7 @@ failureMessage: The ClusterRoleBinding references a ClusterRole that allows Pods category: Security target: rbac.authorization.k8s.io/ClusterRoleBinding schemaString: | - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object anyOf: # Do not alert on default ClusterRoleBindings. diff --git a/pkg/config/checks/cpuLimitsMissing.yaml b/pkg/config/checks/cpuLimitsMissing.yaml index ec90fa27..204980af 100644 --- a/pkg/config/checks/cpuLimitsMissing.yaml +++ b/pkg/config/checks/cpuLimitsMissing.yaml @@ -6,7 +6,7 @@ containers: exclude: - initContainer schema: - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object required: - resources diff --git a/pkg/config/checks/cpuRequestsMissing.yaml b/pkg/config/checks/cpuRequestsMissing.yaml index 4a39b50d..419ff636 100644 --- a/pkg/config/checks/cpuRequestsMissing.yaml +++ b/pkg/config/checks/cpuRequestsMissing.yaml @@ -6,7 +6,7 @@ containers: exclude: - initContainer schema: - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object required: - resources diff --git a/pkg/config/checks/dangerousCapabilities.yaml b/pkg/config/checks/dangerousCapabilities.yaml index a1eef758..071f8a33 100644 --- a/pkg/config/checks/dangerousCapabilities.yaml +++ b/pkg/config/checks/dangerousCapabilities.yaml @@ -3,7 +3,7 @@ failureMessage: Container should not have dangerous capabilities category: Security target: Container schema: - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object properties: securityContext: diff --git a/pkg/config/checks/deploymentMissingReplicas.yaml b/pkg/config/checks/deploymentMissingReplicas.yaml index 19f561a9..1487afba 100644 --- a/pkg/config/checks/deploymentMissingReplicas.yaml +++ b/pkg/config/checks/deploymentMissingReplicas.yaml @@ -6,7 +6,7 @@ controllers: include: - Deployment schema: - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object required: - spec diff --git a/pkg/config/checks/hostIPCSet.yaml b/pkg/config/checks/hostIPCSet.yaml index 88845c89..b5e622d0 100644 --- a/pkg/config/checks/hostIPCSet.yaml +++ b/pkg/config/checks/hostIPCSet.yaml @@ -3,7 +3,7 @@ failureMessage: Host IPC should not be configured category: Security target: PodSpec schema: - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object properties: hostIPC: diff --git a/pkg/config/checks/hostNetworkSet.yaml b/pkg/config/checks/hostNetworkSet.yaml index 53728aa1..136e445a 100644 --- a/pkg/config/checks/hostNetworkSet.yaml +++ b/pkg/config/checks/hostNetworkSet.yaml @@ -3,7 +3,7 @@ failureMessage: Host network should not be configured category: Security target: PodSpec schema: - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object properties: hostNetwork: diff --git a/pkg/config/checks/hostPIDSet.yaml b/pkg/config/checks/hostPIDSet.yaml index fe1f5c7b..6a1f5223 100644 --- a/pkg/config/checks/hostPIDSet.yaml +++ b/pkg/config/checks/hostPIDSet.yaml @@ -3,7 +3,7 @@ failureMessage: Host PID should not be configured category: Security target: PodSpec schema: - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object properties: hostPID: diff --git a/pkg/config/checks/hostPathSet.yaml b/pkg/config/checks/hostPathSet.yaml index 62a4483d..cd36eb32 100644 --- a/pkg/config/checks/hostPathSet.yaml +++ b/pkg/config/checks/hostPathSet.yaml @@ -3,7 +3,7 @@ failureMessage: HostPath volumes must be forbidden category: Security target: PodSpec schema: - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object properties: volumes: diff --git a/pkg/config/checks/hostPortSet.yaml b/pkg/config/checks/hostPortSet.yaml index 91a90ca2..cd16200d 100644 --- a/pkg/config/checks/hostPortSet.yaml +++ b/pkg/config/checks/hostPortSet.yaml @@ -3,7 +3,7 @@ failureMessage: Host port should not be configured category: Security target: Container schema: - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object required: properties: diff --git a/pkg/config/checks/hostProcess.yaml b/pkg/config/checks/hostProcess.yaml index d0cb85d9..9d408e8e 100644 --- a/pkg/config/checks/hostProcess.yaml +++ b/pkg/config/checks/hostProcess.yaml @@ -3,7 +3,7 @@ failureMessage: Privileged access to the host is disallowed category: Security target: PodSpec schema: - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object properties: containers: diff --git a/pkg/config/checks/hpaMaxAvailability.yaml b/pkg/config/checks/hpaMaxAvailability.yaml index b30e51d8..7f044e78 100644 --- a/pkg/config/checks/hpaMaxAvailability.yaml +++ b/pkg/config/checks/hpaMaxAvailability.yaml @@ -3,7 +3,7 @@ failureMessage: HPA maxReplicas and minReplicas should be different category: Reliability target: autoscaling/HorizontalPodAutoscaler schemaString: | - "$schema": http://json-schema.org/draft-07/schema# + "$schema": https://json-schema.org/draft/2019-09/schema# type: object properties: spec: diff --git a/pkg/config/checks/hpaMinAvailability.yaml b/pkg/config/checks/hpaMinAvailability.yaml index b57eecc9..39ade5cb 100644 --- a/pkg/config/checks/hpaMinAvailability.yaml +++ b/pkg/config/checks/hpaMinAvailability.yaml @@ -3,7 +3,7 @@ failureMessage: HPA minReplicas should be 2 or more category: Reliability target: autoscaling/HorizontalPodAutoscaler schema: - "$schema": http://json-schema.org/draft-07/schema# + "$schema": https://json-schema.org/draft/2019-09/schema# type: object properties: spec: diff --git a/pkg/config/checks/insecureCapabilities.yaml b/pkg/config/checks/insecureCapabilities.yaml index 3526c02e..5de348c2 100644 --- a/pkg/config/checks/insecureCapabilities.yaml +++ b/pkg/config/checks/insecureCapabilities.yaml @@ -3,7 +3,7 @@ failureMessage: Container should not have insecure capabilities category: Security target: Container schema: - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object required: - securityContext diff --git a/pkg/config/checks/linuxHardening.yaml b/pkg/config/checks/linuxHardening.yaml index cbb64270..2135d788 100644 --- a/pkg/config/checks/linuxHardening.yaml +++ b/pkg/config/checks/linuxHardening.yaml @@ -3,8 +3,8 @@ FailureMessage: Use one of AppArmor, Seccomp, SELinux, or dropping Linux Capabil category: Security target: Container schemaString: | - '$schema': http://json-schema.org/draft-07/schema - definitions: + '$schema': https://json-schema.org/draft/2019-09/schema + $defs: podOrContainerSeccompProfile: type: object {{ $podSeccompProfileType := .Polaris.PodSpec.securityContext.seccompProfile.type }} @@ -83,7 +83,7 @@ schemaString: | type: object {{ else }} anyOf: - - $ref: "#/definitions/podOrContainerSeccompProfile" - - $ref: "#/definitions/podOrContainerSELinuxOptions" - - $ref: "#/definitions/containerDropCapabilities" + - $ref: "#/$defs/podOrContainerSeccompProfile" + - $ref: "#/$defs/podOrContainerSELinuxOptions" + - $ref: "#/$defs/containerDropCapabilities" {{ end}} diff --git a/pkg/config/checks/livenessProbeMissing.yaml b/pkg/config/checks/livenessProbeMissing.yaml index 7db3a78a..5984ac31 100644 --- a/pkg/config/checks/livenessProbeMissing.yaml +++ b/pkg/config/checks/livenessProbeMissing.yaml @@ -10,7 +10,7 @@ containers: - initContainer target: Container schema: - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object required: - livenessProbe diff --git a/pkg/config/checks/memoryLimitsMissing.yaml b/pkg/config/checks/memoryLimitsMissing.yaml index babba7d2..410c8ac4 100644 --- a/pkg/config/checks/memoryLimitsMissing.yaml +++ b/pkg/config/checks/memoryLimitsMissing.yaml @@ -6,7 +6,7 @@ containers: exclude: - initContainer schema: - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object required: - resources diff --git a/pkg/config/checks/memoryRequestsMissing.yaml b/pkg/config/checks/memoryRequestsMissing.yaml index 7ea2dcea..365a6654 100644 --- a/pkg/config/checks/memoryRequestsMissing.yaml +++ b/pkg/config/checks/memoryRequestsMissing.yaml @@ -6,7 +6,7 @@ containers: exclude: - initContainer schema: - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object required: - resources diff --git a/pkg/config/checks/metadataAndInstanceMismatched.yaml b/pkg/config/checks/metadataAndInstanceMismatched.yaml index 30aea6e5..0b9ea27f 100644 --- a/pkg/config/checks/metadataAndInstanceMismatched.yaml +++ b/pkg/config/checks/metadataAndInstanceMismatched.yaml @@ -3,7 +3,7 @@ failureMessage: Label app.kubernetes.io/instance must match metadata.name category: Reliability target: Controller schema: - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object properties: metadata: diff --git a/pkg/config/checks/missingNetworkPolicy.yaml b/pkg/config/checks/missingNetworkPolicy.yaml index b349f87e..f1c38b77 100644 --- a/pkg/config/checks/missingNetworkPolicy.yaml +++ b/pkg/config/checks/missingNetworkPolicy.yaml @@ -3,7 +3,7 @@ failureMessage: A NetworkPolicy should match pod labels and contain applied egre category: Security target: PodTemplate schema: - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object properties: metadata: diff --git a/pkg/config/checks/missingPodDisruptionBudget.yaml b/pkg/config/checks/missingPodDisruptionBudget.yaml index 434299be..2e3e8364 100644 --- a/pkg/config/checks/missingPodDisruptionBudget.yaml +++ b/pkg/config/checks/missingPodDisruptionBudget.yaml @@ -6,7 +6,7 @@ controllers: include: - Deployment schema: - "$schema": http://json-schema.org/draft-07/schema# + "$schema": https://json-schema.org/draft/2019-09/schema# type: object required: [spec] properties: diff --git a/pkg/config/checks/notReadOnlyRootFilesystem.yaml b/pkg/config/checks/notReadOnlyRootFilesystem.yaml index c77cf2b5..40f60ee3 100644 --- a/pkg/config/checks/notReadOnlyRootFilesystem.yaml +++ b/pkg/config/checks/notReadOnlyRootFilesystem.yaml @@ -4,8 +4,8 @@ category: Security target: Container schemaTarget: PodSpec schema: - '$schema': http://json-schema.org/draft-07/schema - definitions: + '$schema': https://json-schema.org/draft/2019-09/schema + $defs: goodSecurityContext: type: object anyOf: @@ -25,13 +25,13 @@ schema: - securityContext properties: securityContext: - $ref: "#/definitions/goodSecurityContext" + $ref: "#/$defs/goodSecurityContext" containers: type: array items: properties: securityContext: - $ref: "#/definitions/notBadSecurityContext" + $ref: "#/$defs/notBadSecurityContext" - properties: containers: type: array @@ -40,7 +40,7 @@ schema: - securityContext properties: securityContext: - $ref: "#/definitions/goodSecurityContext" + $ref: "#/$defs/goodSecurityContext" mutations: - op: add path: /securityContext/readOnlyRootFilesystem diff --git a/pkg/config/checks/pdbDisruptionsIsZero.yaml b/pkg/config/checks/pdbDisruptionsIsZero.yaml index 8f830b28..51d577c9 100644 --- a/pkg/config/checks/pdbDisruptionsIsZero.yaml +++ b/pkg/config/checks/pdbDisruptionsIsZero.yaml @@ -3,7 +3,7 @@ failureMessage: Voluntary evictions are not possible category: Reliability target: policy/PodDisruptionBudget schema: - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object required: - spec diff --git a/pkg/config/checks/priorityClassNotSet.yaml b/pkg/config/checks/priorityClassNotSet.yaml index 7a0ecce1..fda239b0 100644 --- a/pkg/config/checks/priorityClassNotSet.yaml +++ b/pkg/config/checks/priorityClassNotSet.yaml @@ -3,7 +3,7 @@ failureMessage: Priority class should be set category: Reliability target: PodSpec schema: - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object required: - priorityClassName diff --git a/pkg/config/checks/privilegeEscalationAllowed.yaml b/pkg/config/checks/privilegeEscalationAllowed.yaml index d21eed86..5afa9053 100644 --- a/pkg/config/checks/privilegeEscalationAllowed.yaml +++ b/pkg/config/checks/privilegeEscalationAllowed.yaml @@ -4,8 +4,8 @@ category: Security target: Container schemaTarget: PodSpec schema: - '$schema': http://json-schema.org/draft-07/schema - definitions: + '$schema': https://json-schema.org/draft/2019-09/schema + $defs: goodSecurityContext: type: object anyOf: @@ -25,13 +25,13 @@ schema: - securityContext properties: securityContext: - $ref: "#/definitions/goodSecurityContext" + $ref: "#/$defs/goodSecurityContext" containers: type: array items: properties: securityContext: - $ref: "#/definitions/notBadSecurityContext" + $ref: "#/$defs/notBadSecurityContext" - properties: containers: type: array @@ -40,7 +40,7 @@ schema: - securityContext properties: securityContext: - $ref: "#/definitions/goodSecurityContext" + $ref: "#/$defs/goodSecurityContext" mutations: - op: add diff --git a/pkg/config/checks/procMount.yaml b/pkg/config/checks/procMount.yaml index 10c547a8..e06aa719 100644 --- a/pkg/config/checks/procMount.yaml +++ b/pkg/config/checks/procMount.yaml @@ -3,7 +3,7 @@ failureMessage: Proc mount must not be changed from the default category: Security target: PodSpec schema: - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object properties: containers: diff --git a/pkg/config/checks/pullPolicyNotAlways.yaml b/pkg/config/checks/pullPolicyNotAlways.yaml index 2921c7a1..e823abb5 100644 --- a/pkg/config/checks/pullPolicyNotAlways.yaml +++ b/pkg/config/checks/pullPolicyNotAlways.yaml @@ -3,7 +3,7 @@ failureMessage: Image pull policy should be "Always" category: Reliability target: Container schema: - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema required: - imagePullPolicy properties: diff --git a/pkg/config/checks/readinessProbeMissing.yaml b/pkg/config/checks/readinessProbeMissing.yaml index 94d7ad6a..41ea9849 100644 --- a/pkg/config/checks/readinessProbeMissing.yaml +++ b/pkg/config/checks/readinessProbeMissing.yaml @@ -10,7 +10,7 @@ containers: - initContainer target: Container schema: - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object required: - readinessProbe diff --git a/pkg/config/checks/rolePodExecAttach.yaml b/pkg/config/checks/rolePodExecAttach.yaml index 39c780d0..aeeb793d 100644 --- a/pkg/config/checks/rolePodExecAttach.yaml +++ b/pkg/config/checks/rolePodExecAttach.yaml @@ -3,7 +3,7 @@ failureMessage: The Role allows Pods/exec or pods/attach category: Security target: rbac.authorization.k8s.io/Role schemaString: | - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object required: ["metadata", "rules"] anyOf: diff --git a/pkg/config/checks/rolebindingClusterAdminClusterRole.yaml b/pkg/config/checks/rolebindingClusterAdminClusterRole.yaml index f836df47..82bfe2f8 100644 --- a/pkg/config/checks/rolebindingClusterAdminClusterRole.yaml +++ b/pkg/config/checks/rolebindingClusterAdminClusterRole.yaml @@ -3,7 +3,7 @@ failureMessage: The RoleBinding references the default cluster-admin ClusterRole category: Security target: rbac.authorization.k8s.io/RoleBinding schemaString: | - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object anyOf: # Pass RoleBindings that point to a Role. diff --git a/pkg/config/checks/rolebindingClusterAdminRole.yaml b/pkg/config/checks/rolebindingClusterAdminRole.yaml index 2e5af37e..72dbd8c5 100644 --- a/pkg/config/checks/rolebindingClusterAdminRole.yaml +++ b/pkg/config/checks/rolebindingClusterAdminRole.yaml @@ -3,7 +3,7 @@ failureMessage: The RoleBinding references a Role with wildcard permissions category: Security target: rbac.authorization.k8s.io/RoleBinding schemaString: | - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object anyOf: # Pass RoleBindings that point to a ClusterRole. diff --git a/pkg/config/checks/rolebindingClusterRolePodExecAttach.yaml b/pkg/config/checks/rolebindingClusterRolePodExecAttach.yaml index 3aaf90d6..c85efb10 100644 --- a/pkg/config/checks/rolebindingClusterRolePodExecAttach.yaml +++ b/pkg/config/checks/rolebindingClusterRolePodExecAttach.yaml @@ -3,7 +3,7 @@ failureMessage: The RoleBinding references a ClusterRole that allows Pods/exec, category: Security target: rbac.authorization.k8s.io/RoleBinding schemaString: | - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object anyOf: # Pass RoleBindings that point to a Role. diff --git a/pkg/config/checks/rolebindingRolePodExecAttach.yaml b/pkg/config/checks/rolebindingRolePodExecAttach.yaml index a098713d..02d38b56 100644 --- a/pkg/config/checks/rolebindingRolePodExecAttach.yaml +++ b/pkg/config/checks/rolebindingRolePodExecAttach.yaml @@ -3,7 +3,7 @@ failureMessage: The RoleBinding references a Role that allows Pods/exec, allows category: Security target: rbac.authorization.k8s.io/RoleBinding schemaString: | - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object anyOf: # Pass RoleBindings that point to a ClusterRole. diff --git a/pkg/config/checks/runAsPrivileged.yaml b/pkg/config/checks/runAsPrivileged.yaml index fc4e59e6..04a28688 100644 --- a/pkg/config/checks/runAsPrivileged.yaml +++ b/pkg/config/checks/runAsPrivileged.yaml @@ -4,8 +4,8 @@ category: Security target: Container schemaTarget: PodSpec schema: - '$schema': http://json-schema.org/draft-07/schema - definitions: + '$schema': https://json-schema.org/draft/2019-09/schema + $defs: notBadSecurityContext: type: object properties: @@ -15,13 +15,13 @@ schema: type: object properties: securityContext: - $ref: "#/definitions/notBadSecurityContext" + $ref: "#/$defs/notBadSecurityContext" containers: type: array items: properties: securityContext: - $ref: "#/definitions/notBadSecurityContext" + $ref: "#/$defs/notBadSecurityContext" mutations: - op: add path: /securityContext/privileged diff --git a/pkg/config/checks/runAsRootAllowed.yaml b/pkg/config/checks/runAsRootAllowed.yaml index 11eaca11..1c0c0125 100644 --- a/pkg/config/checks/runAsRootAllowed.yaml +++ b/pkg/config/checks/runAsRootAllowed.yaml @@ -4,8 +4,8 @@ category: Security target: Container schemaTarget: PodSpec schema: - '$schema': http://json-schema.org/draft-07/schema - definitions: + '$schema': https://json-schema.org/draft/2019-09/schema + $defs: goodSecurityContext: type: object anyOf: @@ -33,13 +33,13 @@ schema: - securityContext properties: securityContext: - $ref: "#/definitions/goodSecurityContext" + $ref: "#/$defs/goodSecurityContext" containers: type: array items: properties: securityContext: - $ref: "#/definitions/notBadSecurityContext" + $ref: "#/$defs/notBadSecurityContext" # non-root specified at container level - properties: containers: @@ -49,7 +49,7 @@ schema: - securityContext properties: securityContext: - $ref: "#/definitions/goodSecurityContext" + $ref: "#/$defs/goodSecurityContext" mutations: - op: add path: /securityContext/runAsNonRoot diff --git a/pkg/config/checks/sensitiveConfigmapContent.yaml b/pkg/config/checks/sensitiveConfigmapContent.yaml index 48089159..e2aaf5cc 100644 --- a/pkg/config/checks/sensitiveConfigmapContent.yaml +++ b/pkg/config/checks/sensitiveConfigmapContent.yaml @@ -3,7 +3,7 @@ failureMessage: Potentially sensitive content is detected in the ConfigMap keys category: Security target: /ConfigMap schemaString: | - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object required: ["metadata"] properties: diff --git a/pkg/config/checks/sensitiveContainerEnvVar.yaml b/pkg/config/checks/sensitiveContainerEnvVar.yaml index 25e6219b..212444a7 100644 --- a/pkg/config/checks/sensitiveContainerEnvVar.yaml +++ b/pkg/config/checks/sensitiveContainerEnvVar.yaml @@ -3,7 +3,7 @@ failureMessage: The container sets potentially sensitive environment variables category: Security target: Container schemaString: | - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object properties: env: diff --git a/pkg/config/checks/tagNotSpecified.yaml b/pkg/config/checks/tagNotSpecified.yaml index 29ff9e96..848191d3 100644 --- a/pkg/config/checks/tagNotSpecified.yaml +++ b/pkg/config/checks/tagNotSpecified.yaml @@ -3,7 +3,7 @@ failureMessage: Image tag should be specified category: Reliability target: Container schema: - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema required: - image allOf: diff --git a/pkg/config/checks/tlsSettingsMissing.yaml b/pkg/config/checks/tlsSettingsMissing.yaml index 47a6ef2b..038983b4 100644 --- a/pkg/config/checks/tlsSettingsMissing.yaml +++ b/pkg/config/checks/tlsSettingsMissing.yaml @@ -3,7 +3,7 @@ failureMessage: Ingress does not have TLS configured category: Security target: networking.k8s.io/Ingress schema: - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object required: - spec diff --git a/pkg/config/checks/topologySpreadConstraint.yaml b/pkg/config/checks/topologySpreadConstraint.yaml index e4173455..f701d3bc 100644 --- a/pkg/config/checks/topologySpreadConstraint.yaml +++ b/pkg/config/checks/topologySpreadConstraint.yaml @@ -7,7 +7,7 @@ controllers: - Job - CronJob schema: - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object required: - topologySpreadConstraints diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 5ad91f32..e84b4619 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -52,7 +52,7 @@ customChecks: category: Security target: Container schema: - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object required: - securityContext @@ -69,7 +69,7 @@ customChecks: target: Container jsonSchema: > { - "$schema": "http://json-schema.org/draft-07/schema", + "$schema": "https://json-schema.org/draft/2019-09/schema", "type": "object", "required": ["securityContext"] } @@ -83,7 +83,7 @@ customChecks: category: Security target: Container schema: - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object required: - securityContext @@ -155,22 +155,22 @@ func TestConfigWithCustomChecks(t *testing.T) { assert.NoError(t, err, "Expected no error when parsing YAML config") assert.Equal(t, 1, len(parsedConf.CustomChecks)) check, err := parsedConf.CustomChecks["foo"].TemplateForResource(map[string]interface{}{}) - isValid, _, err := check.CheckObject(valid) + isValid, _, err := check.CheckObject(context.TODO(), valid) assert.NoError(t, err) assert.Equal(t, true, isValid) - isValid, _, err = check.CheckObject(invalid) + isValid, _, err = check.CheckObject(context.TODO(), invalid) assert.NoError(t, err) assert.Equal(t, false, isValid) parsedConf, err = Parse([]byte(confCustomChecksWithJSONSchema)) assert.NoError(t, err, "Expected no error when parsing YAML config") assert.Equal(t, 1, len(parsedConf.CustomChecks)) - isValid, problems, err := parsedConf.CustomChecks["foo"].CheckObject(valid) + isValid, problems, err := parsedConf.CustomChecks["foo"].CheckObject(context.TODO(), valid) assert.NoError(t, err) if !assert.Equal(t, true, isValid) { fmt.Println(problems[0].PropertyPath, problems[0].InvalidValue, problems[0].Message) } - isValid, _, err = check.CheckObject(invalid) + isValid, _, err = check.CheckObject(context.TODO(), invalid) assert.NoError(t, err) assert.Equal(t, false, isValid) } diff --git a/pkg/config/examples/config-full.yaml b/pkg/config/examples/config-full.yaml index 6db56283..b19e4d4c 100644 --- a/pkg/config/examples/config-full.yaml +++ b/pkg/config/examples/config-full.yaml @@ -75,7 +75,7 @@ customChecks: category: Resources target: Container schema: - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object required: - resources @@ -105,7 +105,7 @@ customChecks: category: Images target: Container schema: - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object properties: image: diff --git a/pkg/config/schema.go b/pkg/config/schema.go index 7743041b..66d77c6f 100644 --- a/pkg/config/schema.go +++ b/pkg/config/schema.go @@ -16,6 +16,7 @@ package config import ( "bytes" + "context" "encoding/json" "errors" "fmt" @@ -23,6 +24,7 @@ import ( "strings" "text/template" + "github.com/qri-io/jsonpointer" "github.com/qri-io/jsonschema" "github.com/thoas/go-funk" corev1 "k8s.io/api/core/v1" @@ -72,10 +74,10 @@ type SchemaCheck struct { SchemaTarget TargetKind `yaml:"schemaTarget" json:"schemaTarget"` Schema map[string]interface{} `yaml:"schema" json:"schema"` SchemaString string `yaml:"schemaString" json:"schemaString"` - Validator jsonschema.RootSchema `yaml:"-" json:"-"` + Validator jsonschema.Schema `yaml:"-" json:"-"` AdditionalSchemas map[string]map[string]interface{} `yaml:"additionalSchemas" json:"additionalSchemas"` AdditionalSchemaStrings map[string]string `yaml:"additionalSchemaStrings" json:"additionalSchemaStrings"` - AdditionalValidators map[string]jsonschema.RootSchema `yaml:"-" json:"-"` + AdditionalValidators map[string]jsonschema.Schema `yaml:"-" json:"-"` Mutations []Mutation `yaml:"mutations" json:"mutations"` } @@ -109,8 +111,9 @@ func ParseCheck(id string, rawBytes []byte) (SchemaCheck, error) { } func init() { - jsonschema.RegisterValidator("resourceMinimum", newResourceMinimum) - jsonschema.RegisterValidator("resourceMaximum", newResourceMaximum) + jsonschema.RegisterKeyword("resourceMinimum", newResourceMinimum) + jsonschema.RegisterKeyword("resourceMaximum", newResourceMaximum) + jsonschema.LoadDraft2019_09() } type includeExcludeList struct { @@ -118,50 +121,68 @@ type includeExcludeList struct { Exclude []string `yaml:"exclude"` } -func newResourceMinimum() jsonschema.Validator { +func newResourceMinimum() jsonschema.Keyword { return new(resourceMinimum) } -func newResourceMaximum() jsonschema.Validator { +func newResourceMaximum() jsonschema.Keyword { return new(resourceMaximum) } -// Validate checks that a specified quanitity is not less than the minimum -func (min resourceMinimum) Validate(path string, data interface{}, errs *[]jsonschema.ValError) { - err := validateRange(path, string(min), data, true) +func (min resourceMinimum) ValidateKeyword(ctx context.Context, currentState *jsonschema.ValidationState, data interface{}) { + err := validateRange(string(min), data, true) if err != nil { + errs := currentState.Errs *errs = append(*errs, *err...) + currentState.Errs = errs + } +} +func (max resourceMaximum) ValidateKeyword(ctx context.Context, currentState *jsonschema.ValidationState, data interface{}) { + err := validateRange(string(max), data, false) + if err != nil { + errs := currentState.Errs + *errs = append(*errs, *err...) + currentState.Errs = errs } } -// Validate checks that a specified quanitity is not greater than the maximum -func (max resourceMaximum) Validate(path string, data interface{}, errs *[]jsonschema.ValError) { - err := validateRange(path, string(max), data, false) - if err != nil { - *errs = append(*errs, *err...) - } +func (min resourceMinimum) Resolve(pointer jsonpointer.Pointer, uri string) *jsonschema.Schema { + // Not implemented + return nil } -func parseQuantity(i interface{}) (resource.Quantity, *[]jsonschema.ValError) { +func (min resourceMinimum) Register(uri string, registry *jsonschema.SchemaRegistry) { + // Not implemented +} + +func (max resourceMaximum) Resolve(pointer jsonpointer.Pointer, uri string) *jsonschema.Schema { + // Not implemented + return nil +} +func (max resourceMaximum) Register(uri string, registry *jsonschema.SchemaRegistry) { + // Not implemented +} + +func parseQuantity(i interface{}) (resource.Quantity, *[]jsonschema.KeyError) { if resNum, ok := i.(float64); ok { i = fmt.Sprintf("%f", resNum) } resStr, ok := i.(string) if !ok { - return resource.Quantity{}, &[]jsonschema.ValError{ + return resource.Quantity{}, &[]jsonschema.KeyError{ {Message: fmt.Sprintf("Resource quantity %v is not a string", i)}, } } q, err := resource.ParseQuantity(resStr) if err != nil { - return resource.Quantity{}, &[]jsonschema.ValError{ + return resource.Quantity{}, &[]jsonschema.KeyError{ {Message: fmt.Sprintf("Could not parse resource quantity: %s", resStr)}, } } return q, nil } -func validateRange(path string, limit interface{}, data interface{}, isMinimum bool) *[]jsonschema.ValError { +func validateRange(limit interface{}, data interface{}, isMinimum bool) *[]jsonschema.KeyError { limitQuantity, err := parseQuantity(limit) if err != nil { return err @@ -173,14 +194,14 @@ func validateRange(path string, limit interface{}, data interface{}, isMinimum b cmp := limitQuantity.Cmp(actualQuantity) if isMinimum { if cmp == 1 { - return &[]jsonschema.ValError{ - {Message: fmt.Sprintf("%s quantity %v is > %v", path, actualQuantity, limitQuantity)}, + return &[]jsonschema.KeyError{ + {Message: fmt.Sprintf("quantity %v is > %v", actualQuantity, limitQuantity)}, } } } else { if cmp == -1 { - return &[]jsonschema.ValError{ - {Message: fmt.Sprintf("%s quantity %v is < %v", path, actualQuantity, limitQuantity)}, + return &[]jsonschema.KeyError{ + {Message: fmt.Sprintf("quantity %v is < %v", actualQuantity, limitQuantity)}, } } } @@ -251,9 +272,9 @@ func (check SchemaCheck) TemplateForResource(res interface{}) (*SchemaCheck, err } } - newCheck.AdditionalValidators = map[string]jsonschema.RootSchema{} + newCheck.AdditionalValidators = map[string]jsonschema.Schema{} for kind, schemaStr := range newCheck.AdditionalSchemaStrings { - val := jsonschema.RootSchema{} + val := jsonschema.Schema{} err := UnmarshalYAMLOrJSON([]byte(schemaStr), &val) if err != nil { return nil, err @@ -268,38 +289,38 @@ func (check SchemaCheck) TemplateForResource(res interface{}) (*SchemaCheck, err } // CheckPodSpec checks a pod spec against the schema -func (check SchemaCheck) CheckPodSpec(pod *corev1.PodSpec) (bool, []jsonschema.ValError, error) { - return check.CheckObject(pod) +func (check SchemaCheck) CheckPodSpec(ctx context.Context, pod *corev1.PodSpec) (bool, []jsonschema.KeyError, error) { + return check.CheckObject(ctx, pod) } // CheckPodTemplate checks a pod template against the schema -func (check SchemaCheck) CheckPodTemplate(podTemplate interface{}) (bool, []jsonschema.ValError, error) { - return check.CheckObject(podTemplate) +func (check SchemaCheck) CheckPodTemplate(ctx context.Context, podTemplate interface{}) (bool, []jsonschema.KeyError, error) { + return check.CheckObject(ctx, podTemplate) } // CheckController checks a controler's spec against the schema -func (check SchemaCheck) CheckController(bytes []byte) (bool, []jsonschema.ValError, error) { - errs, err := check.Validator.ValidateBytes(bytes) +func (check SchemaCheck) CheckController(ctx context.Context, bytes []byte) (bool, []jsonschema.KeyError, error) { + errs, err := check.Validator.ValidateBytes(ctx, bytes) return len(errs) == 0, errs, err } // CheckContainer checks a container spec against the schema -func (check SchemaCheck) CheckContainer(container *corev1.Container) (bool, []jsonschema.ValError, error) { - return check.CheckObject(container) +func (check SchemaCheck) CheckContainer(ctx context.Context, container *corev1.Container) (bool, []jsonschema.KeyError, error) { + return check.CheckObject(ctx, container) } // CheckObject checks arbitrary data against the schema -func (check SchemaCheck) CheckObject(obj interface{}) (bool, []jsonschema.ValError, error) { +func (check SchemaCheck) CheckObject(ctx context.Context, obj interface{}) (bool, []jsonschema.KeyError, error) { bytes, err := json.Marshal(obj) if err != nil { return false, nil, err } - errs, err := check.Validator.ValidateBytes(bytes) + errs, err := check.Validator.ValidateBytes(ctx, bytes) return len(errs) == 0, errs, err } // CheckAdditionalObjects looks for an object that passes the specified additional schema -func (check SchemaCheck) CheckAdditionalObjects(groupkind string, objects []interface{}) (bool, error) { +func (check SchemaCheck) CheckAdditionalObjects(ctx context.Context, groupkind string, objects []interface{}) (bool, error) { val, ok := check.AdditionalValidators[groupkind] if !ok { return false, errors.New("No validator found for " + groupkind) @@ -309,7 +330,7 @@ func (check SchemaCheck) CheckAdditionalObjects(groupkind string, objects []inte if err != nil { return false, err } - errs, err := val.ValidateBytes(bytes) + errs, err := val.ValidateBytes(ctx, bytes) if err != nil { return false, err } diff --git a/pkg/dashboard/dashboard.go b/pkg/dashboard/dashboard.go index e965c12d..dde70bef 100644 --- a/pkg/dashboard/dashboard.go +++ b/pkg/dashboard/dashboard.go @@ -16,6 +16,7 @@ package dashboard import ( "bytes" + "context" "embed" "encoding/json" "html/template" @@ -140,7 +141,7 @@ func stripUnselectedNamespaces(data *validator.AuditData, selectedNamespaces []s } // GetRouter returns a mux router serving all routes necessary for the dashboard -func GetRouter(c config.Configuration, auditPath string, port int, basePath string, auditData *validator.AuditData) (*mux.Router, error) { +func GetRouter(ctx context.Context, c config.Configuration, auditPath string, port int, basePath string, auditData *validator.AuditData) (*mux.Router, error) { router := mux.NewRouter().PathPrefix(basePath).Subrouter() assetsSubFS, err := fs.Sub(assetsFS, "assets") @@ -176,7 +177,7 @@ func GetRouter(c config.Configuration, auditPath string, port int, basePath stri } var auditDataObj validator.AuditData - auditDataObj, err = validator.RunAudit(adjustedConf, k) + auditDataObj, err = validator.RunAudit(ctx, adjustedConf, k) if err != nil { http.Error(w, "Error Fetching Deployments", http.StatusInternalServerError) return @@ -212,7 +213,7 @@ func GetRouter(c config.Configuration, auditPath string, port int, basePath stri logrus.Infof("Running audit") var auditData validator.AuditData - auditData, err = validator.RunAudit(adjustedConf, k) + auditData, err = validator.RunAudit(ctx, adjustedConf, k) if err != nil { logrus.Errorf("Error getting audit data: %v", err) http.Error(w, "Error running audit", 500) diff --git a/pkg/fix/fix.go b/pkg/fix/fix.go index 9a007182..c2b4d26d 100644 --- a/pkg/fix/fix.go +++ b/pkg/fix/fix.go @@ -1,6 +1,7 @@ package fix import ( + "context" "errors" "fmt" "os" @@ -19,7 +20,7 @@ const templateCloseMarker = "POLARIS_CLOSE_TMPL" var ErrFilesPathRequired = errors.New("files-path flag is required") -func Execute(config config.Configuration, filesPath string, isTemplate bool, checksToFix ...string) error { +func Execute(ctx context.Context, config config.Configuration, filesPath string, isTemplate bool, checksToFix ...string) error { if filesPath == "" { return ErrFilesPathRequired } @@ -69,7 +70,7 @@ func Execute(config config.Configuration, filesPath string, isTemplate bool, che if err != nil { return fmt.Errorf("error creating resource provider from yaml: %v", err) } - results, err := validator.ApplyAllSchemaChecksToResourceProvider(&config, kubeResources) + results, err := validator.ApplyAllSchemaChecksToResourceProvider(ctx, &config, kubeResources) if err != nil { return fmt.Errorf("error applying schema check to the resources %s: %v", fullFilePath, err) } diff --git a/pkg/validator/arbitrary_test.go b/pkg/validator/arbitrary_test.go index ae48dab2..8d606290 100644 --- a/pkg/validator/arbitrary_test.go +++ b/pkg/validator/arbitrary_test.go @@ -15,6 +15,7 @@ package validator import ( + "context" "encoding/json" "testing" @@ -36,7 +37,7 @@ func TestValidatePDB(t *testing.T) { res, err := kube.NewGenericResourceFromUnstructured(pdb, nil) res.Kind = "PodDisruptionBudget" - actualResult, err := applyNonControllerSchemaChecks(&c, nil, res) + actualResult, err := applyNonControllerSchemaChecks(context.Background(), &c, nil, res) if err != nil { panic(err) } @@ -76,7 +77,7 @@ func TestValidateIngress(t *testing.T) { } res.Kind = "Ingress" - actualResult, err := applyNonControllerSchemaChecks(&c, nil, res) + actualResult, err := applyNonControllerSchemaChecks(context.Background(), &c, nil, res) if err != nil { panic(err) } diff --git a/pkg/validator/container_test.go b/pkg/validator/container_test.go index 62c3820a..26084dff 100644 --- a/pkg/validator/container_test.go +++ b/pkg/validator/container_test.go @@ -15,6 +15,7 @@ package validator import ( + "context" "fmt" "testing" @@ -69,7 +70,7 @@ func testValidateWithWorkload(t *testing.T, container *corev1.Container, resourc assert.NoError(t, err, "Expected no error when parsing config") var results ResultSet - results, err = applyContainerSchemaChecks(&parsedConf, nil, workload, container, false) + results, err = applyContainerSchemaChecks(context.Background(), &parsedConf, nil, workload, container, false) if err != nil { panic(err) } @@ -93,7 +94,7 @@ func TestValidateResourcesEmptyConfig(t *testing.T) { Name: "Empty", } - results, err := applyContainerSchemaChecks(&conf.Configuration{}, nil, getEmptyWorkload(t, ""), container, false) + results, err := applyContainerSchemaChecks(context.Background(), &conf.Configuration{}, nil, getEmptyWorkload(t, ""), container, false) if err != nil { panic(err) } @@ -190,7 +191,7 @@ func TestValidateHealthChecks(t *testing.T) { for idx, tt := range testCases { t.Run(tt.name, func(t *testing.T) { controller := getEmptyWorkload(t, "") - results, err := applyContainerSchemaChecks(&conf.Configuration{Checks: tt.probes}, nil, controller, tt.container, tt.isInit) + results, err := applyContainerSchemaChecks(context.Background(), &conf.Configuration{Checks: tt.probes}, nil, controller, tt.container, tt.isInit) if err != nil { panic(err) } @@ -304,7 +305,7 @@ func TestValidateImage(t *testing.T) { for _, tt := range testCases { t.Run(tt.name, func(t *testing.T) { controller := getEmptyWorkload(t, "") - results, err := applyContainerSchemaChecks(&conf.Configuration{Checks: tt.image}, nil, controller, tt.container, false) + results, err := applyContainerSchemaChecks(context.Background(), &conf.Configuration{Checks: tt.image}, nil, controller, tt.container, false) if err != nil { panic(err) } @@ -421,7 +422,7 @@ func TestValidateNetworking(t *testing.T) { for _, tt := range testCases { t.Run(tt.name, func(t *testing.T) { controller := getEmptyWorkload(t, "") - results, err := applyContainerSchemaChecks(&conf.Configuration{Checks: tt.networkConf}, nil, controller, tt.container, false) + results, err := applyContainerSchemaChecks(context.Background(), &conf.Configuration{Checks: tt.networkConf}, nil, controller, tt.container, false) if err != nil { panic(err) } @@ -926,7 +927,7 @@ func TestValidateSecurity(t *testing.T) { t.Run(tt.name, func(t *testing.T) { workload, err := kube.NewGenericResourceFromPod(corev1.Pod{Spec: *tt.pod}, nil) assert.NoError(t, err) - results, err := applyContainerSchemaChecks(&conf.Configuration{Checks: tt.securityConf}, nil, workload, tt.container, false) + results, err := applyContainerSchemaChecks(context.Background(), &conf.Configuration{Checks: tt.securityConf}, nil, workload, tt.container, false) if err != nil { panic(err) } @@ -1071,7 +1072,7 @@ func TestValidateRunAsRoot(t *testing.T) { t.Run(tt.name, func(t *testing.T) { workload, err := kube.NewGenericResourceFromPod(corev1.Pod{Spec: *tt.pod}, nil) assert.NoError(t, err) - results, err := applyContainerSchemaChecks(&config, nil, workload, tt.container, false) + results, err := applyContainerSchemaChecks(context.Background(), &config, nil, workload, tt.container, false) if err != nil { panic(err) } diff --git a/pkg/validator/controller_test.go b/pkg/validator/controller_test.go index c3989936..53b55f67 100644 --- a/pkg/validator/controller_test.go +++ b/pkg/validator/controller_test.go @@ -49,7 +49,7 @@ func TestValidateController(t *testing.T) { } var actualResult Result - actualResult, err = applyControllerSchemaChecks(&c, nil, deployment) + actualResult, err = applyControllerSchemaChecks(context.Background(), &c, nil, deployment) if err != nil { panic(err) } @@ -73,7 +73,7 @@ func TestControllerLevelChecks(t *testing.T) { Category: "Reliability", } for _, controller := range res.Resources["Deployment"] { - actualResult, err := applyControllerSchemaChecks(&c, nil, controller) + actualResult, err := applyControllerSchemaChecks(context.Background(), &c, nil, controller) if err != nil { panic(err) } @@ -137,7 +137,7 @@ func TestSkipHealthChecks(t *testing.T) { "livenessProbeMissing": {ID: "livenessProbeMissing", Message: "Liveness probe should be configured", Success: false, Severity: "warning", Category: "Reliability"}, } var actualResult Result - actualResult, err = applyControllerSchemaChecks(&c, nil, deployment) + actualResult, err = applyControllerSchemaChecks(context.Background(), &c, nil, deployment) if err != nil { panic(err) } @@ -156,7 +156,7 @@ func TestSkipHealthChecks(t *testing.T) { Dangers: uint(0), } expectedResults = ResultSet{} - actualResult, err = applyControllerSchemaChecks(&c, nil, job) + actualResult, err = applyControllerSchemaChecks(context.Background(), &c, nil, job) if err != nil { panic(err) } @@ -174,7 +174,7 @@ func TestSkipHealthChecks(t *testing.T) { Dangers: uint(0), } expectedResults = ResultSet{} - actualResult, err = applyControllerSchemaChecks(&c, nil, cronjob) + actualResult, err = applyControllerSchemaChecks(context.Background(), &c, nil, cronjob) if err != nil { panic(err) } @@ -210,7 +210,7 @@ func TestControllerExemptions(t *testing.T) { resources := []kube.GenericResource{workload} var actualResults []Result - actualResults, err = ApplyAllSchemaChecksToAllResources(&c, nil, resources) + actualResults, err = ApplyAllSchemaChecksToAllResources(context.Background(), &c, nil, resources) if err != nil { panic(err) } @@ -221,7 +221,7 @@ func TestControllerExemptions(t *testing.T) { c.Exemptions = []conf.Exemption{{ Namespace: "foo", }} - actualResults, err = ApplyAllSchemaChecksToAllResources(&c, nil, resources) + actualResults, err = ApplyAllSchemaChecksToAllResources(context.Background(), &c, nil, resources) if err != nil { panic(err) } @@ -233,7 +233,7 @@ func TestControllerExemptions(t *testing.T) { resources[0].ObjectMeta.SetAnnotations(map[string]string{ exemptionAnnotationKey: "true", }) - actualResults, err = ApplyAllSchemaChecksToAllResources(&c, nil, resources) + actualResults, err = ApplyAllSchemaChecksToAllResources(context.Background(), &c, nil, resources) if err != nil { panic(err) } @@ -242,7 +242,7 @@ func TestControllerExemptions(t *testing.T) { assert.EqualValues(t, expectedExemptSum, actualResults[0].GetSummary()) c.DisallowExemptions = true - actualResults, err = ApplyAllSchemaChecksToAllResources(&c, nil, resources) + actualResults, err = ApplyAllSchemaChecksToAllResources(context.Background(), &c, nil, resources) if err != nil { panic(err) } diff --git a/pkg/validator/custom.go b/pkg/validator/custom.go index 417a5183..e8494363 100644 --- a/pkg/validator/custom.go +++ b/pkg/validator/custom.go @@ -6,7 +6,7 @@ import ( "github.com/qri-io/jsonschema" ) -type validatorFunction func(test schemaTestCase) (bool, []jsonschema.ValError, error) +type validatorFunction func(test schemaTestCase) (bool, []jsonschema.KeyError, error) var validatorMapper = map[string]validatorFunction{} var lock = &sync.Mutex{} diff --git a/pkg/validator/fullaudit.go b/pkg/validator/fullaudit.go index fe7af5ed..285d2ef0 100644 --- a/pkg/validator/fullaudit.go +++ b/pkg/validator/fullaudit.go @@ -16,6 +16,7 @@ package validator import ( "bytes" + "context" "fmt" "io" "os" @@ -29,13 +30,13 @@ import ( ) // RunAudit runs a full Polaris audit and returns an AuditData object -func RunAudit(config conf.Configuration, kubeResources *kube.ResourceProvider) (AuditData, error) { +func RunAudit(ctx context.Context, config conf.Configuration, kubeResources *kube.ResourceProvider) (AuditData, error) { displayName := config.DisplayName if displayName == "" { displayName = kubeResources.SourceName } - results, err := ApplyAllSchemaChecksToResourceProvider(&config, kubeResources) + results, err := ApplyAllSchemaChecksToResourceProvider(ctx, &config, kubeResources) if err != nil { return AuditData{}, err } diff --git a/pkg/validator/fullaudit_test.go b/pkg/validator/fullaudit_test.go index 3ebf3ecb..20c4cf47 100644 --- a/pkg/validator/fullaudit_test.go +++ b/pkg/validator/fullaudit_test.go @@ -45,7 +45,7 @@ func TestGetTemplateData(t *testing.T) { score := uint(0) var actualAudit AuditData - actualAudit, err = RunAudit(c, resources) + actualAudit, err = RunAudit(context.Background(), c, resources) assert.Equal(t, err, nil, "error should be nil") assert.Equal(t, score, actualAudit.Score, "") assert.EqualValues(t, sum, actualAudit.GetSummary()) diff --git a/pkg/validator/pdb_hpa_validator.go b/pkg/validator/pdb_hpa_validator.go index dcebe719..2f57926b 100644 --- a/pkg/validator/pdb_hpa_validator.go +++ b/pkg/validator/pdb_hpa_validator.go @@ -19,7 +19,7 @@ func init() { registerCustomChecks("pdbMinAvailableGreaterThanHPAMinReplicas", pdbMinAvailableGreaterThanHPAMinReplicas) } -func pdbMinAvailableGreaterThanHPAMinReplicas(test schemaTestCase) (bool, []jsonschema.ValError, error) { +func pdbMinAvailableGreaterThanHPAMinReplicas(test schemaTestCase) (bool, []jsonschema.KeyError, error) { if test.ResourceProvider == nil { return true, nil, nil } @@ -70,7 +70,7 @@ func pdbMinAvailableGreaterThanHPAMinReplicas(test schemaTestCase) (bool, []json } if attachedHPA.Spec.MinReplicas != nil && pdbMinAvailable > int(*attachedHPA.Spec.MinReplicas) { - return false, []jsonschema.ValError{ + return false, []jsonschema.KeyError{ { PropertyPath: "spec.minAvailable", InvalidValue: pdbMinAvailable, diff --git a/pkg/validator/pod_test.go b/pkg/validator/pod_test.go index c1997a5a..67ff916c 100644 --- a/pkg/validator/pod_test.go +++ b/pkg/validator/pod_test.go @@ -15,6 +15,7 @@ package validator import ( + "context" "testing" "github.com/stretchr/testify/assert" @@ -57,7 +58,7 @@ func TestValidatePod(t *testing.T) { "hostProcess": {ID: "hostProcess", Message: "Privileged access to the host check is valid", Success: true, Severity: "warning", Category: "Security"}, } - actualPodResult, err := applyControllerSchemaChecks(&c, nil, deployment) + actualPodResult, err := applyControllerSchemaChecks(context.Background(), &c, nil, deployment) if err != nil { panic(err) } @@ -115,7 +116,7 @@ func TestInvalidIPCPod(t *testing.T) { "hostProcess": {ID: "hostProcess", Message: "Privileged access to the host is disallowed", Success: false, Severity: "warning", Category: "Security"}, } - actualPodResult, err := applyControllerSchemaChecks(&c, nil, workload) + actualPodResult, err := applyControllerSchemaChecks(context.Background(), &c, nil, workload) if err != nil { panic(err) } @@ -151,7 +152,7 @@ func TestInvalidNetworkPod(t *testing.T) { "hostPIDSet": {ID: "hostPIDSet", Message: "Host PID is not configured", Success: true, Severity: "danger", Category: "Security"}, } - actualPodResult, err := applyControllerSchemaChecks(&c, nil, workload) + actualPodResult, err := applyControllerSchemaChecks(context.Background(), &c, nil, workload) if err != nil { panic(err) } @@ -187,7 +188,7 @@ func TestInvalidPIDPod(t *testing.T) { "hostNetworkSet": {ID: "hostNetworkSet", Message: "Host network is not configured", Success: true, Severity: "warning", Category: "Security"}, } - actualPodResult, err := applyControllerSchemaChecks(&c, nil, workload) + actualPodResult, err := applyControllerSchemaChecks(context.Background(), &c, nil, workload) if err != nil { panic(err) } @@ -230,7 +231,7 @@ func TestExemption(t *testing.T) { "hostPIDSet": {ID: "hostPIDSet", Message: "Host PID is not configured", Success: true, Severity: "danger", Category: "Security"}, } - actualPodResult, err := applyControllerSchemaChecks(&c, nil, workload) + actualPodResult, err := applyControllerSchemaChecks(context.Background(), &c, nil, workload) if err != nil { panic(err) } diff --git a/pkg/validator/schema.go b/pkg/validator/schema.go index 82e8d0f1..44e08f75 100644 --- a/pkg/validator/schema.go +++ b/pkg/validator/schema.go @@ -15,6 +15,8 @@ package validator import ( + "context" + "encoding/json" "errors" "fmt" "sort" @@ -133,7 +135,7 @@ func getTemplateInput(test schemaTestCase) (map[string]interface{}, error) { return templateInput, nil } -func makeResult(conf *config.Configuration, check *config.SchemaCheck, passes bool, issues []jsonschema.ValError) ResultMessage { +func makeResult(conf *config.Configuration, check *config.SchemaCheck, passes bool, issues []jsonschema.KeyError) ResultMessage { details := []string{} for _, issue := range issues { details = append(details, issue.Message) @@ -172,13 +174,13 @@ func hasExemptionAnnotation(objMeta metaV1.Object, checkID string) bool { } // ApplyAllSchemaChecksToResourceProvider applies all available checks to a ResourceProvider -func ApplyAllSchemaChecksToResourceProvider(conf *config.Configuration, resourceProvider *kube.ResourceProvider) ([]Result, error) { +func ApplyAllSchemaChecksToResourceProvider(ctx context.Context, conf *config.Configuration, resourceProvider *kube.ResourceProvider) ([]Result, error) { results := []Result{} if resourceProvider == nil { return nil, errors.New("No resource provider set, cannot apply schema checks") } for _, resources := range resourceProvider.Resources { - kindResults, err := ApplyAllSchemaChecksToAllResources(conf, resourceProvider, resources) + kindResults, err := ApplyAllSchemaChecksToAllResources(ctx, conf, resourceProvider, resources) if err != nil { return results, err } @@ -188,10 +190,10 @@ func ApplyAllSchemaChecksToResourceProvider(conf *config.Configuration, resource } // ApplyAllSchemaChecksToAllResources applies available checks to a list of resources -func ApplyAllSchemaChecksToAllResources(conf *config.Configuration, resourceProvider *kube.ResourceProvider, resources []kube.GenericResource) ([]Result, error) { +func ApplyAllSchemaChecksToAllResources(ctx context.Context, conf *config.Configuration, resourceProvider *kube.ResourceProvider, resources []kube.GenericResource) ([]Result, error) { results := []Result{} for _, resource := range resources { - result, err := ApplyAllSchemaChecks(conf, resourceProvider, resource) + result, err := ApplyAllSchemaChecks(ctx, conf, resourceProvider, resource) if err != nil { return results, err } @@ -203,37 +205,37 @@ func ApplyAllSchemaChecksToAllResources(conf *config.Configuration, resourceProv } // ApplyAllSchemaChecks applies available checks to a single resource -func ApplyAllSchemaChecks(conf *config.Configuration, resourceProvider *kube.ResourceProvider, resource kube.GenericResource) (Result, error) { +func ApplyAllSchemaChecks(ctx context.Context, conf *config.Configuration, resourceProvider *kube.ResourceProvider, resource kube.GenericResource) (Result, error) { if resource.PodSpec == nil { - return applyNonControllerSchemaChecks(conf, resourceProvider, resource) + return applyNonControllerSchemaChecks(ctx, conf, resourceProvider, resource) } - return applyControllerSchemaChecks(conf, resourceProvider, resource) + return applyControllerSchemaChecks(ctx, conf, resourceProvider, resource) } -func applyNonControllerSchemaChecks(conf *config.Configuration, resourceProvider *kube.ResourceProvider, resource kube.GenericResource) (Result, error) { +func applyNonControllerSchemaChecks(ctx context.Context, conf *config.Configuration, resourceProvider *kube.ResourceProvider, resource kube.GenericResource) (Result, error) { finalResult := Result{ Kind: resource.Kind, Name: resource.ObjectMeta.GetName(), Namespace: resource.ObjectMeta.GetNamespace(), } - resultSet, err := applyTopLevelSchemaChecks(conf, resourceProvider, resource, false) + resultSet, err := applyTopLevelSchemaChecks(ctx, conf, resourceProvider, resource, false) finalResult.Results = resultSet return finalResult, err } -func applyControllerSchemaChecks(conf *config.Configuration, resourceProvider *kube.ResourceProvider, resource kube.GenericResource) (Result, error) { +func applyControllerSchemaChecks(ctx context.Context, conf *config.Configuration, resourceProvider *kube.ResourceProvider, resource kube.GenericResource) (Result, error) { finalResult := Result{ Kind: resource.Kind, Name: resource.ObjectMeta.GetName(), Namespace: resource.ObjectMeta.GetNamespace(), } - resultSet, err := applyTopLevelSchemaChecks(conf, resourceProvider, resource, true) + resultSet, err := applyTopLevelSchemaChecks(ctx, conf, resourceProvider, resource, true) if err != nil { return finalResult, err } finalResult.Results = resultSet - nonControllerResults, err := applyTopLevelSchemaChecks(conf, resourceProvider, resource, false) + nonControllerResults, err := applyTopLevelSchemaChecks(ctx, conf, resourceProvider, resource, false) if err != nil { return finalResult, err } @@ -244,7 +246,7 @@ func applyControllerSchemaChecks(conf *config.Configuration, resourceProvider *k finalResult.Results[key] = val } - podRS, err := applyPodSchemaChecks(conf, resourceProvider, resource) + podRS, err := applyPodSchemaChecks(ctx, conf, resourceProvider, resource) if err != nil { return finalResult, err } @@ -255,7 +257,7 @@ func applyControllerSchemaChecks(conf *config.Configuration, resourceProvider *k finalResult.PodResult = &podRes for _, container := range resource.PodSpec.InitContainers { - results, err := applyContainerSchemaChecks(conf, resourceProvider, resource, &container, true) + results, err := applyContainerSchemaChecks(ctx, conf, resourceProvider, resource, &container, true) if err != nil { return finalResult, err } @@ -266,7 +268,7 @@ func applyControllerSchemaChecks(conf *config.Configuration, resourceProvider *k podRes.ContainerResults = append(podRes.ContainerResults, cRes) } for _, container := range resource.PodSpec.Containers { - results, err := applyContainerSchemaChecks(conf, resourceProvider, resource, &container, false) + results, err := applyContainerSchemaChecks(ctx, conf, resourceProvider, resource, &container, false) if err != nil { return finalResult, err } @@ -280,7 +282,7 @@ func applyControllerSchemaChecks(conf *config.Configuration, resourceProvider *k return finalResult, nil } -func applyTopLevelSchemaChecks(conf *config.Configuration, resources *kube.ResourceProvider, res kube.GenericResource, isController bool) (ResultSet, error) { +func applyTopLevelSchemaChecks(ctx context.Context, conf *config.Configuration, resources *kube.ResourceProvider, res kube.GenericResource, isController bool) (ResultSet, error) { test := schemaTestCase{ ResourceProvider: resources, Resource: res, @@ -288,19 +290,19 @@ func applyTopLevelSchemaChecks(conf *config.Configuration, resources *kube.Resou if isController { test.Target = config.TargetController } - return applySchemaChecks(conf, test) + return applySchemaChecks(ctx, conf, test) } -func applyPodSchemaChecks(conf *config.Configuration, resources *kube.ResourceProvider, controller kube.GenericResource) (ResultSet, error) { +func applyPodSchemaChecks(ctx context.Context, conf *config.Configuration, resources *kube.ResourceProvider, controller kube.GenericResource) (ResultSet, error) { test := schemaTestCase{ Target: config.TargetPodSpec, ResourceProvider: resources, Resource: controller, } - return applySchemaChecks(conf, test) + return applySchemaChecks(ctx, conf, test) } -func applyContainerSchemaChecks(conf *config.Configuration, resources *kube.ResourceProvider, controller kube.GenericResource, container *corev1.Container, isInit bool) (ResultSet, error) { +func applyContainerSchemaChecks(ctx context.Context, conf *config.Configuration, resources *kube.ResourceProvider, controller kube.GenericResource, container *corev1.Container, isInit bool) (ResultSet, error) { test := schemaTestCase{ Target: config.TargetContainer, ResourceProvider: resources, @@ -308,14 +310,14 @@ func applyContainerSchemaChecks(conf *config.Configuration, resources *kube.Reso Container: container, IsInitContainer: isInit, } - return applySchemaChecks(conf, test) + return applySchemaChecks(ctx, conf, test) } -func applySchemaChecks(conf *config.Configuration, test schemaTestCase) (ResultSet, error) { +func applySchemaChecks(ctx context.Context, conf *config.Configuration, test schemaTestCase) (ResultSet, error) { results := ResultSet{} checkIDs := getSortedKeys(conf.Checks) for _, checkID := range checkIDs { - result, err := applySchemaCheck(conf, checkID, test) + result, err := applySchemaCheck(ctx, conf, checkID, test) if err != nil { return results, err } @@ -326,7 +328,7 @@ func applySchemaChecks(conf *config.Configuration, test schemaTestCase) (ResultS return results, nil } -func applySchemaCheck(conf *config.Configuration, checkID string, test schemaTestCase) (*ResultMessage, error) { +func applySchemaCheck(ctx context.Context, conf *config.Configuration, checkID string, test schemaTestCase) (*ResultMessage, error) { check, err := resolveCheck(conf, checkID, test) if err != nil { return nil, err @@ -334,8 +336,16 @@ func applySchemaCheck(conf *config.Configuration, checkID string, test schemaTes return nil, nil } var passes bool - var issues []jsonschema.ValError + var issues []jsonschema.KeyError var prefix string + emptyValidator := true + validatorBytes, err := json.Marshal(check.Validator) + if err != nil { + return nil, err + } + if string(validatorBytes) != "" && string(validatorBytes) != "null" && string(validatorBytes) != "{}" { + emptyValidator = false + } if check.SchemaTarget != "" { if check.SchemaTarget == config.TargetPodSpec && check.Target == config.TargetContainer { podCopy := *test.Resource.PodSpec @@ -359,15 +369,15 @@ func applySchemaCheck(conf *config.Configuration, checkID string, test schemaTes prefix += "/containers/" + strconv.Itoa(containerIndex) } } - passes, issues, err = check.CheckPodSpec(&podCopy) + passes, issues, err = check.CheckPodSpec(ctx, &podCopy) } else { return nil, fmt.Errorf("Unknown combination of target (%s) and schema target (%s)", check.Target, check.SchemaTarget) } } else if check.Target == config.TargetPodSpec { - passes, issues, err = check.CheckPodSpec(test.Resource.PodSpec) + passes, issues, err = check.CheckPodSpec(ctx, test.Resource.PodSpec) prefix = getJSONSchemaPrefix(test.Resource.Kind) } else if check.Target == config.TargetPodTemplate { - passes, issues, err = check.CheckPodTemplate(test.Resource.PodTemplate) + passes, issues, err = check.CheckPodTemplate(ctx, test.Resource.PodTemplate) prefix = getJSONSchemaPrefix(test.Resource.Kind) } else if check.Target == config.TargetContainer { containerIndex := -1 @@ -388,13 +398,13 @@ func applySchemaCheck(conf *config.Configuration, checkID string, test schemaTes prefix += "/containers/" + strconv.Itoa(containerIndex) } } - passes, issues, err = check.CheckContainer(test.Container) - } else if check.Validator.SchemaURI != "" { - passes, issues, err = check.CheckObject(test.Resource.Resource.Object) + passes, issues, err = check.CheckContainer(ctx, test.Container) + } else if !emptyValidator { + passes, issues, err = check.CheckObject(ctx, test.Resource.Resource.Object) } else if validatorMapper[checkID] != nil { passes, issues, err = validatorMapper[checkID](test) } else { - passes, issues, err = true, []jsonschema.ValError{}, nil + passes, issues, err = true, []jsonschema.KeyError{}, nil } if err != nil { return nil, err @@ -418,7 +428,7 @@ func applySchemaCheck(conf *config.Configuration, checkID string, test schemaTes objects := funk.Map(resources, func(res kube.GenericResource) interface{} { return res.Resource.Object }).([]interface{}) - passes, err = check.CheckAdditionalObjects(groupkind, objects) + passes, err = check.CheckAdditionalObjects(ctx, groupkind, objects) if err != nil { return nil, err } diff --git a/pkg/validator/schema_test.go b/pkg/validator/schema_test.go index 55349682..d808a388 100644 --- a/pkg/validator/schema_test.go +++ b/pkg/validator/schema_test.go @@ -15,6 +15,7 @@ package validator import ( + "context" "testing" conf "github.com/fairwindsops/polaris/pkg/config" @@ -58,7 +59,7 @@ customChecks: category: Efficiency target: Container schema: - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object required: - resources @@ -86,7 +87,7 @@ customChecks: exclude: - initContainer schema: - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object required: - resources @@ -158,14 +159,14 @@ func TestValidateResourcesInit(t *testing.T) { assert.NoError(t, err, "Expected no error when parsing config") var results ResultSet - results, err = applyContainerSchemaChecks(&parsedConf, nil, controller, emptyContainer, false) + results, err = applyContainerSchemaChecks(context.Background(), &parsedConf, nil, controller, emptyContainer, false) if err != nil { panic(err) } assert.Equal(t, uint(1), results.GetSummary().Dangers) assert.Equal(t, uint(1), results.GetSummary().Warnings) - results, err = applyContainerSchemaChecks(&parsedConf, nil, controller, emptyContainer, true) + results, err = applyContainerSchemaChecks(context.Background(), &parsedConf, nil, controller, emptyContainer, true) if err != nil { panic(err) } diff --git a/pkg/webhook/mutate.go b/pkg/webhook/mutate.go index c4d10aa4..fe60b3c8 100644 --- a/pkg/webhook/mutate.go +++ b/pkg/webhook/mutate.go @@ -37,7 +37,7 @@ type Mutator struct { } // NewMutateWebhook creates a mutating admission webhook for the apiType. -func NewMutateWebhook(mgr manager.Manager, c config.Configuration) { +func NewMutateWebhook(ctx context.Context, mgr manager.Manager, c config.Configuration) { path := "/mutate" decoder := admission.NewDecoder(runtime.NewScheme()) mutator := Mutator{ @@ -48,8 +48,8 @@ func NewMutateWebhook(mgr manager.Manager, c config.Configuration) { mgr.GetWebhookServer().Register(path, &webhook.Admission{Handler: &mutator}) } -func (m *Mutator) mutate(req admission.Request) ([]jsonpatch.Operation, error) { - results, kubeResources, err := GetValidatedResults(req.AdmissionRequest.Kind.Kind, m.decoder, req, m.Config) +func (m *Mutator) mutate(ctx context.Context, req admission.Request) ([]jsonpatch.Operation, error) { + results, kubeResources, err := GetValidatedResults(ctx, req.AdmissionRequest.Kind.Kind, m.decoder, req, m.Config) if err != nil { logrus.Errorf("Error while validating resource: %v", err) return nil, err @@ -87,7 +87,7 @@ func (m *Mutator) mutate(req admission.Request) ([]jsonpatch.Operation, error) { // Handle for Validator to run validation checks. func (m *Mutator) Handle(ctx context.Context, req admission.Request) admission.Response { logrus.Info("Starting mutation request") - patches, err := m.mutate(req) + patches, err := m.mutate(ctx, req) if err != nil { logrus.Errorf("Error while getting mutations: %v", err) return admission.Errored(403, err) diff --git a/pkg/webhook/webhook.go b/pkg/webhook/webhook.go index 950ac595..9a2f0c1f 100644 --- a/pkg/webhook/webhook.go +++ b/pkg/webhook/webhook.go @@ -52,12 +52,12 @@ func NewValidateWebhook(mgr manager.Manager, c config.Configuration) { mgr.GetWebhookServer().Register(path, &webhook.Admission{Handler: &validator}) } -func (v *Validator) handleInternal(req admission.Request) (*validator.Result, kube.GenericResource, error) { - return GetValidatedResults(req.AdmissionRequest.Kind.Kind, v.decoder, req, v.Config) +func (v *Validator) handleInternal(ctx context.Context, req admission.Request) (*validator.Result, kube.GenericResource, error) { + return GetValidatedResults(ctx, req.AdmissionRequest.Kind.Kind, v.decoder, req, v.Config) } // GetValidatedResults returns the validated results. -func GetValidatedResults(kind string, decoder *admission.Decoder, req admission.Request, config config.Configuration) (*validator.Result, kube.GenericResource, error) { +func GetValidatedResults(ctx context.Context, kind string, decoder *admission.Decoder, req admission.Request, config config.Configuration) (*validator.Result, kube.GenericResource, error) { var resource kube.GenericResource var err error rawBytes := req.Object.Raw @@ -106,7 +106,7 @@ func GetValidatedResults(kind string, decoder *admission.Decoder, req admission. logrus.Errorf("Failed to create resource: %v", err) return nil, resource, err } - resourceResult, err := validator.ApplyAllSchemaChecks(&config, nil, resource) + resourceResult, err := validator.ApplyAllSchemaChecks(ctx, &config, nil, resource) if err != nil { return nil, resource, err } @@ -116,7 +116,7 @@ func GetValidatedResults(kind string, decoder *admission.Decoder, req admission. // Handle for Validator to run validation checks. func (v *Validator) Handle(ctx context.Context, req admission.Request) admission.Response { logrus.Info("Starting admission request") - result, _, err := v.handleInternal(req) + result, _, err := v.handleInternal(ctx, req) if err != nil { logrus.Errorf("Error validating request: %v", err) return admission.Errored(http.StatusBadRequest, err) diff --git a/test/checks/nakedPod/check.yaml b/test/checks/nakedPod/check.yaml index 066889d6..4c8258e0 100644 --- a/test/checks/nakedPod/check.yaml +++ b/test/checks/nakedPod/check.yaml @@ -2,7 +2,7 @@ successMessage: example FP success failureMessage: example FP fail target: Pod schema: - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object properties: metadata: diff --git a/test/checks/oneController/check.yaml b/test/checks/oneController/check.yaml index 6f964ce3..3ea4cb1e 100644 --- a/test/checks/oneController/check.yaml +++ b/test/checks/oneController/check.yaml @@ -2,7 +2,7 @@ successMessage: example FP success failureMessage: example FP fail target: StatefulSet schema: - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object properties: metadata: diff --git a/test/checks/resourceRange/check.yaml b/test/checks/resourceRange/check.yaml index b937393a..dab87034 100644 --- a/test/checks/resourceRange/check.yaml +++ b/test/checks/resourceRange/check.yaml @@ -6,7 +6,7 @@ failureMessage: Resource limits should be within the required range category: Resources target: Container schema: - '$schema': http://json-schema.org/draft-07/schema + '$schema': https://json-schema.org/draft/2019-09/schema type: object required: - resources diff --git a/test/mutation_test.go b/test/mutation_test.go index b0814b96..86060737 100644 --- a/test/mutation_test.go +++ b/test/mutation_test.go @@ -1,6 +1,7 @@ package test import ( + "context" "fmt" "strings" "testing" @@ -49,7 +50,7 @@ func TestMutations(t *testing.T) { newConfig.Checks = map[string]config.Severity{} newConfig.Checks[mutationStr] = config.SeverityDanger newConfig.Mutations = []string{mutationStr} - results, err := validator.ApplyAllSchemaChecksToResourceProvider(&newConfig, tc.resources) + results, err := validator.ApplyAllSchemaChecksToResourceProvider(context.Background(), &newConfig, tc.resources) assert.NoError(t, err) assert.Len(t, results, 1) allMutations := mutation.GetMutationsFromResults(results) diff --git a/test/schema_test.go b/test/schema_test.go index 0cad1931..43307a44 100644 --- a/test/schema_test.go +++ b/test/schema_test.go @@ -15,6 +15,7 @@ package test import ( + "context" "fmt" "os" "path/filepath" @@ -123,7 +124,7 @@ func initTestCases() ([]testCase, map[string]string, map[string][]testCase) { func TestChecks(t *testing.T) { testCases, _, _ := initTestCases() for _, tc := range testCases { - results, err := validator.ApplyAllSchemaChecksToResourceProvider(&tc.config, tc.resources) + results, err := validator.ApplyAllSchemaChecksToResourceProvider(context.Background(), &tc.config, tc.resources) if err != nil { t.Fatalf("Error running checks: %v", err) }