From 80dc575a285974614ce317401bd8e49402703db9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Sun, 24 Mar 2024 16:48:31 +0100 Subject: [PATCH 1/5] misconf: Add helm api-version and kube-version flag --- .../references/configuration/cli/trivy_aws.md | 2 + .../configuration/cli/trivy_config.md | 2 + .../configuration/cli/trivy_filesystem.md | 2 + .../configuration/cli/trivy_image.md | 2 + .../configuration/cli/trivy_kubernetes.md | 2 + .../configuration/cli/trivy_repository.md | 2 + .../configuration/cli/trivy_rootfs.md | 2 + .../references/configuration/cli/trivy_vm.md | 2 + pkg/commands/artifact/run.go | 2 + pkg/flag/misconf_flags.go | 20 +++++ pkg/iac/scanners/helm/options.go | 8 ++ pkg/iac/scanners/helm/parser/option.go | 9 +++ pkg/iac/scanners/helm/parser/parser.go | 19 ++++- pkg/iac/scanners/helm/parser/parser_test.go | 3 +- pkg/iac/scanners/helm/scanner.go | 5 +- pkg/iac/scanners/helm/test/option_test.go | 74 +++++++++++++++++-- pkg/iac/scanners/helm/test/parser_test.go | 15 ++-- .../with-kube-version/templates/pdb.yaml | 17 +++++ .../testdata/with-kube-version/.helmignore | 23 ++++++ .../testdata/with-kube-version/Chart.yaml | 26 +++++++ .../with-kube-version/templates/_helpers.tpl | 62 ++++++++++++++++ .../with-kube-version/templates/pdb.yaml | 11 +++ .../testdata/with-kube-version/values.yaml | 0 pkg/misconf/scanner.go | 10 +++ 24 files changed, 304 insertions(+), 16 deletions(-) create mode 100644 pkg/iac/scanners/helm/test/testdata/expected/options/with-kube-version/templates/pdb.yaml create mode 100644 pkg/iac/scanners/helm/test/testdata/with-kube-version/.helmignore create mode 100644 pkg/iac/scanners/helm/test/testdata/with-kube-version/Chart.yaml create mode 100644 pkg/iac/scanners/helm/test/testdata/with-kube-version/templates/_helpers.tpl create mode 100644 pkg/iac/scanners/helm/test/testdata/with-kube-version/templates/pdb.yaml create mode 100644 pkg/iac/scanners/helm/test/testdata/with-kube-version/values.yaml diff --git a/docs/docs/references/configuration/cli/trivy_aws.md b/docs/docs/references/configuration/cli/trivy_aws.md index af1ebc44a834..b87bfce2bc30 100644 --- a/docs/docs/references/configuration/cli/trivy_aws.md +++ b/docs/docs/references/configuration/cli/trivy_aws.md @@ -76,6 +76,8 @@ trivy aws [flags] --endpoint string AWS Endpoint override --exit-code int specify exit code when any security issues are found -f, --format string format (table,json,template,sarif,cyclonedx,spdx,spdx-json,github,cosign-vuln) (default "table") + --helm-api-versions strings Available API versions used for Capabilities.APIVersions. This flag is the same as the api-versions flag of the helm template command. (can specify multiple or separate values with commas: policy/v1/PodDisruptionBudget,apps/v1/Deployment) + --helm-kube-version string Kubernetes version used for Capabilities.KubeVersion. This flag is the same as the kube-version flag of the helm template command. --helm-set strings specify Helm values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) --helm-set-file strings specify Helm values from respective files specified via the command line (can specify multiple or separate values with commas: key1=path1,key2=path2) --helm-set-string strings specify Helm string values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) diff --git a/docs/docs/references/configuration/cli/trivy_config.md b/docs/docs/references/configuration/cli/trivy_config.md index 865ecb6ba605..070257b4a896 100644 --- a/docs/docs/references/configuration/cli/trivy_config.md +++ b/docs/docs/references/configuration/cli/trivy_config.md @@ -20,6 +20,8 @@ trivy config [flags] DIR --exit-code int specify exit code when any security issues are found --file-patterns strings specify config file patterns -f, --format string format (table,json,template,sarif,cyclonedx,spdx,spdx-json,github,cosign-vuln) (default "table") + --helm-api-versions strings Available API versions used for Capabilities.APIVersions. This flag is the same as the api-versions flag of the helm template command. (can specify multiple or separate values with commas: policy/v1/PodDisruptionBudget,apps/v1/Deployment) + --helm-kube-version string Kubernetes version used for Capabilities.KubeVersion. This flag is the same as the kube-version flag of the helm template command. --helm-set strings specify Helm values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) --helm-set-file strings specify Helm values from respective files specified via the command line (can specify multiple or separate values with commas: key1=path1,key2=path2) --helm-set-string strings specify Helm string values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) diff --git a/docs/docs/references/configuration/cli/trivy_filesystem.md b/docs/docs/references/configuration/cli/trivy_filesystem.md index e26b26df7bfd..7aeb72ca5970 100644 --- a/docs/docs/references/configuration/cli/trivy_filesystem.md +++ b/docs/docs/references/configuration/cli/trivy_filesystem.md @@ -35,6 +35,8 @@ trivy filesystem [flags] PATH --exit-code int specify exit code when any security issues are found --file-patterns strings specify config file patterns -f, --format string format (table,json,template,sarif,cyclonedx,spdx,spdx-json,github,cosign-vuln) (default "table") + --helm-api-versions strings Available API versions used for Capabilities.APIVersions. This flag is the same as the api-versions flag of the helm template command. (can specify multiple or separate values with commas: policy/v1/PodDisruptionBudget,apps/v1/Deployment) + --helm-kube-version string Kubernetes version used for Capabilities.KubeVersion. This flag is the same as the kube-version flag of the helm template command. --helm-set strings specify Helm values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) --helm-set-file strings specify Helm values from respective files specified via the command line (can specify multiple or separate values with commas: key1=path1,key2=path2) --helm-set-string strings specify Helm string values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) diff --git a/docs/docs/references/configuration/cli/trivy_image.md b/docs/docs/references/configuration/cli/trivy_image.md index 20be9b459413..e5d91f31eb7c 100644 --- a/docs/docs/references/configuration/cli/trivy_image.md +++ b/docs/docs/references/configuration/cli/trivy_image.md @@ -51,6 +51,8 @@ trivy image [flags] IMAGE_NAME --exit-on-eol int exit with the specified code when the OS reaches end of service/life --file-patterns strings specify config file patterns -f, --format string format (table,json,template,sarif,cyclonedx,spdx,spdx-json,github,cosign-vuln) (default "table") + --helm-api-versions strings Available API versions used for Capabilities.APIVersions. This flag is the same as the api-versions flag of the helm template command. (can specify multiple or separate values with commas: policy/v1/PodDisruptionBudget,apps/v1/Deployment) + --helm-kube-version string Kubernetes version used for Capabilities.KubeVersion. This flag is the same as the kube-version flag of the helm template command. --helm-set strings specify Helm values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) --helm-set-file strings specify Helm values from respective files specified via the command line (can specify multiple or separate values with commas: key1=path1,key2=path2) --helm-set-string strings specify Helm string values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) diff --git a/docs/docs/references/configuration/cli/trivy_kubernetes.md b/docs/docs/references/configuration/cli/trivy_kubernetes.md index ed22d8299f5b..a1befafeb504 100644 --- a/docs/docs/references/configuration/cli/trivy_kubernetes.md +++ b/docs/docs/references/configuration/cli/trivy_kubernetes.md @@ -46,6 +46,8 @@ trivy kubernetes [flags] { cluster | all | specific resources like kubectl. eg: --exit-code int specify exit code when any security issues are found --file-patterns strings specify config file patterns -f, --format string format (table,json,cyclonedx) (default "table") + --helm-api-versions strings Available API versions used for Capabilities.APIVersions. This flag is the same as the api-versions flag of the helm template command. (can specify multiple or separate values with commas: policy/v1/PodDisruptionBudget,apps/v1/Deployment) + --helm-kube-version string Kubernetes version used for Capabilities.KubeVersion. This flag is the same as the kube-version flag of the helm template command. --helm-set strings specify Helm values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) --helm-set-file strings specify Helm values from respective files specified via the command line (can specify multiple or separate values with commas: key1=path1,key2=path2) --helm-set-string strings specify Helm string values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) diff --git a/docs/docs/references/configuration/cli/trivy_repository.md b/docs/docs/references/configuration/cli/trivy_repository.md index 0a2a614e4a5b..fa4d13bbdeee 100644 --- a/docs/docs/references/configuration/cli/trivy_repository.md +++ b/docs/docs/references/configuration/cli/trivy_repository.md @@ -35,6 +35,8 @@ trivy repository [flags] (REPO_PATH | REPO_URL) --exit-code int specify exit code when any security issues are found --file-patterns strings specify config file patterns -f, --format string format (table,json,template,sarif,cyclonedx,spdx,spdx-json,github,cosign-vuln) (default "table") + --helm-api-versions strings Available API versions used for Capabilities.APIVersions. This flag is the same as the api-versions flag of the helm template command. (can specify multiple or separate values with commas: policy/v1/PodDisruptionBudget,apps/v1/Deployment) + --helm-kube-version string Kubernetes version used for Capabilities.KubeVersion. This flag is the same as the kube-version flag of the helm template command. --helm-set strings specify Helm values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) --helm-set-file strings specify Helm values from respective files specified via the command line (can specify multiple or separate values with commas: key1=path1,key2=path2) --helm-set-string strings specify Helm string values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) diff --git a/docs/docs/references/configuration/cli/trivy_rootfs.md b/docs/docs/references/configuration/cli/trivy_rootfs.md index 571fb009f4f0..088b5deb6d88 100644 --- a/docs/docs/references/configuration/cli/trivy_rootfs.md +++ b/docs/docs/references/configuration/cli/trivy_rootfs.md @@ -38,6 +38,8 @@ trivy rootfs [flags] ROOTDIR --exit-on-eol int exit with the specified code when the OS reaches end of service/life --file-patterns strings specify config file patterns -f, --format string format (table,json,template,sarif,cyclonedx,spdx,spdx-json,github,cosign-vuln) (default "table") + --helm-api-versions strings Available API versions used for Capabilities.APIVersions. This flag is the same as the api-versions flag of the helm template command. (can specify multiple or separate values with commas: policy/v1/PodDisruptionBudget,apps/v1/Deployment) + --helm-kube-version string Kubernetes version used for Capabilities.KubeVersion. This flag is the same as the kube-version flag of the helm template command. --helm-set strings specify Helm values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) --helm-set-file strings specify Helm values from respective files specified via the command line (can specify multiple or separate values with commas: key1=path1,key2=path2) --helm-set-string strings specify Helm string values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) diff --git a/docs/docs/references/configuration/cli/trivy_vm.md b/docs/docs/references/configuration/cli/trivy_vm.md index 6acf6606284b..7b186a505794 100644 --- a/docs/docs/references/configuration/cli/trivy_vm.md +++ b/docs/docs/references/configuration/cli/trivy_vm.md @@ -35,6 +35,8 @@ trivy vm [flags] VM_IMAGE --exit-on-eol int exit with the specified code when the OS reaches end of service/life --file-patterns strings specify config file patterns -f, --format string format (table,json,template,sarif,cyclonedx,spdx,spdx-json,github,cosign-vuln) (default "table") + --helm-api-versions strings Available API versions used for Capabilities.APIVersions. This flag is the same as the api-versions flag of the helm template command. (can specify multiple or separate values with commas: policy/v1/PodDisruptionBudget,apps/v1/Deployment) + --helm-kube-version string Kubernetes version used for Capabilities.KubeVersion. This flag is the same as the kube-version flag of the helm template command. --helm-set strings specify Helm values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) --helm-set-file strings specify Helm values from respective files specified via the command line (can specify multiple or separate values with commas: key1=path1,key2=path2) --helm-set-string strings specify Helm string values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) diff --git a/pkg/commands/artifact/run.go b/pkg/commands/artifact/run.go index 4f8be6e1d911..da3b86c0e8b9 100644 --- a/pkg/commands/artifact/run.go +++ b/pkg/commands/artifact/run.go @@ -603,6 +603,8 @@ func initScannerConfig(opts flag.Options, cacheClient cache.Cache) (ScannerConfi HelmValueFiles: opts.HelmValueFiles, HelmFileValues: opts.HelmFileValues, HelmStringValues: opts.HelmStringValues, + HelmAPIVersions: opts.HelmAPIVersions, + HelmKubeVersion: opts.HelmKubeVersion, TerraformTFVars: opts.TerraformTFVars, CloudFormationParamVars: opts.CloudFormationParamVars, K8sVersion: opts.K8sVersion, diff --git a/pkg/flag/misconf_flags.go b/pkg/flag/misconf_flags.go index 492960b60ff9..57a91820c60d 100644 --- a/pkg/flag/misconf_flags.go +++ b/pkg/flag/misconf_flags.go @@ -45,6 +45,16 @@ var ( ConfigName: "misconfiguration.helm.set-string", Usage: "specify Helm string values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)", } + HelmAPIVersionsFlag = Flag[[]string]{ + Name: "helm-api-versions", + ConfigName: "misconfiguration.helm.api-versions", + Usage: "Available API versions used for Capabilities.APIVersions. This flag is the same as the api-versions flag of the helm template command. (can specify multiple or separate values with commas: policy/v1/PodDisruptionBudget,apps/v1/Deployment)", + } + HelmKubeVersionFlag = Flag[string]{ + Name: "helm-kube-version", + ConfigName: "misconfiguration.helm.kube-version", + Usage: "Kubernetes version used for Capabilities.KubeVersion. This flag is the same as the kube-version flag of the helm template command.", + } TfVarsFlag = Flag[[]string]{ Name: "tf-vars", ConfigName: "misconfiguration.terraform.vars", @@ -86,6 +96,8 @@ type MisconfFlagGroup struct { HelmValueFiles *Flag[[]string] HelmFileValues *Flag[[]string] HelmStringValues *Flag[[]string] + HelmAPIVersions *Flag[[]string] + HelmKubeVersion *Flag[string] TerraformTFVars *Flag[[]string] CloudformationParamVars *Flag[[]string] TerraformExcludeDownloaded *Flag[bool] @@ -102,6 +114,8 @@ type MisconfOptions struct { HelmValueFiles []string HelmFileValues []string HelmStringValues []string + HelmAPIVersions []string + HelmKubeVersion string TerraformTFVars []string CloudFormationParamVars []string TfExcludeDownloaded bool @@ -118,6 +132,8 @@ func NewMisconfFlagGroup() *MisconfFlagGroup { HelmFileValues: HelmSetFileFlag.Clone(), HelmStringValues: HelmSetStringFlag.Clone(), HelmValueFiles: HelmValuesFileFlag.Clone(), + HelmAPIVersions: HelmAPIVersionsFlag.Clone(), + HelmKubeVersion: HelmKubeVersionFlag.Clone(), TerraformTFVars: TfVarsFlag.Clone(), CloudformationParamVars: CfParamsFlag.Clone(), TerraformExcludeDownloaded: TerraformExcludeDownloaded.Clone(), @@ -138,6 +154,8 @@ func (f *MisconfFlagGroup) Flags() []Flagger { f.HelmValueFiles, f.HelmFileValues, f.HelmStringValues, + f.HelmAPIVersions, + f.HelmKubeVersion, f.TerraformTFVars, f.TerraformExcludeDownloaded, f.CloudformationParamVars, @@ -158,6 +176,8 @@ func (f *MisconfFlagGroup) ToOptions() (MisconfOptions, error) { HelmValueFiles: f.HelmValueFiles.Value(), HelmFileValues: f.HelmFileValues.Value(), HelmStringValues: f.HelmStringValues.Value(), + HelmAPIVersions: f.HelmAPIVersions.Value(), + HelmKubeVersion: f.HelmKubeVersion.Value(), TerraformTFVars: f.TerraformTFVars.Value(), CloudFormationParamVars: f.CloudformationParamVars.Value(), TfExcludeDownloaded: f.TerraformExcludeDownloaded.Value(), diff --git a/pkg/iac/scanners/helm/options.go b/pkg/iac/scanners/helm/options.go index 009809e734e6..6ac412bf1e34 100644 --- a/pkg/iac/scanners/helm/options.go +++ b/pkg/iac/scanners/helm/options.go @@ -49,3 +49,11 @@ func ScannerWithAPIVersions(values ...string) options.ScannerOption { } } } + +func ScannerWithKubeVersion(values string) options.ScannerOption { + return func(s options.ConfigurableScanner) { + if helmScanner, ok := s.(ConfigurableHelmScanner); ok { + helmScanner.AddParserOptions(parser.OptionWithKubeVersion(values)) + } + } +} diff --git a/pkg/iac/scanners/helm/parser/option.go b/pkg/iac/scanners/helm/parser/option.go index 379cc9460979..6de98d765182 100644 --- a/pkg/iac/scanners/helm/parser/option.go +++ b/pkg/iac/scanners/helm/parser/option.go @@ -9,6 +9,7 @@ type ConfigurableHelmParser interface { SetFileValues(...string) SetStringValues(...string) SetAPIVersions(...string) + SetKubeVersion(string) } func OptionWithValuesFile(paths ...string) options.ParserOption { @@ -50,3 +51,11 @@ func OptionWithAPIVersions(values ...string) options.ParserOption { } } } + +func OptionWithKubeVersion(value string) options.ParserOption { + return func(p options.ConfigurableParser) { + if helmParser, ok := p.(ConfigurableHelmParser); ok { + helmParser.SetKubeVersion(value) + } + } +} diff --git a/pkg/iac/scanners/helm/parser/parser.go b/pkg/iac/scanners/helm/parser/parser.go index 20228258e5d3..7ae69e72eb95 100644 --- a/pkg/iac/scanners/helm/parser/parser.go +++ b/pkg/iac/scanners/helm/parser/parser.go @@ -18,6 +18,7 @@ import ( "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/chart/loader" + "helm.sh/helm/v3/pkg/chartutil" "helm.sh/helm/v3/pkg/release" "helm.sh/helm/v3/pkg/releaseutil" @@ -41,6 +42,7 @@ type Parser struct { fileValues []string stringValues []string apiVersions []string + kubeVersion string } type ChartFile struct { @@ -76,7 +78,11 @@ func (p *Parser) SetAPIVersions(values ...string) { p.apiVersions = values } -func New(path string, opts ...options.ParserOption) *Parser { +func (p *Parser) SetKubeVersion(value string) { + p.kubeVersion = value +} + +func New(path string, opts ...options.ParserOption) (*Parser, error) { client := action.NewInstall(&action.Configuration{}) client.DryRun = true // don't do anything @@ -96,7 +102,16 @@ func New(path string, opts ...options.ParserOption) *Parser { p.helmClient.APIVersions = p.apiVersions } - return p + if p.kubeVersion != "" { + kubeVersion, err := chartutil.ParseKubeVersion(p.kubeVersion) + if err != nil { + return nil, err + } + + p.helmClient.KubeVersion = kubeVersion + } + + return p, nil } func (p *Parser) ParseFS(ctx context.Context, target fs.FS, path string) error { diff --git a/pkg/iac/scanners/helm/parser/parser_test.go b/pkg/iac/scanners/helm/parser/parser_test.go index c146b8f9e18f..030b0efbb86f 100644 --- a/pkg/iac/scanners/helm/parser/parser_test.go +++ b/pkg/iac/scanners/helm/parser/parser_test.go @@ -12,7 +12,8 @@ import ( func TestParseFS(t *testing.T) { t.Run("source chart is located next to an same archived chart", func(t *testing.T) { - p := New(".") + p, err := New(".") + require.NoError(t, err) require.NoError(t, p.ParseFS(context.TODO(), os.DirFS(filepath.Join("testdata", "chart-and-archived-chart")), ".")) expectedFiles := []string{ diff --git a/pkg/iac/scanners/helm/scanner.go b/pkg/iac/scanners/helm/scanner.go index e655932e01fd..b5e9686b3071 100644 --- a/pkg/iac/scanners/helm/scanner.go +++ b/pkg/iac/scanners/helm/scanner.go @@ -162,7 +162,10 @@ func (s *Scanner) ScanFS(ctx context.Context, target fs.FS, path string) (scan.R } func (s *Scanner) getScanResults(path string, ctx context.Context, target fs.FS) (results []scan.Result, err error) { - helmParser := parser.New(path, s.parserOptions...) + helmParser, err := parser.New(path, s.parserOptions...) + if err != nil { + return nil, err + } if err := helmParser.ParseFS(ctx, target, path); err != nil { return nil, err diff --git a/pkg/iac/scanners/helm/test/option_test.go b/pkg/iac/scanners/helm/test/option_test.go index 2ad7efc64008..ab28f508e989 100644 --- a/pkg/iac/scanners/helm/test/option_test.go +++ b/pkg/iac/scanners/helm/test/option_test.go @@ -40,8 +40,9 @@ func Test_helm_parser_with_options_with_values_file(t *testing.T) { opts = append(opts, parser2.OptionWithValuesFile(test.valuesFile)) } - helmParser := parser2.New(chartName, opts...) - err := helmParser.ParseFS(context.TODO(), os.DirFS(filepath.Join("testdata", chartName)), ".") + helmParser, err := parser2.New(chartName, opts...) + require.NoError(t, err) + err = helmParser.ParseFS(context.TODO(), os.DirFS(filepath.Join("testdata", chartName)), ".") require.NoError(t, err) manifests, err := helmParser.RenderedChartFiles() require.NoError(t, err) @@ -94,8 +95,9 @@ func Test_helm_parser_with_options_with_set_value(t *testing.T) { opts = append(opts, parser2.OptionWithValues(test.values)) } - helmParser := parser2.New(chartName, opts...) - err := helmParser.ParseFS(context.TODO(), os.DirFS(filepath.Join("testdata", chartName)), ".") + helmParser, err := parser2.New(chartName, opts...) + require.NoError(t, err) + err = helmParser.ParseFS(context.TODO(), os.DirFS(filepath.Join("testdata", chartName)), ".") require.NoError(t, err) manifests, err := helmParser.RenderedChartFiles() require.NoError(t, err) @@ -143,8 +145,68 @@ func Test_helm_parser_with_options_with_api_versions(t *testing.T) { opts = append(opts, parser2.OptionWithAPIVersions(test.apiVersions...)) } - helmParser := parser2.New(chartName, opts...) - err := helmParser.ParseFS(context.TODO(), os.DirFS(filepath.Join("testdata", chartName)), ".") + helmParser, err := parser2.New(chartName, opts...) + require.NoError(t, err) + err = helmParser.ParseFS(context.TODO(), os.DirFS(filepath.Join("testdata", chartName)), ".") + require.NoError(t, err) + manifests, err := helmParser.RenderedChartFiles() + require.NoError(t, err) + + assert.Len(t, manifests, 1) + + for _, manifest := range manifests { + expectedPath := filepath.Join("testdata", "expected", "options", chartName, manifest.TemplateFilePath) + + expectedContent, err := os.ReadFile(expectedPath) + require.NoError(t, err) + + cleanExpected := strings.TrimSpace(strings.ReplaceAll(string(expectedContent), "\r\n", "\n")) + cleanActual := strings.TrimSpace(strings.ReplaceAll(manifest.ManifestContent, "\r\n", "\n")) + + assert.Equal(t, cleanExpected, cleanActual) + } + }) + } +} + +func Test_helm_parser_with_options_with_kube_versions(t *testing.T) { + + tests := []struct { + testName string + chartName string + kubeVersion string + error string + }{ + { + testName: "Parsing directory 'with-kube-version'", + chartName: "with-kube-version", + kubeVersion: "1.60", + }, + { + testName: "Parsing directory 'with-kube-version' with invalid kube version", + chartName: "with-kube-version", + kubeVersion: "a.b.c", + error: "Invalid Semantic Version", + }, + } + + for _, test := range tests { + t.Run(test.testName, func(t *testing.T) { + chartName := test.chartName + + t.Logf("Running test: %s", test.testName) + + var opts []options.ParserOption + + opts = append(opts, parser2.OptionWithKubeVersion(test.kubeVersion)) + + helmParser, err := parser2.New(chartName, opts...) + if test.error != "" { + require.EqualError(t, err, test.error) + return + } + require.NoError(t, err) + err = helmParser.ParseFS(context.TODO(), os.DirFS(filepath.Join("testdata", chartName)), ".") require.NoError(t, err) manifests, err := helmParser.RenderedChartFiles() require.NoError(t, err) diff --git a/pkg/iac/scanners/helm/test/parser_test.go b/pkg/iac/scanners/helm/test/parser_test.go index 6d9f5ad0cff3..89667a211f83 100644 --- a/pkg/iac/scanners/helm/test/parser_test.go +++ b/pkg/iac/scanners/helm/test/parser_test.go @@ -35,8 +35,9 @@ func Test_helm_parser(t *testing.T) { t.Logf("Running test: %s", test.testName) - helmParser := parser.New(chartName) - err := helmParser.ParseFS(context.TODO(), os.DirFS(filepath.Join("testdata", chartName)), ".") + helmParser, err := parser.New(chartName) + require.NoError(t, err) + err = helmParser.ParseFS(context.TODO(), os.DirFS(filepath.Join("testdata", chartName)), ".") require.NoError(t, err) manifests, err := helmParser.RenderedChartFiles() require.NoError(t, err) @@ -73,8 +74,9 @@ func Test_helm_parser_where_name_non_string(t *testing.T) { t.Logf("Running test: %s", test.testName) - helmParser := parser.New(chartName) - err := helmParser.ParseFS(context.TODO(), os.DirFS(filepath.Join("testdata", chartName)), ".") + helmParser, err := parser.New(chartName) + require.NoError(t, err) + err = helmParser.ParseFS(context.TODO(), os.DirFS(filepath.Join("testdata", chartName)), ".") require.NoError(t, err) } } @@ -163,8 +165,9 @@ func Test_helm_tarball_parser(t *testing.T) { testFs := os.DirFS(testTemp) - helmParser := parser.New(test.archiveFile) - err := helmParser.ParseFS(context.TODO(), testFs, ".") + helmParser, err := parser.New(test.archiveFile) + require.NoError(t, err) + err = helmParser.ParseFS(context.TODO(), testFs, ".") require.NoError(t, err) manifests, err := helmParser.RenderedChartFiles() diff --git a/pkg/iac/scanners/helm/test/testdata/expected/options/with-kube-version/templates/pdb.yaml b/pkg/iac/scanners/helm/test/testdata/expected/options/with-kube-version/templates/pdb.yaml new file mode 100644 index 000000000000..7c7ef5fd74d7 --- /dev/null +++ b/pkg/iac/scanners/helm/test/testdata/expected/options/with-kube-version/templates/pdb.yaml @@ -0,0 +1,17 @@ +# Source: with-api-version/templates/pdb.yaml +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: with-api-version + labels: + helm.sh/chart: with-api-version-0.1.0 + app.kubernetes.io/name: with-api-version + app.kubernetes.io/instance: with-api-version + app.kubernetes.io/version: "1.16.0" + app.kubernetes.io/managed-by: Helm +spec: + selector: + matchLabels: + app.kubernetes.io/name: with-api-version + app.kubernetes.io/instance: with-api-version + maxUnavailable: 0 diff --git a/pkg/iac/scanners/helm/test/testdata/with-kube-version/.helmignore b/pkg/iac/scanners/helm/test/testdata/with-kube-version/.helmignore new file mode 100644 index 000000000000..0e8a0eb36f4c --- /dev/null +++ b/pkg/iac/scanners/helm/test/testdata/with-kube-version/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/pkg/iac/scanners/helm/test/testdata/with-kube-version/Chart.yaml b/pkg/iac/scanners/helm/test/testdata/with-kube-version/Chart.yaml new file mode 100644 index 000000000000..99c44c125940 --- /dev/null +++ b/pkg/iac/scanners/helm/test/testdata/with-kube-version/Chart.yaml @@ -0,0 +1,26 @@ +apiVersion: v2 +name: with-api-version +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "1.16.0" + +kubeVersion: ">=1.60.0-0" diff --git a/pkg/iac/scanners/helm/test/testdata/with-kube-version/templates/_helpers.tpl b/pkg/iac/scanners/helm/test/testdata/with-kube-version/templates/_helpers.tpl new file mode 100644 index 000000000000..cab726131dc5 --- /dev/null +++ b/pkg/iac/scanners/helm/test/testdata/with-kube-version/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "with-api-version.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "with-api-version.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "with-api-version.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "with-api-version.labels" -}} +helm.sh/chart: {{ include "with-api-version.chart" . }} +{{ include "with-api-version.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "with-api-version.selectorLabels" -}} +app.kubernetes.io/name: {{ include "with-api-version.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "with-api-version.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "with-api-version.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/pkg/iac/scanners/helm/test/testdata/with-kube-version/templates/pdb.yaml b/pkg/iac/scanners/helm/test/testdata/with-kube-version/templates/pdb.yaml new file mode 100644 index 000000000000..0c063e06df97 --- /dev/null +++ b/pkg/iac/scanners/helm/test/testdata/with-kube-version/templates/pdb.yaml @@ -0,0 +1,11 @@ +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: {{ include "with-api-version.fullname" . }} + labels: + {{- include "with-api-version.labels" . | nindent 4 }} +spec: + selector: + matchLabels: + {{- include "with-api-version.selectorLabels" . | nindent 6 }} + maxUnavailable: 0 diff --git a/pkg/iac/scanners/helm/test/testdata/with-kube-version/values.yaml b/pkg/iac/scanners/helm/test/testdata/with-kube-version/values.yaml new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/pkg/misconf/scanner.go b/pkg/misconf/scanner.go index 4c353f22c26e..6a30c9b69ec4 100644 --- a/pkg/misconf/scanner.go +++ b/pkg/misconf/scanner.go @@ -59,6 +59,8 @@ type ScannerOption struct { HelmValueFiles []string HelmFileValues []string HelmStringValues []string + HelmAPIVersions []string + HelmKubeVersion string TerraformTFVars []string CloudFormationParamVars []string TfExcludeDownloaded bool @@ -332,6 +334,14 @@ func addHelmOpts(opts []options.ScannerOption, scannerOption ScannerOption) []op opts = append(opts, helm2.ScannerWithStringValues(scannerOption.HelmStringValues...)) } + if len(scannerOption.HelmAPIVersions) > 0 { + opts = append(opts, helm2.ScannerWithAPIVersions(scannerOption.HelmAPIVersions...)) + } + + if scannerOption.HelmKubeVersion != "" { + opts = append(opts, helm2.ScannerWithKubeVersion(scannerOption.HelmKubeVersion)) + } + return opts } From 68545f19400f618908f40eb7bf8f609f207d787f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Mon, 25 Mar 2024 12:22:46 +0100 Subject: [PATCH 2/5] add docs for config file --- .../references/configuration/config-file.md | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/docs/docs/references/configuration/config-file.md b/docs/docs/references/configuration/config-file.md index 1c94f2eb8db6..f649d2a213b6 100644 --- a/docs/docs/references/configuration/config-file.md +++ b/docs/docs/references/configuration/config-file.md @@ -279,35 +279,39 @@ misconfiguration: - terraform # helm value override configurations - # set individual values helm: + # set individual values set: - securityContext.runAsUser=10001 - # set values with file - helm: + # set values with file values: - overrides.yaml - # set specific values from specific files - helm: + # set specific values from specific files set-file: - image=dev-overrides.yaml - # set as string and preserve type - helm: + # set as string and preserve type set-string: - name=true + # Available API versions used for Capabilities.APIVersions. This flag is the same as the api-versions flag of the helm template command. + api-versions: + - policy/v1/PodDisruptionBudget + - apps/v1/Deployment + + # Kubernetes version used for Capabilities.KubeVersion. This flag is the same as the kube-version flag of the helm template command. + kube-version: "v1.21.0" + # terraform tfvars overrrides terraform: vars: - dev-terraform.tfvars - common-terraform.tfvars - # Same as '--tf-exclude-downloaded-modules' - # Default is false - terraform: + # Same as '--tf-exclude-downloaded-modules' + # Default is false exclude-downloaded-modules: false ``` From cf4e244a86925d601b6b5a03b7d6c5bd6d2ee21c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Tue, 26 Mar 2024 09:15:50 +0100 Subject: [PATCH 3/5] apply suggestions --- pkg/iac/scanners/helm/test/option_test.go | 26 +++++++++++------------ pkg/iac/scanners/helm/test/parser_test.go | 3 +-- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/pkg/iac/scanners/helm/test/option_test.go b/pkg/iac/scanners/helm/test/option_test.go index ab28f508e989..bca7619097c8 100644 --- a/pkg/iac/scanners/helm/test/option_test.go +++ b/pkg/iac/scanners/helm/test/option_test.go @@ -42,8 +42,7 @@ func Test_helm_parser_with_options_with_values_file(t *testing.T) { helmParser, err := parser2.New(chartName, opts...) require.NoError(t, err) - err = helmParser.ParseFS(context.TODO(), os.DirFS(filepath.Join("testdata", chartName)), ".") - require.NoError(t, err) + require.NoError(t, helmParser.ParseFS(context.TODO(), os.DirFS(filepath.Join("testdata", chartName)), ".")) manifests, err := helmParser.RenderedChartFiles() require.NoError(t, err) @@ -172,10 +171,10 @@ func Test_helm_parser_with_options_with_api_versions(t *testing.T) { func Test_helm_parser_with_options_with_kube_versions(t *testing.T) { tests := []struct { - testName string - chartName string - kubeVersion string - error string + testName string + chartName string + kubeVersion string + expectedError string }{ { testName: "Parsing directory 'with-kube-version'", @@ -183,10 +182,10 @@ func Test_helm_parser_with_options_with_kube_versions(t *testing.T) { kubeVersion: "1.60", }, { - testName: "Parsing directory 'with-kube-version' with invalid kube version", - chartName: "with-kube-version", - kubeVersion: "a.b.c", - error: "Invalid Semantic Version", + testName: "Parsing directory 'with-kube-version' with invalid kube version", + chartName: "with-kube-version", + kubeVersion: "a.b.c", + expectedError: "Invalid Semantic Version", }, } @@ -201,13 +200,12 @@ func Test_helm_parser_with_options_with_kube_versions(t *testing.T) { opts = append(opts, parser2.OptionWithKubeVersion(test.kubeVersion)) helmParser, err := parser2.New(chartName, opts...) - if test.error != "" { - require.EqualError(t, err, test.error) + if test.expectedError != "" { + require.EqualError(t, err, test.expectedError) return } require.NoError(t, err) - err = helmParser.ParseFS(context.TODO(), os.DirFS(filepath.Join("testdata", chartName)), ".") - require.NoError(t, err) + require.NoError(t, helmParser.ParseFS(context.TODO(), os.DirFS(filepath.Join("testdata", chartName)), ".")) manifests, err := helmParser.RenderedChartFiles() require.NoError(t, err) diff --git a/pkg/iac/scanners/helm/test/parser_test.go b/pkg/iac/scanners/helm/test/parser_test.go index 89667a211f83..9531796ada4e 100644 --- a/pkg/iac/scanners/helm/test/parser_test.go +++ b/pkg/iac/scanners/helm/test/parser_test.go @@ -37,8 +37,7 @@ func Test_helm_parser(t *testing.T) { helmParser, err := parser.New(chartName) require.NoError(t, err) - err = helmParser.ParseFS(context.TODO(), os.DirFS(filepath.Join("testdata", chartName)), ".") - require.NoError(t, err) + require.NoError(t, helmParser.ParseFS(context.TODO(), os.DirFS(filepath.Join("testdata", chartName)), ".")) manifests, err := helmParser.RenderedChartFiles() require.NoError(t, err) From 34a1871ecd6497cfe9afdfe2ac0e69bfebbb575a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Tue, 26 Mar 2024 09:17:10 +0100 Subject: [PATCH 4/5] apply suggestions --- pkg/iac/scanners/helm/test/parser_test.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pkg/iac/scanners/helm/test/parser_test.go b/pkg/iac/scanners/helm/test/parser_test.go index 9531796ada4e..751649be5e3c 100644 --- a/pkg/iac/scanners/helm/test/parser_test.go +++ b/pkg/iac/scanners/helm/test/parser_test.go @@ -75,8 +75,7 @@ func Test_helm_parser_where_name_non_string(t *testing.T) { helmParser, err := parser.New(chartName) require.NoError(t, err) - err = helmParser.ParseFS(context.TODO(), os.DirFS(filepath.Join("testdata", chartName)), ".") - require.NoError(t, err) + require.NoError(t, helmParser.ParseFS(context.TODO(), os.DirFS(filepath.Join("testdata", chartName)), ".")) } } @@ -166,8 +165,7 @@ func Test_helm_tarball_parser(t *testing.T) { helmParser, err := parser.New(test.archiveFile) require.NoError(t, err) - err = helmParser.ParseFS(context.TODO(), testFs, ".") - require.NoError(t, err) + require.NoError(t, helmParser.ParseFS(context.TODO(), testFs, ".")) manifests, err := helmParser.RenderedChartFiles() require.NoError(t, err) From c397b69ba37339058942de4d36bf713aae8d9387 Mon Sep 17 00:00:00 2001 From: Simar Date: Fri, 5 Apr 2024 22:37:47 -0600 Subject: [PATCH 5/5] fix broken test --- pkg/iac/scanners/helm/test/option_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/iac/scanners/helm/test/option_test.go b/pkg/iac/scanners/helm/test/option_test.go index 3c03cdb67d23..8efb03f16116 100644 --- a/pkg/iac/scanners/helm/test/option_test.go +++ b/pkg/iac/scanners/helm/test/option_test.go @@ -197,9 +197,9 @@ func Test_helm_parser_with_options_with_kube_versions(t *testing.T) { var opts []options.ParserOption - opts = append(opts, parser2.OptionWithKubeVersion(test.kubeVersion)) + opts = append(opts, parser.OptionWithKubeVersion(test.kubeVersion)) - helmParser, err := parser2.New(chartName, opts...) + helmParser, err := parser.New(chartName, opts...) if test.expectedError != "" { require.EqualError(t, err, test.expectedError) return