From bd0c60364a22b489e64953429f97545a44d86cf4 Mon Sep 17 00:00:00 2001 From: Teppei Fukuda Date: Sun, 23 Apr 2023 19:22:46 +0300 Subject: [PATCH] perf(misconf): replace with post-analyzers (#4090) Signed-off-by: Simar Co-authored-by: Simar --- go.mod | 1 - go.sum | 2 - internal/testutil/error.go | 11 + pkg/commands/artifact/run.go | 6 +- pkg/fanal/analyzer/analyzer.go | 39 +- pkg/fanal/analyzer/config/all/import.go | 5 +- .../analyzer/config/azurearm/azurearm.go | 38 ++ .../analyzer/config/azurearm/azurearm_test.go | 35 ++ .../config/cloudformation/cloudformation.go | 30 ++ pkg/fanal/analyzer/config/config.go | 74 +++- pkg/fanal/analyzer/config/config_test.go | 106 +++-- .../analyzer/config/dockerfile/docker.go | 57 +-- .../analyzer/config/dockerfile/docker_test.go | 58 --- .../dockerfile/testdata/Dockerfile.deployment | 3 - .../dockerfile/testdata/Dockerfile.multistage | 6 - pkg/fanal/analyzer/config/helm/helm.go | 124 ++---- pkg/fanal/analyzer/config/helm/helm_test.go | 380 +----------------- .../analyzer/config/helm/testdata/Chart.yaml | 24 -- .../analyzer/config/helm/testdata/nope.tgz | Bin 114 -> 0 bytes .../config/helm/testdata/testchart.tar.gz | Bin 4196 -> 0 bytes .../config/helm/testdata/testchart.tgz | Bin 1787 -> 0 bytes .../helm/testdata/testchart/.helmignore | 23 -- .../config/helm/testdata/testchart/Chart.yaml | 24 -- .../testdata/testchart/templates/NOTES.txt | 22 - .../testdata/testchart/templates/_helpers.tpl | 62 --- .../testchart/templates/deployment.yml | 61 --- .../testdata/testchart/templates/hpa.yaml | 28 -- .../testdata/testchart/templates/ingress.yaml | 61 --- .../testdata/testchart/templates/service.yaml | 15 - .../testchart/templates/serviceaccount.yaml | 12 - .../templates/tests/test-connection.yaml | 15 - .../helm/testdata/testchart/values.yaml | 86 ---- .../analyzer/config/helm/testdata/values.yaml | 86 ---- pkg/fanal/analyzer/config/json/json.go | 65 --- pkg/fanal/analyzer/config/json/json_test.go | 98 ----- .../analyzer/config/json/testdata/array.json | 22 - .../config/json/testdata/deployment.json | 10 - .../config/json/testdata/deployment_deny.json | 10 - pkg/fanal/analyzer/config/k8s/k8s.go | 30 ++ .../analyzer/config/terraform/terraform.go | 59 ++- .../config/terraform/terraform_test.go | 44 -- .../analyzer/config/testdata/docker_deny.rego | 21 - .../config/testdata/docker_multi.rego | 35 -- .../analyzer/config/testdata/docker_non.rego | 20 - .../config/testdata/docker_violation.rego | 13 - .../analyzer/config/testdata/docker_warn.rego | 19 - .../analyzer/config/testdata/kubernetes.rego | 15 - .../analyzer/config/testdata/rego/policy.rego | 32 ++ .../analyzer/config/testdata/src/Dockerfile | 1 + .../analyzer/config/yaml/testdata/anchor.yaml | 15 - .../analyzer/config/yaml/testdata/broken.yaml | 1 - .../yaml/testdata/circular_references.yaml | 3 - .../analyzer/config/yaml/testdata/deny.rego | 13 - .../config/yaml/testdata/deployment.yaml | 6 - .../config/yaml/testdata/deployment_deny.yaml | 6 - .../yaml/testdata/incompatible_json.yaml | 4 - .../config/yaml/testdata/multiple.yaml | 18 - pkg/fanal/analyzer/config/yaml/yaml.go | 61 --- pkg/fanal/analyzer/config/yaml/yaml_test.go | 97 ----- pkg/fanal/analyzer/config_analyzer.go | 2 +- pkg/fanal/analyzer/const.go | 22 +- .../analyzer/imgconf/dockerfile/dockerfile.go | 16 +- pkg/fanal/artifact/artifact.go | 2 +- pkg/fanal/artifact/image/image.go | 28 +- pkg/fanal/artifact/image/image_test.go | 98 ++--- pkg/fanal/artifact/local/fs.go | 18 +- pkg/fanal/artifact/local/fs_test.go | 351 ++++++++++++---- .../testdata/misconfig/mixed/rego/policy.rego | 32 ++ .../testdata/misconfig/mixed/src/main.tf | 3 + .../testdata/misconfig/mixed/src/main.yaml | 6 + .../busted-relative-paths/rego/policy.rego | 32 ++ .../busted-relative-paths/src/child/main.tf | 11 + .../busted-relative-paths/src/parent/more.tf | 3 + pkg/fanal/artifact/remote/git_test.go | 4 +- pkg/fanal/artifact/vm/vm.go | 1 + pkg/fanal/artifact/vm/vm_test.go | 4 +- pkg/fanal/cache/key_test.go | 6 +- pkg/fanal/external/config_scan.go | 4 +- pkg/fanal/external/config_scan_test.go | 3 +- pkg/fanal/handler/all/import.go | 1 - pkg/fanal/handler/misconf/misconf.go | 63 --- pkg/fanal/types/handler.go | 2 - pkg/fanal/types/misconf.go | 4 + pkg/mapfs/file.go | 18 +- pkg/mapfs/fs.go | 19 +- pkg/misconf/scanner.go | 274 ++++++++----- pkg/misconf/scanner_test.go | 99 ++++- 87 files changed, 1168 insertions(+), 2170 deletions(-) create mode 100644 internal/testutil/error.go create mode 100644 pkg/fanal/analyzer/config/azurearm/azurearm.go create mode 100644 pkg/fanal/analyzer/config/azurearm/azurearm_test.go create mode 100644 pkg/fanal/analyzer/config/cloudformation/cloudformation.go delete mode 100644 pkg/fanal/analyzer/config/dockerfile/testdata/Dockerfile.deployment delete mode 100644 pkg/fanal/analyzer/config/dockerfile/testdata/Dockerfile.multistage delete mode 100644 pkg/fanal/analyzer/config/helm/testdata/Chart.yaml delete mode 100644 pkg/fanal/analyzer/config/helm/testdata/nope.tgz delete mode 100644 pkg/fanal/analyzer/config/helm/testdata/testchart.tar.gz delete mode 100644 pkg/fanal/analyzer/config/helm/testdata/testchart.tgz delete mode 100644 pkg/fanal/analyzer/config/helm/testdata/testchart/.helmignore delete mode 100644 pkg/fanal/analyzer/config/helm/testdata/testchart/Chart.yaml delete mode 100644 pkg/fanal/analyzer/config/helm/testdata/testchart/templates/NOTES.txt delete mode 100644 pkg/fanal/analyzer/config/helm/testdata/testchart/templates/_helpers.tpl delete mode 100644 pkg/fanal/analyzer/config/helm/testdata/testchart/templates/deployment.yml delete mode 100644 pkg/fanal/analyzer/config/helm/testdata/testchart/templates/hpa.yaml delete mode 100644 pkg/fanal/analyzer/config/helm/testdata/testchart/templates/ingress.yaml delete mode 100644 pkg/fanal/analyzer/config/helm/testdata/testchart/templates/service.yaml delete mode 100644 pkg/fanal/analyzer/config/helm/testdata/testchart/templates/serviceaccount.yaml delete mode 100644 pkg/fanal/analyzer/config/helm/testdata/testchart/templates/tests/test-connection.yaml delete mode 100644 pkg/fanal/analyzer/config/helm/testdata/testchart/values.yaml delete mode 100644 pkg/fanal/analyzer/config/helm/testdata/values.yaml delete mode 100644 pkg/fanal/analyzer/config/json/json.go delete mode 100644 pkg/fanal/analyzer/config/json/json_test.go delete mode 100644 pkg/fanal/analyzer/config/json/testdata/array.json delete mode 100644 pkg/fanal/analyzer/config/json/testdata/deployment.json delete mode 100644 pkg/fanal/analyzer/config/json/testdata/deployment_deny.json create mode 100644 pkg/fanal/analyzer/config/k8s/k8s.go delete mode 100644 pkg/fanal/analyzer/config/testdata/docker_deny.rego delete mode 100644 pkg/fanal/analyzer/config/testdata/docker_multi.rego delete mode 100644 pkg/fanal/analyzer/config/testdata/docker_non.rego delete mode 100644 pkg/fanal/analyzer/config/testdata/docker_violation.rego delete mode 100644 pkg/fanal/analyzer/config/testdata/docker_warn.rego delete mode 100644 pkg/fanal/analyzer/config/testdata/kubernetes.rego create mode 100644 pkg/fanal/analyzer/config/testdata/rego/policy.rego create mode 100644 pkg/fanal/analyzer/config/testdata/src/Dockerfile delete mode 100644 pkg/fanal/analyzer/config/yaml/testdata/anchor.yaml delete mode 100644 pkg/fanal/analyzer/config/yaml/testdata/broken.yaml delete mode 100644 pkg/fanal/analyzer/config/yaml/testdata/circular_references.yaml delete mode 100644 pkg/fanal/analyzer/config/yaml/testdata/deny.rego delete mode 100644 pkg/fanal/analyzer/config/yaml/testdata/deployment.yaml delete mode 100644 pkg/fanal/analyzer/config/yaml/testdata/deployment_deny.yaml delete mode 100644 pkg/fanal/analyzer/config/yaml/testdata/incompatible_json.yaml delete mode 100644 pkg/fanal/analyzer/config/yaml/testdata/multiple.yaml delete mode 100644 pkg/fanal/analyzer/config/yaml/yaml.go delete mode 100644 pkg/fanal/analyzer/config/yaml/yaml_test.go create mode 100644 pkg/fanal/artifact/local/testdata/misconfig/mixed/rego/policy.rego create mode 100644 pkg/fanal/artifact/local/testdata/misconfig/mixed/src/main.tf create mode 100644 pkg/fanal/artifact/local/testdata/misconfig/mixed/src/main.yaml create mode 100644 pkg/fanal/artifact/local/testdata/misconfig/terraform/busted-relative-paths/rego/policy.rego create mode 100644 pkg/fanal/artifact/local/testdata/misconfig/terraform/busted-relative-paths/src/child/main.tf create mode 100644 pkg/fanal/artifact/local/testdata/misconfig/terraform/busted-relative-paths/src/parent/more.tf delete mode 100644 pkg/fanal/handler/misconf/misconf.go diff --git a/go.mod b/go.mod index dc306c7a48..9d56e9c055 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,6 @@ require ( github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46 github.com/aquasecurity/go-version v0.0.0-20210121072130-637058cfe492 github.com/aquasecurity/loading v0.0.5 - github.com/aquasecurity/memoryfs v1.4.4 github.com/aquasecurity/table v1.8.0 github.com/aquasecurity/testdocker v0.0.0-20230111101738-e741bda259da github.com/aquasecurity/tml v0.6.1 diff --git a/go.sum b/go.sum index b48008bef9..5d730e92ad 100644 --- a/go.sum +++ b/go.sum @@ -333,8 +333,6 @@ github.com/aquasecurity/go-version v0.0.0-20210121072130-637058cfe492 h1:rcEG5HI github.com/aquasecurity/go-version v0.0.0-20210121072130-637058cfe492/go.mod h1:9Beu8XsUNNfzml7WBf3QmyPToP1wm1Gj/Vc5UJKqTzU= github.com/aquasecurity/loading v0.0.5 h1:2iq02sPSSMU+ULFPmk0v0lXnK/eZ2e0dRAj/Dl5TvuM= github.com/aquasecurity/loading v0.0.5/go.mod h1:NSHeeq1JTDTFuXAe87q4yQ2DX57pXiaQMqq8Zm9HCJA= -github.com/aquasecurity/memoryfs v1.4.4 h1:HdkShi6jjKZLAgQ+6/CXXDB/zwH2hAMp2oklo9w5t7A= -github.com/aquasecurity/memoryfs v1.4.4/go.mod h1:kLxvGxhdyG0zmlFUJB6VAkLn4WRPOycLW/UYO6dspao= github.com/aquasecurity/table v1.8.0 h1:9ntpSwrUfjrM6/YviArlx/ZBGd6ix8W+MtojQcM7tv0= github.com/aquasecurity/table v1.8.0/go.mod h1:eqOmvjjB7AhXFgFqpJUEE/ietg7RrMSJZXyTN8E/wZw= github.com/aquasecurity/testdocker v0.0.0-20230111101738-e741bda259da h1:pj/adfN0Wbzc0H8YkI1nX5K92wOU5/1/1TRuuc0y5Nw= diff --git a/internal/testutil/error.go b/internal/testutil/error.go new file mode 100644 index 0000000000..3a24df91c1 --- /dev/null +++ b/internal/testutil/error.go @@ -0,0 +1,11 @@ +package testutil + +import ( + "runtime" + + "github.com/samber/lo" +) + +var ErrNotExist string = lo.Ternary(runtime.GOOS == "windows", + "The system cannot find the file specified.", + "no such file or directory") diff --git a/pkg/commands/artifact/run.go b/pkg/commands/artifact/run.go index 10904192a5..630724e4dc 100644 --- a/pkg/commands/artifact/run.go +++ b/pkg/commands/artifact/run.go @@ -16,13 +16,13 @@ import ( tcache "github.com/aquasecurity/trivy/pkg/cache" "github.com/aquasecurity/trivy/pkg/commands/operation" "github.com/aquasecurity/trivy/pkg/fanal/analyzer" - "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config" "github.com/aquasecurity/trivy/pkg/fanal/artifact" "github.com/aquasecurity/trivy/pkg/fanal/cache" ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" "github.com/aquasecurity/trivy/pkg/flag" "github.com/aquasecurity/trivy/pkg/javadb" "github.com/aquasecurity/trivy/pkg/log" + "github.com/aquasecurity/trivy/pkg/misconf" "github.com/aquasecurity/trivy/pkg/module" "github.com/aquasecurity/trivy/pkg/report" pkgReport "github.com/aquasecurity/trivy/pkg/report" @@ -562,7 +562,7 @@ func initScannerConfig(opts flag.Options, cacheClient cache.Cache) (ScannerConfi } // ScannerOption is filled only when config scanning is enabled. - var configScannerOptions config.ScannerOption + var configScannerOptions misconf.ScannerOption if opts.Scanners.Enabled(types.MisconfigScanner) || opts.ImageConfigScanners.Enabled(types.MisconfigScanner) { log.Logger.Info("Misconfiguration scanning is enabled") @@ -577,7 +577,7 @@ func initScannerConfig(opts flag.Options, cacheClient cache.Cache) (ScannerConfi log.Logger.Debug("Policies successfully loaded from disk") disableEmbedded = true } - configScannerOptions = config.ScannerOption{ + configScannerOptions = misconf.ScannerOption{ Trace: opts.Trace, Namespaces: append(opts.PolicyNamespaces, defaultPolicyNamespaces...), PolicyPaths: append(opts.PolicyPaths, downloadedPolicyPaths...), diff --git a/pkg/fanal/analyzer/analyzer.go b/pkg/fanal/analyzer/analyzer.go index 30267b68bc..46d91f1f71 100644 --- a/pkg/fanal/analyzer/analyzer.go +++ b/pkg/fanal/analyzer/analyzer.go @@ -20,6 +20,7 @@ import ( "github.com/aquasecurity/trivy/pkg/fanal/log" "github.com/aquasecurity/trivy/pkg/fanal/types" "github.com/aquasecurity/trivy/pkg/mapfs" + "github.com/aquasecurity/trivy/pkg/misconf" "github.com/aquasecurity/trivy/pkg/syncx" ) @@ -45,6 +46,7 @@ type AnalyzerOptions struct { Slow bool FilePatterns []string DisabledAnalyzers []Type + MisconfScannerOption misconf.ScannerOption SecretScannerOption SecretScannerOption LicenseScannerOption LicenseScannerOption } @@ -153,13 +155,11 @@ type AnalysisResult struct { Repository *types.Repository PackageInfos []types.PackageInfo Applications []types.Application + Misconfigurations []types.Misconfiguration Secrets []types.Secret Licenses []types.LicenseFile SystemInstalledFiles []string // A list of files installed by OS package manager - // Files holds necessary file contents for the respective post-handler - Files map[types.HandlerType][]types.File - // Digests contains SHA-256 digests of unpackaged files // used to search for SBOM attestation. Digests map[string]string @@ -174,14 +174,13 @@ type AnalysisResult struct { func NewAnalysisResult() *AnalysisResult { result := new(AnalysisResult) - result.Files = map[types.HandlerType][]types.File{} return result } func (r *AnalysisResult) isEmpty() bool { return lo.IsEmpty(r.OS) && r.Repository == nil && len(r.PackageInfos) == 0 && len(r.Applications) == 0 && - len(r.Secrets) == 0 && len(r.Licenses) == 0 && len(r.SystemInstalledFiles) == 0 && - r.BuildInfo == nil && len(r.Files) == 0 && len(r.Digests) == 0 && len(r.CustomResources) == 0 + len(r.Misconfigurations) == 0 && len(r.Secrets) == 0 && len(r.Licenses) == 0 && len(r.SystemInstalledFiles) == 0 && + r.BuildInfo == nil && len(r.Digests) == 0 && len(r.CustomResources) == 0 } func (r *AnalysisResult) Sort() { @@ -213,11 +212,10 @@ func (r *AnalysisResult) Sort() { return r.CustomResources[i].FilePath < r.CustomResources[j].FilePath }) - for _, files := range r.Files { - sort.Slice(files, func(i, j int) bool { - return files[i].Path < files[j].Path - }) - } + // Misconfigurations + sort.Slice(r.Misconfigurations, func(i, j int) bool { + return r.Misconfigurations[i].FilePath < r.Misconfigurations[j].FilePath + }) // Secrets sort.Slice(r.Secrets, func(i, j int) bool { @@ -274,14 +272,7 @@ func (r *AnalysisResult) Merge(new *AnalysisResult) { r.Digests = lo.Assign(r.Digests, new.Digests) } - for t, files := range new.Files { - if v, ok := r.Files[t]; ok { - r.Files[t] = append(v, files...) - } else { - r.Files[t] = files - } - } - + r.Misconfigurations = append(r.Misconfigurations, new.Misconfigurations...) r.Secrets = append(r.Secrets, new.Secrets...) r.Licenses = append(r.Licenses, new.Licenses...) r.SystemInstalledFiles = append(r.SystemInstalledFiles, new.SystemInstalledFiles...) @@ -400,6 +391,9 @@ func (ag AnalyzerGroup) AnalyzerVersions() Versions { } } +// AnalyzeFile determines which files are required by the analyzers based on the file name and attributes, +// and passes only those files to the analyzer for analysis. +// This function may be called concurrently and must be thread-safe. func (ag AnalyzerGroup) AnalyzeFile(ctx context.Context, wg *sync.WaitGroup, limit *semaphore.Weighted, result *AnalysisResult, dir, filePath string, info os.FileInfo, opener Opener, disabled []Type, opts AnalysisOptions) error { if info.IsDir() { @@ -454,19 +448,24 @@ func (ag AnalyzerGroup) AnalyzeFile(ctx context.Context, wg *sync.WaitGroup, lim return nil } +// RequiredPostAnalyzers returns a list of analyzer types that require the given file. func (ag AnalyzerGroup) RequiredPostAnalyzers(filePath string, info os.FileInfo) []Type { if info.IsDir() { return nil } var postAnalyzerTypes []Type for _, a := range ag.postAnalyzers { - if a.Required(filePath, info) { + if ag.filePatternMatch(a.Type(), filePath) || a.Required(filePath, info) { postAnalyzerTypes = append(postAnalyzerTypes, a.Type()) } } return postAnalyzerTypes } +// PostAnalyze passes a virtual filesystem containing only required files +// and passes it to the respective post-analyzer. +// The obtained results are merged into the "result". +// This function may be called concurrently and must be thread-safe. func (ag AnalyzerGroup) PostAnalyze(ctx context.Context, files *syncx.Map[Type, *mapfs.FS], result *AnalysisResult, opts AnalysisOptions) error { for _, a := range ag.postAnalyzers { fsys, ok := files.Load(a.Type()) diff --git a/pkg/fanal/analyzer/config/all/import.go b/pkg/fanal/analyzer/config/all/import.go index 75b01e10e8..0f27286c3e 100644 --- a/pkg/fanal/analyzer/config/all/import.go +++ b/pkg/fanal/analyzer/config/all/import.go @@ -1,9 +1,10 @@ package all import ( + _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/azurearm" + _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/cloudformation" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/dockerfile" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/helm" - _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/json" + _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/k8s" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/terraform" - _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/yaml" ) diff --git a/pkg/fanal/analyzer/config/azurearm/azurearm.go b/pkg/fanal/analyzer/config/azurearm/azurearm.go new file mode 100644 index 0000000000..3c0c4b9828 --- /dev/null +++ b/pkg/fanal/analyzer/config/azurearm/azurearm.go @@ -0,0 +1,38 @@ +package azurearm + +import ( + "os" + "path/filepath" + + "github.com/aquasecurity/trivy/pkg/fanal/analyzer" + "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config" + "github.com/aquasecurity/trivy/pkg/misconf" +) + +const ( + version = 1 + analyzerType = analyzer.TypeAzureARM +) + +func init() { + analyzer.RegisterPostAnalyzer(analyzerType, newAzureARMConfigAnalyzer) +} + +// azureARMConfigAnalyzer is an analyzer for detecting misconfigurations in Azure ARM templates. +// It embeds config.Analyzer so it can implement analyzer.PostAnalyzer. +type azureARMConfigAnalyzer struct { + *config.Analyzer +} + +func newAzureARMConfigAnalyzer(opts analyzer.AnalyzerOptions) (analyzer.PostAnalyzer, error) { + a, err := config.NewAnalyzer(analyzerType, version, misconf.NewAzureARMScanner, opts) + if err != nil { + return nil, err + } + return &azureARMConfigAnalyzer{Analyzer: a}, nil +} + +// Required overrides config.Analyzer.Required() and check if the given file is JSON. +func (a *azureARMConfigAnalyzer) Required(filePath string, _ os.FileInfo) bool { + return filepath.Ext(filePath) == ".json" +} diff --git a/pkg/fanal/analyzer/config/azurearm/azurearm_test.go b/pkg/fanal/analyzer/config/azurearm/azurearm_test.go new file mode 100644 index 0000000000..19039676e3 --- /dev/null +++ b/pkg/fanal/analyzer/config/azurearm/azurearm_test.go @@ -0,0 +1,35 @@ +package azurearm + +import ( + "testing" + + "github.com/aquasecurity/trivy/pkg/fanal/analyzer" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_azureARMConfigAnalyzer_Required(t *testing.T) { + tests := []struct { + name string + filePath string + want bool + }{ + { + name: "json", + filePath: "test.json", + want: true, + }, + { + name: "yaml", + filePath: "test.yaml", + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a, err := newAzureARMConfigAnalyzer(analyzer.AnalyzerOptions{}) + require.NoError(t, err) + assert.Equal(t, tt.want, a.Required(tt.filePath, nil)) + }) + } +} diff --git a/pkg/fanal/analyzer/config/cloudformation/cloudformation.go b/pkg/fanal/analyzer/config/cloudformation/cloudformation.go new file mode 100644 index 0000000000..06f7e458a8 --- /dev/null +++ b/pkg/fanal/analyzer/config/cloudformation/cloudformation.go @@ -0,0 +1,30 @@ +package cloudformation + +import ( + "github.com/aquasecurity/trivy/pkg/fanal/analyzer" + "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config" + "github.com/aquasecurity/trivy/pkg/misconf" +) + +const ( + analyzerType = analyzer.TypeCloudFormation + version = 1 +) + +func init() { + analyzer.RegisterPostAnalyzer(analyzerType, newCloudFormationConfigAnalyzer) +} + +// cloudFormationConfigAnalyzer is an analyzer for detecting misconfigurations in CloudFormation files. +// It embeds config.Analyzer so it can implement analyzer.PostAnalyzer. +type cloudFormationConfigAnalyzer struct { + *config.Analyzer +} + +func newCloudFormationConfigAnalyzer(opts analyzer.AnalyzerOptions) (analyzer.PostAnalyzer, error) { + a, err := config.NewAnalyzer(analyzerType, version, misconf.NewCloudFormationScanner, opts) + if err != nil { + return nil, err + } + return &cloudFormationConfigAnalyzer{Analyzer: a}, nil +} diff --git a/pkg/fanal/analyzer/config/config.go b/pkg/fanal/analyzer/config/config.go index 6b24ac539a..67ecb62c8d 100644 --- a/pkg/fanal/analyzer/config/config.go +++ b/pkg/fanal/analyzer/config/config.go @@ -1,27 +1,65 @@ package config import ( - "sort" + "context" + "os" + "path/filepath" + + "golang.org/x/xerrors" + "k8s.io/utils/strings/slices" + + "github.com/aquasecurity/trivy/pkg/fanal/analyzer" + "github.com/aquasecurity/trivy/pkg/misconf" ) -type ScannerOption struct { - Trace bool - RegoOnly bool - Namespaces []string - PolicyPaths []string - DataPaths []string - DisableEmbeddedPolicies bool +var ( + _ analyzer.PostAnalyzer = (*Analyzer)(nil) - HelmValues []string - HelmValueFiles []string - HelmFileValues []string - HelmStringValues []string - TerraformTFVars []string - K8sVersion string + requiredExts = []string{".json", ".yaml", ".yml"} +) + +// Analyzer represents an analyzer for config files, +// which is embedded into each config analyzer such as Kubernetes. +type Analyzer struct { + typ analyzer.Type + version int + scanner *misconf.Scanner } -func (o *ScannerOption) Sort() { - sort.Strings(o.Namespaces) - sort.Strings(o.PolicyPaths) - sort.Strings(o.DataPaths) +type NewScanner func([]string, misconf.ScannerOption) (*misconf.Scanner, error) + +func NewAnalyzer(t analyzer.Type, version int, newScanner NewScanner, opts analyzer.AnalyzerOptions) (*Analyzer, error) { + s, err := newScanner(opts.FilePatterns, opts.MisconfScannerOption) + if err != nil { + return nil, xerrors.Errorf("%s scanner init error: %w", t, err) + } + return &Analyzer{ + typ: t, + version: version, + scanner: s, + }, nil +} + +// PostAnalyze performs configuration analysis on the input filesystem and detect misconfigurations. +func (a *Analyzer) PostAnalyze(ctx context.Context, input analyzer.PostAnalysisInput) (*analyzer.AnalysisResult, error) { + misconfs, err := a.scanner.Scan(ctx, input.FS) + if err != nil { + return nil, xerrors.Errorf("%s scan error: %w", a.typ, err) + } + return &analyzer.AnalysisResult{Misconfigurations: misconfs}, nil +} + +// Required checks if the given file path has one of the required file extensions. +func (a *Analyzer) Required(filePath string, _ os.FileInfo) bool { + return slices.Contains(requiredExts, filepath.Ext(filePath)) +} + +// Type returns the analyzer type of the current Analyzer instance. +func (a *Analyzer) Type() analyzer.Type { + return a.typ +} + +// Version returns the version of the current Analyzer instance. +func (a *Analyzer) Version() int { + return a.version } diff --git a/pkg/fanal/analyzer/config/config_test.go b/pkg/fanal/analyzer/config/config_test.go index 3aac67e6f4..ef0a86b630 100644 --- a/pkg/fanal/analyzer/config/config_test.go +++ b/pkg/fanal/analyzer/config/config_test.go @@ -1,59 +1,107 @@ package config_test import ( + "context" + "os" "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/aquasecurity/defsec/pkg/detection" + "github.com/aquasecurity/trivy/internal/testutil" + "github.com/aquasecurity/trivy/pkg/fanal/analyzer" "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config" + "github.com/aquasecurity/trivy/pkg/fanal/types" + "github.com/aquasecurity/trivy/pkg/misconf" ) -func TestScannerOption_Sort(t *testing.T) { +func TestAnalyzer_PostAnalyze(t *testing.T) { type fields struct { - Namespaces []string - PolicyPaths []string - DataPaths []string + typ analyzer.Type + newScanner config.NewScanner + opts analyzer.AnalyzerOptions } tests := []struct { - name string - fields fields - want config.ScannerOption + name string + fields fields + dir string + want *analyzer.AnalysisResult + wantErr string }{ { - name: "happy path", + name: "dockerfile", fields: fields{ - Namespaces: []string{"main", "custom", "default"}, - PolicyPaths: []string{"policy"}, - DataPaths: []string{"data/b", "data/c", "data/a"}, + typ: analyzer.TypeDockerfile, + newScanner: misconf.NewDockerfileScanner, + opts: analyzer.AnalyzerOptions{ + MisconfScannerOption: misconf.ScannerOption{ + Namespaces: []string{"user"}, + PolicyPaths: []string{"testdata/rego"}, + DisableEmbeddedPolicies: true, + }, + }, }, - want: config.ScannerOption{ - Namespaces: []string{"custom", "default", "main"}, - PolicyPaths: []string{"policy"}, - DataPaths: []string{"data/a", "data/b", "data/c"}, + dir: "testdata/src", + want: &analyzer.AnalysisResult{ + Misconfigurations: []types.Misconfiguration{ + { + FileType: string(detection.FileTypeDockerfile), + FilePath: "Dockerfile", + Successes: types.MisconfResults{ + types.MisconfResult{ + Namespace: "user.something", + Query: "data.user.something.deny", + PolicyMetadata: types.PolicyMetadata{ + ID: "TEST001", + AVDID: "AVD-TEST-0001", + Type: "Dockerfile Security Check", + Title: "Test policy", + Description: "This is a test policy.", + Severity: "LOW", + RecommendedActions: "Have a cup of tea.", + References: []string{"https://trivy.dev/"}, + }, + CauseMetadata: types.CauseMetadata{ + Provider: "Generic", + Service: "general", + }, + }, + }, + }, + }, }, }, { - name: "missing some fields", + name: "non-existent dir", fields: fields{ - Namespaces: []string{"main"}, - PolicyPaths: nil, - DataPaths: nil, - }, - want: config.ScannerOption{ - Namespaces: []string{"main"}, + typ: analyzer.TypeDockerfile, + newScanner: misconf.NewDockerfileScanner, + opts: analyzer.AnalyzerOptions{ + MisconfScannerOption: misconf.ScannerOption{ + Namespaces: []string{"user"}, + PolicyPaths: []string{"testdata/rego"}, + DisableEmbeddedPolicies: true, + }, + }, }, + dir: "testdata/non-existent", + wantErr: testutil.ErrNotExist, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - o := config.ScannerOption{ - Namespaces: tt.fields.Namespaces, - PolicyPaths: tt.fields.PolicyPaths, - DataPaths: tt.fields.DataPaths, - } - o.Sort() + a, err := config.NewAnalyzer(tt.fields.typ, 0, tt.fields.newScanner, tt.fields.opts) + require.NoError(t, err) - assert.Equal(t, tt.want, o) + got, err := a.PostAnalyze(context.Background(), analyzer.PostAnalysisInput{ + FS: os.DirFS(tt.dir), + }) + if tt.wantErr != "" { + assert.ErrorContains(t, err, tt.wantErr) + return + } + assert.Equal(t, tt.want, got) }) } } diff --git a/pkg/fanal/analyzer/config/dockerfile/docker.go b/pkg/fanal/analyzer/config/dockerfile/docker.go index 666bad5f3f..353cef4eb6 100644 --- a/pkg/fanal/analyzer/config/dockerfile/docker.go +++ b/pkg/fanal/analyzer/config/dockerfile/docker.go @@ -1,51 +1,44 @@ package dockerfile import ( - "context" - "io" "os" "path/filepath" "strings" - "golang.org/x/xerrors" - "github.com/aquasecurity/trivy/pkg/fanal/analyzer" - "github.com/aquasecurity/trivy/pkg/fanal/types" + "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config" + "github.com/aquasecurity/trivy/pkg/misconf" ) -func init() { - analyzer.RegisterAnalyzer(&dockerConfigAnalyzer{}) -} - -const version = 1 +const ( + version = 1 + analyzerType = analyzer.TypeDockerfile +) var requiredFiles = []string{"Dockerfile", "Containerfile"} -type dockerConfigAnalyzer struct{} +func init() { + analyzer.RegisterPostAnalyzer(analyzerType, newDockerfileConfigAnalyzer) +} -func (s dockerConfigAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) { - b, err := io.ReadAll(input.Content) +// dockerConfigAnalyzer is an analyzer for detecting misconfigurations in Dockerfiles. +// It embeds config.Analyzer so it can implement analyzer.PostAnalyzer. +type dockerConfigAnalyzer struct { + *config.Analyzer +} + +func newDockerfileConfigAnalyzer(opts analyzer.AnalyzerOptions) (analyzer.PostAnalyzer, error) { + a, err := config.NewAnalyzer(analyzerType, version, misconf.NewDockerfileScanner, opts) if err != nil { - return nil, xerrors.Errorf("failed to read %s: %w", input.FilePath, err) + return nil, err } - - return &analyzer.AnalysisResult{ - Files: map[types.HandlerType][]types.File{ - // It will be passed to misconfig post handler - types.MisconfPostHandler: { - { - Type: types.Dockerfile, - Path: input.FilePath, - Content: b, - }, - }, - }, - }, nil + return &dockerConfigAnalyzer{Analyzer: a}, nil } // Required does a case-insensitive check for filePath and returns true if // filePath equals/startsWith/hasExtension requiredFiles -func (s dockerConfigAnalyzer) Required(filePath string, _ os.FileInfo) bool { +// It overrides config.Analyzer.Required(). +func (a *dockerConfigAnalyzer) Required(filePath string, _ os.FileInfo) bool { base := filepath.Base(filePath) ext := filepath.Ext(base) for _, file := range requiredFiles { @@ -59,11 +52,3 @@ func (s dockerConfigAnalyzer) Required(filePath string, _ os.FileInfo) bool { return false } - -func (s dockerConfigAnalyzer) Type() analyzer.Type { - return analyzer.TypeDockerfile -} - -func (s dockerConfigAnalyzer) Version() int { - return version -} diff --git a/pkg/fanal/analyzer/config/dockerfile/docker_test.go b/pkg/fanal/analyzer/config/dockerfile/docker_test.go index 7fd0485a7c..1a791be8bf 100644 --- a/pkg/fanal/analyzer/config/dockerfile/docker_test.go +++ b/pkg/fanal/analyzer/config/dockerfile/docker_test.go @@ -1,62 +1,11 @@ package dockerfile import ( - "context" - "strings" "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/fanal/analyzer" - "github.com/aquasecurity/trivy/pkg/fanal/types" ) -func Test_dockerConfigAnalyzer_Analyze(t *testing.T) { - tests := []struct { - name string - inputFile string - want *analyzer.AnalysisResult - wantErr string - }{ - { - name: "happy path", - inputFile: "testdata/Dockerfile.deployment", - want: &analyzer.AnalysisResult{ - Files: map[types.HandlerType][]types.File{ - types.MisconfPostHandler: { - { - Type: types.Dockerfile, - Path: "testdata/Dockerfile.deployment", - Content: []byte(`FROM scratch`), - }, - }, - }, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := strings.NewReader("FROM scratch") - a := dockerConfigAnalyzer{} - ctx := context.Background() - got, err := a.Analyze(ctx, analyzer.AnalysisInput{ - FilePath: tt.inputFile, - Content: r, - }) - - if tt.wantErr != "" { - require.NotNil(t, err) - assert.Contains(t, err.Error(), tt.wantErr) - return - } - assert.NoError(t, err) - assert.Equal(t, tt.want, got) - }) - } -} - func Test_dockerConfigAnalyzer_Required(t *testing.T) { tests := []struct { name string @@ -122,10 +71,3 @@ func Test_dockerConfigAnalyzer_Required(t *testing.T) { }) } } - -func Test_dockerConfigAnalyzer_Type(t *testing.T) { - s := dockerConfigAnalyzer{} - want := analyzer.TypeDockerfile - got := s.Type() - assert.Equal(t, want, got) -} diff --git a/pkg/fanal/analyzer/config/dockerfile/testdata/Dockerfile.deployment b/pkg/fanal/analyzer/config/dockerfile/testdata/Dockerfile.deployment deleted file mode 100644 index b969a117ab..0000000000 --- a/pkg/fanal/analyzer/config/dockerfile/testdata/Dockerfile.deployment +++ /dev/null @@ -1,3 +0,0 @@ -FROM foo -COPY . / -RUN echo hello diff --git a/pkg/fanal/analyzer/config/dockerfile/testdata/Dockerfile.multistage b/pkg/fanal/analyzer/config/dockerfile/testdata/Dockerfile.multistage deleted file mode 100644 index 590e36a241..0000000000 --- a/pkg/fanal/analyzer/config/dockerfile/testdata/Dockerfile.multistage +++ /dev/null @@ -1,6 +0,0 @@ -FROM foo AS build -COPY . / -RUN echo hello - -FROM scratch -COPY --from=build /bar /bar diff --git a/pkg/fanal/analyzer/config/helm/helm.go b/pkg/fanal/analyzer/config/helm/helm.go index 026f194ddd..14ea6aff63 100644 --- a/pkg/fanal/analyzer/config/helm/helm.go +++ b/pkg/fanal/analyzer/config/helm/helm.go @@ -1,76 +1,49 @@ package helm import ( - "archive/tar" - "bytes" - "compress/gzip" - "context" - "errors" - "io" "os" "path/filepath" "strings" - "golang.org/x/xerrors" - - dio "github.com/aquasecurity/go-dep-parser/pkg/io" "github.com/aquasecurity/trivy/pkg/fanal/analyzer" - "github.com/aquasecurity/trivy/pkg/fanal/types" + "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config" + "github.com/aquasecurity/trivy/pkg/misconf" ) +const ( + analyzerType = analyzer.TypeHelm + version = 1 + maxTarSize = 209_715_200 // 200MB +) + +var acceptedExts = []string{".tpl", ".json", ".yml", ".yaml", ".tar", ".tgz", ".tar.gz"} + func init() { - analyzer.RegisterAnalyzer(&helmConfigAnalyzer{}) + analyzer.RegisterPostAnalyzer(analyzerType, newHelmConfigAnalyzer) } -const version = 1 +// helmConfigAnalyzer is an analyzer for detecting misconfigurations in Helm charts. +// It embeds config.Analyzer so it can implement analyzer.PostAnalyzer. +type helmConfigAnalyzer struct { + *config.Analyzer +} -const maxTarSize = 209_715_200 // 200MB - -type helmConfigAnalyzer struct{} - -func (a helmConfigAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) { - isAnArchive := false - if isArchive(input.FilePath) { - isAnArchive = true - if !isHelmChart(input.FilePath, input.Content) { - return nil, nil - } - // reset the content - _, err := input.Content.Seek(0, 0) - if err != nil { - return nil, err - } - } - b, err := io.ReadAll(input.Content) +func newHelmConfigAnalyzer(opts analyzer.AnalyzerOptions) (analyzer.PostAnalyzer, error) { + a, err := config.NewAnalyzer(analyzerType, version, misconf.NewHelmScanner, opts) if err != nil { - return nil, xerrors.Errorf("failed to read %s: %w", input.FilePath, err) + return nil, err } - if !isAnArchive { - // if it's not an archive we need to remove the carriage returns - b = bytes.ReplaceAll(b, []byte("\r"), []byte("")) - } - - return &analyzer.AnalysisResult{ - Files: map[types.HandlerType][]types.File{ - // it will be passed to misconfig post handler - types.MisconfPostHandler: { - { - Type: types.Helm, - Path: input.FilePath, - Content: b, - }, - }, - }, - }, nil + return &helmConfigAnalyzer{Analyzer: a}, nil } -func (a helmConfigAnalyzer) Required(filePath string, info os.FileInfo) bool { +// Required overrides config.Analyzer.Required() and checks if the given file is a Helm chart. +func (*helmConfigAnalyzer) Required(filePath string, info os.FileInfo) bool { if info.Size() > maxTarSize { // tarball is too big to be Helm chart - move on return false } - for _, acceptable := range []string{".tpl", ".json", ".yml", ".yaml", ".tar", ".tgz", ".tar.gz"} { + for _, acceptable := range acceptedExts { if strings.HasSuffix(strings.ToLower(filePath), acceptable) { return true } @@ -85,54 +58,3 @@ func (a helmConfigAnalyzer) Required(filePath string, info os.FileInfo) bool { return false } - -func (helmConfigAnalyzer) Type() analyzer.Type { - return analyzer.TypeHelm -} - -func (helmConfigAnalyzer) Version() int { - return version -} - -func isHelmChart(path string, file dio.ReadSeekerAt) bool { - - var err error - var fr io.Reader = file - - if isGzip(path) { - if fr, err = gzip.NewReader(file); err != nil { - return false - } - } - tr := tar.NewReader(fr) - - for { - header, err := tr.Next() - if err != nil { - if errors.Is(err, io.EOF) { - break - } - return false - } - - if header.Typeflag == tar.TypeReg && strings.HasSuffix(header.Name, "Chart.yaml") { - return true - } - } - return false -} - -func isArchive(path string) bool { - if strings.HasSuffix(path, ".tar") || isGzip(path) { - return true - } - return false -} - -func isGzip(path string) bool { - if strings.HasSuffix(path, ".tgz") || - strings.HasSuffix(path, ".tar.gz") { - return true - } - return false -} diff --git a/pkg/fanal/analyzer/config/helm/helm_test.go b/pkg/fanal/analyzer/config/helm/helm_test.go index d7e1bff62c..982020769f 100644 --- a/pkg/fanal/analyzer/config/helm/helm_test.go +++ b/pkg/fanal/analyzer/config/helm/helm_test.go @@ -1,361 +1,13 @@ package helm import ( - "context" "os" - "path/filepath" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/fanal/analyzer" - "github.com/aquasecurity/trivy/pkg/fanal/types" ) -func Test_helmConfigAnalyzer_Analyze(t *testing.T) { - type args struct { - namespaces []string - policyPaths []string - } - tests := []struct { - name string - args args - inputFile string - want *analyzer.AnalysisResult - wantErr string - }{ - { - name: "Chart.yaml", - args: args{ - namespaces: []string{"main"}, - policyPaths: []string{"../testdata/kubernetes.rego"}, - }, - inputFile: filepath.Join("testdata", "Chart.yaml"), - want: &analyzer.AnalysisResult{ - Files: map[types.HandlerType][]types.File{ - types.MisconfPostHandler: { - { - Type: "helm", - Path: filepath.Join("testdata", "Chart.yaml"), - Content: []byte(`apiVersion: v2 -name: testchart -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" -`), - }, - }, - }, - }, - }, - { - name: "values.yaml", - args: args{ - namespaces: []string{"main"}, - policyPaths: []string{"../testdata/kubernetes.rego"}, - }, - inputFile: filepath.Join("testdata", "values.yaml"), - want: &analyzer.AnalysisResult{ - Files: map[types.HandlerType][]types.File{ - types.MisconfPostHandler: { - { - Type: "helm", - Path: filepath.Join("testdata", "values.yaml"), - Content: []byte(`# Default values for testchart. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. - -replicaCount: 1 - -image: - repository: nginx - pullPolicy: IfNotPresent - # Overrides the image tag whose default is the chart appVersion. - tag: "" - -imagePullSecrets: [] -nameOverride: "" -fullnameOverride: "" - -serviceAccount: - # Specifies whether a service account should be created - create: true - # Annotations to add to the service account - annotations: {} - # The name of the service account to use. - # If not set and create is true, a name is generated using the fullname template - name: "" - -podAnnotations: {} - -podSecurityContext: - {} -# fsGroup: 2000 - -securityContext: - {} - # capabilities: - # drop: - # - ALL - # readOnlyRootFilesystem: true - # runAsNonRoot: true -# runAsUser: 1000 - -service: - type: ClusterIP - port: 80 - -ingress: - enabled: false - className: "" - annotations: - {} - # kubernetes.io/ingress.class: nginx - # kubernetes.io/tls-acme: "true" - hosts: - - host: chart-example.local - paths: - - path: / - pathType: ImplementationSpecific - tls: [] - # - secretName: chart-example-tls - # hosts: - # - chart-example.local - -resources: - {} - # We usually recommend not to specify default resources and to leave this as a conscious - # choice for the user. This also increases chances charts run on environments with little - # resources, such as Minikube. If you do want to specify resources, uncomment the following - # lines, adjust them as necessary, and remove the curly braces after 'resources:'. - # limits: - # cpu: 100m - # memory: 128Mi - # requests: - # cpu: 100m -# memory: 128Mi - -autoscaling: - enabled: false - minReplicas: 1 - maxReplicas: 100 - targetCPUUtilizationPercentage: 80 - # targetMemoryUtilizationPercentage: 80 - -nodeSelector: {} - -tolerations: [] - -affinity: {} -`), - }, - }, - }, - }, - }, - { - name: "testchart.tgz", - args: args{ - namespaces: []string{"main"}, - policyPaths: []string{"../testdata/kubernetes.rego"}, - }, - inputFile: filepath.Join("testdata", "testchart.tgz"), - want: &analyzer.AnalysisResult{ - Files: map[types.HandlerType][]types.File{ - types.MisconfPostHandler: { - { - Type: "helm", - Path: filepath.Join("testdata", "testchart.tgz"), - Content: []uint8{ - 0x1f, 0x8b, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0xed, 0x58, 0x5b, 0x6f, 0xdb, 0x36, - 0x14, 0xce, 0xb3, 0x7e, 0x5, 0xd7, 0x3c, 0xa4, 0x2d, 0x1a, 0xd9, 0x4a, 0xec, 0xa4, 0xf0, - 0x5b, 0x90, 0xec, 0x12, 0x2c, 0x69, 0x83, 0xa6, 0xed, 0x30, 0xc, 0xc3, 0x40, 0x4b, 0xb4, - 0xcd, 0x85, 0x22, 0x55, 0x92, 0x72, 0xe2, 0xe, 0xdb, 0x6f, 0xdf, 0x77, 0x48, 0xc9, 0x76, - 0x9c, 0xa6, 0xed, 0x43, 0x9b, 0x62, 0x98, 0xce, 0x83, 0x6d, 0x51, 0x87, 0xe7, 0x7e, - 0xb5, 0x17, 0xce, 0xe7, 0x33, 0x6e, 0x7d, 0x6f, 0xeb, 0xab, 0x41, 0x1f, 0x70, 0x38, - 0x1c, 0xd2, 0x77, 0x76, 0x38, 0xec, 0xc7, 0xef, 0xfd, 0xf0, 0xdd, 0xc2, 0x56, 0x36, - 0xd8, 0x1b, 0x64, 0x7b, 0xd9, 0x60, 0x98, 0x1, 0x2f, 0xcb, 0xe, 0x7, 0x87, 0x5b, 0x6c, - 0xf8, 0xf5, 0x44, 0x5a, 0x41, 0xed, 0x3c, 0xb7, 0x8c, 0x6d, 0x99, 0x6b, 0xa1, 0x3f, - 0x8e, 0x27, 0xac, 0x7b, 0x8, 0x81, 0x1e, 0x16, 0xfc, 0xd2, 0xff, 0xc7, 0xf4, 0x99, 0x2e, - 0x78, 0xa9, 0xbe, 0x34, 0xf, 0x72, 0xf0, 0xc1, 0x60, 0x70, 0x9f, 0xff, 0xf7, 0xb2, 0xc3, - 0x2c, 0xf8, 0xbf, 0x3f, 0x18, 0xec, 0xed, 0xf, 0xf6, 0xe0, 0xff, 0xfd, 0x83, 0xc3, 0xe1, - 0x16, 0xeb, 0x7f, 0x69, 0x41, 0x3e, 0x4, 0xff, 0x73, 0xff, 0xf3, 0x4a, 0xbe, 0x85, 0x62, - 0xd2, 0xe8, 0x11, 0x9b, 0xef, 0x25, 0x9a, 0x97, 0x62, 0xc4, 0x96, 0x41, 0x91, 0x14, - 0xc2, 0xe5, 0x56, 0x56, 0x3e, 0xbc, 0x3f, 0x62, 0x3f, 0x9, 0x55, 0xb2, 0xf0, 0x86, 0x4d, - 0x8c, 0x65, 0x3f, 0xd7, 0x63, 0x61, 0xb5, 0x0, 0x7a, 0x92, 0x6c, 0xe3, 0x75, 0x7c, 0x93, - 0x73, 0xcd, 0xc6, 0x82, 0x9, 0xe9, 0x67, 0xc2, 0x32, 0x3c, 0xec, 0xf0, 0xaa, 0x52, 0x32, - 0xe7, 0x44, 0x65, 0x87, 0xe1, 0x1e, 0x67, 0x3b, 0x4a, 0x8e, 0x2d, 0xb7, 0x8b, 0x9d, - 0x78, 0x27, 0x4d, 0xb6, 0x89, 0xc0, 0xa, 0x2d, 0x1e, 0x3b, 0xc6, 0xad, 0x0, 0x76, 0x6e, - 0x94, 0x12, 0x79, 0x38, 0x37, 0x13, 0x48, 0x57, 0x56, 0x8a, 0x83, 0x29, 0xf3, 0x33, - 0xbe, 0x64, 0x57, 0xf1, 0xfc, 0x8a, 0x4f, 0x45, 0xc1, 0xa4, 0xf6, 0x86, 0xcd, 0xa3, - 0x4e, 0x78, 0xe4, 0x36, 0x9f, 0xc9, 0x39, 0x24, 0xdc, 0x66, 0x38, 0x7, 0x62, 0x21, 0x2a, - 0x65, 0x16, 0xa2, 0x88, 0x3c, 0xcf, 0xa2, 0x1c, 0x2d, 0xbf, 0xca, 0x9a, 0xb9, 0x2c, 0x4, - 0x83, 0xb7, 0x27, 0xb5, 0x62, 0xb5, 0x97, 0x4a, 0x7a, 0x9, 0x56, 0x90, 0x7a, 0x52, 0xeb, - 0x20, 0x83, 0xb, 0xba, 0x43, 0xb9, 0x46, 0xdf, 0x42, 0xcc, 0x85, 0x32, 0x95, 0xb0, 0x29, - 0x7b, 0x3d, 0x13, 0x8b, 0x1d, 0x88, 0x2c, 0x75, 0xae, 0xea, 0x82, 0xb8, 0x13, 0x5f, - 0x4e, 0x3c, 0x85, 0x2e, 0x84, 0xce, 0x17, 0xa4, 0x0, 0xbf, 0xab, 0x27, 0x44, 0x93, 0xfa, - 0x4f, 0xe8, 0x8, 0xba, 0xc6, 0x89, 0x35, 0xc6, 0x5c, 0x17, 0x6b, 0x9c, 0x83, 0x72, 0xc4, - 0xda, 0x12, 0x3d, 0x2b, 0xf5, 0x14, 0xf4, 0x2b, 0x59, 0x9, 0x25, 0xb5, 0x48, 0x37, 0xb5, - 0x29, 0xc, 0xd3, 0x86, 0x4, 0x9c, 0xe0, 0x2d, 0x28, 0x2d, 0xd6, 0x6c, 0x47, 0x74, 0xc9, - 0x41, 0x2, 0xca, 0x8, 0x32, 0x22, 0x61, 0xde, 0x32, 0x8f, 0x5f, 0x54, 0x88, 0x85, 0x35, - 0x61, 0xc9, 0xc9, 0xaf, 0x67, 0x12, 0x52, 0xb8, 0x35, 0xf5, 0x1b, 0x53, 0xa7, 0xf1, - 0x55, 0xf3, 0xc4, 0x74, 0x5d, 0x22, 0x36, 0x98, 0x9b, 0x99, 0x5a, 0x15, 0x44, 0x17, - 0x26, 0xb1, 0xa2, 0x14, 0xda, 0xc3, 0x2a, 0x82, 0xe7, 0x33, 0xe6, 0x65, 0x29, 0xd8, - 0xc2, 0xd4, 0xac, 0xe4, 0x57, 0x81, 0x96, 0x9e, 0xb6, 0x5e, 0x5a, 0x11, 0x27, 0x29, - 0x25, 0xd9, 0xa7, 0x95, 0xfb, 0x59, 0x63, 0x5b, 0x68, 0x1e, 0xd0, 0x20, 0xde, 0x52, 0x2, - 0x5c, 0x6e, 0x62, 0x39, 0x6, 0x8e, 0xb8, 0xa9, 0x60, 0x51, 0xf0, 0x3, 0xc9, 0x9, 0x42, - 0xc8, 0x5c, 0xb3, 0x4b, 0x51, 0x72, 0xed, 0x65, 0xde, 0x22, 0x12, 0x99, 0xc7, 0x33, - 0xef, 0x2b, 0x37, 0xea, 0xf5, 0x9c, 0x28, 0x41, 0x2a, 0x35, 0x76, 0xda, 0x7b, 0x92, - 0xcc, 0xdb, 0xac, 0xe8, 0xa7, 0x59, 0xda, 0xdf, 0x54, 0x7d, 0x43, 0x4d, 0x8a, 0xca, - 0x28, 0xcc, 0xd2, 0xb1, 0x63, 0x41, 0xb4, 0x97, 0xd6, 0xfc, 0xb8, 0x75, 0x40, 0xfd, - 0xf3, 0xec, 0xd3, 0x5a, 0x67, 0x8d, 0x53, 0x7a, 0x5b, 0x69, 0x72, 0xe3, 0x9a, 0xe2, - 0xa0, 0x7c, 0xbf, 0xea, 0x31, 0x60, 0x5b, 0x31, 0x10, 0xa, 0x2a, 0x46, 0xe0, 0x4a, 0xbf, - 0x4d, 0xad, 0xa0, 0x43, 0xed, 0xe8, 0x26, 0xe8, 0x9e, 0x7a, 0x7a, 0xb4, 0x22, 0x37, - 0x65, 0x49, 0xd1, 0x18, 0xec, 0x8c, 0xc4, 0x81, 0xc3, 0xd8, 0x35, 0x92, 0x9f, 0xbd, - 0xab, 0xd, 0x3c, 0x96, 0x26, 0x20, 0xb0, 0xac, 0x31, 0x8f, 0xb2, 0x34, 0x3b, 0x48, 0xfb, - 0x8f, 0x92, 0x6f, 0x5d, 0xf8, 0x3a, 0x8, 0xb0, 0xea, 0xff, 0xe9, 0xc, 0xc5, 0x5d, 0x4e, - 0x35, 0xca, 0xc1, 0x17, 0xe6, 0xf1, 0x89, 0xfe, 0xdf, 0x1f, 0xee, 0xf, 0x37, 0xfa, 0xff, - 0x20, 0xeb, 0xf, 0xba, 0xfe, 0xff, 0x10, 0xb0, 0xcd, 0x2e, 0xb8, 0xf7, 0xe8, 0xe2, 0xb1, - 0x7, 0x5, 0xf7, 0xb3, 0xeb, 0x99, 0x40, 0x1, 0xab, 0xa5, 0xa, 0x65, 0xb6, 0xe9, 0xac, - 0x2e, 0x6d, 0x6b, 0xa0, 0xab, 0xab, 0xca, 0x50, 0x7f, 0x71, 0x8, 0x19, 0xc5, 0xa6, 0xca, - 0x8c, 0x51, 0xa4, 0x10, 0x46, 0xc0, 0x7e, 0x86, 0x82, 0x80, 0x42, 0x8d, 0x8e, 0x8b, - 0x7b, 0xa8, 0x1, 0xab, 0x73, 0x94, 0x72, 0x10, 0xd0, 0x62, 0x1a, 0x2b, 0xc9, 0xe3, 0xa, - 0xf5, 0x46, 0xde, 0xa0, 0x6a, 0x84, 0x5a, 0xf1, 0xdd, 0x93, 0x94, 0xbd, 0xd4, 0xa, 0xfd, - 0x51, 0x87, 0x9b, 0x24, 0x12, 0x43, 0x4f, 0x65, 0xa1, 0xb1, 0x25, 0xe9, 0xc9, 0xe5, - 0x1f, 0x97, 0x1e, 0xb2, 0x81, 0xc4, 0x31, 0xea, 0xd, 0x8, 0xbc, 0x3d, 0xbe, 0x64, 0x85, - 0xb4, 0x2e, 0x49, 0xa7, 0xd2, 0xf7, 0xc2, 0x67, 0x14, 0x3f, 0x49, 0xc7, 0xef, 0x6d, - 0x2f, 0x7c, 0xb6, 0x7, 0xb3, 0x69, 0x8f, 0x3e, 0xda, 0x47, 0x37, 0xd7, 0xbd, 0x15, 0xa1, - 0x31, 0xf4, 0xab, 0x2b, 0x36, 0x91, 0xa, 0xfd, 0xe7, 0x69, 0xea, 0xae, 0x2b, 0x7c, 0x8e, - 0xf9, 0x15, 0x3e, 0x7d, 0x49, 0xbf, 0xd, 0xe8, 0x24, 0x4f, 0xff, 0xa1, 0xf6, 0xc2, 0xad, - 0x34, 0xb5, 0x63, 0xa7, 0x27, 0xdf, 0x83, 0x2f, 0x86, 0x5, 0x6a, 0xd9, 0x49, 0x8a, 0x89, - 0x81, 0xf7, 0x22, 0x3a, 0x8e, 0x92, 0x74, 0xee, 0x72, 0x53, 0x88, 0xde, 0x7f, 0xa1, - 0xc6, 0xad, 0xf2, 0x7f, 0xce, 0x55, 0xd, 0x27, 0x7f, 0x85, 0x5, 0xe0, 0x13, 0xf9, 0xbf, - 0x3f, 0xdc, 0x3f, 0xb8, 0x93, 0xff, 0x83, 0xac, 0xcb, 0xff, 0x87, 0x80, 0x6d, 0x76, - 0x22, 0x26, 0xbc, 0x56, 0x98, 0xe3, 0x82, 0xff, 0xe3, 0x6c, 0xdb, 0x6, 0x45, 0xba, 0x36, - 0xf6, 0x70, 0xf6, 0xeb, 0xd1, 0xf9, 0xd9, 0x2e, 0xde, 0x97, 0x94, 0x9e, 0x45, 0x48, - 0x18, 0x42, 0x38, 0x11, 0xb9, 0xa2, 0xc9, 0x63, 0x8e, 0xe4, 0xe0, 0x63, 0x15, 0x87, - 0x94, 0x30, 0x91, 0x3b, 0xd7, 0xce, 0xe3, 0x98, 0x63, 0xec, 0x6a, 0x88, 0x4b, 0x93, - 0xc4, 0x8a, 0x30, 0x54, 0x1c, 0x9b, 0x5a, 0xfb, 0x11, 0xcb, 0x92, 0x44, 0x96, 0xa8, - 0x31, 0xa3, 0x84, 0xa1, 0x7e, 0x54, 0xc6, 0x49, 0xe4, 0xfa, 0x62, 0xc4, 0xf4, 0x54, - 0xea, 0x1b, 0x9c, 0x55, 0xb5, 0x52, 0x17, 0x6, 0x17, 0x70, 0x76, 0x3a, 0x79, 0x61, 0xfc, - 0x85, 0x15, 0xe, 0xb3, 0x12, 0x5e, 0x6d, 0xb3, 0x97, 0x18, 0x56, 0x2c, 0x52, 0x30, 0x4e, - 0x66, 0x81, 0xe, 0xf3, 0x7c, 0x8a, 0x2a, 0x46, 0x93, 0x74, 0xd1, 0xa8, 0x77, 0x6b, 0x66, - 0x5d, 0x4d, 0x24, 0x29, 0x48, 0x0, 0x19, 0x83, 0xc9, 0xa3, 0x46, 0x86, 0xb, 0xf0, 0xba, - 0x14, 0x18, 0xc6, 0xbc, 0x1b, 0xb1, 0xdf, 0x7e, 0xf, 0x2b, 0x51, 0xcb, 0x22, 0xa0, 0x61, - 0x31, 0x50, 0x77, 0xe, 0x13, 0x84, 0xc7, 0x5c, 0xe6, 0xe2, 0x28, 0xcf, 0x83, 0x4a, 0x41, - 0xb2, 0x4b, 0x8c, 0x61, 0x72, 0x42, 0x53, 0x3c, 0x2a, 0x6a, 0xdc, 0x86, 0x58, 0x83, - 0xc7, 0x78, 0x44, 0x5c, 0x9b, 0x90, 0xc1, 0x12, 0xc6, 0x29, 0x70, 0x33, 0xfe, 0xc2, - 0x1e, 0x66, 0x6b, 0x11, 0x8, 0x1d, 0xd1, 0x74, 0xce, 0xe3, 0xa, 0x0, 0x6b, 0xf2, 0xa2, - 0x68, 0x7, 0xc1, 0xd, 0x72, 0xc0, 0xe6, 0x2b, 0xdc, 0x11, 0xfb, 0xeb, 0xef, 0x70, 0x1f, - 0x63, 0x1e, 0x23, 0x91, 0xdb, 0x41, 0x75, 0x53, 0x88, 0x38, 0xb8, 0xa5, 0x1, 0xf7, 0x74, - 0x12, 0x66, 0x48, 0x27, 0xe2, 0xf4, 0x1d, 0x65, 0x9, 0xe6, 0x83, 0x34, 0x28, 0xe3, 0x91, - 0x10, 0x9e, 0xa7, 0x42, 0xb, 0x4b, 0x12, 0xc7, 0x91, 0x30, 0x10, 0x6e, 0x6d, 0xb3, 0xf4, - 0x35, 0x48, 0xc6, 0x9d, 0x92, 0x6c, 0x54, 0x99, 0xe2, 0x68, 0x43, 0x3a, 0x3a, 0x83, - 0xb5, 0x6b, 0x2b, 0xfd, 0xe2, 0xd8, 0x60, 0xfa, 0xbd, 0x9, 0xb6, 0x6b, 0xe4, 0x9e, 0xb8, - 0x1f, 0xad, 0xa9, 0xab, 0x11, 0xdb, 0x43, 0x9d, 0x20, 0x1b, 0xdf, 0x87, 0x98, 0xf3, - 0x8a, 0x8f, 0x9b, 0x95, 0x29, 0xda, 0x9e, 0xb1, 0xc2, 0x9a, 0xaa, 0xfd, 0xbd, 0xcb, - 0x8e, 0xce, 0xce, 0xc2, 0x6f, 0xa8, 0x53, 0x50, 0xa3, 0x79, 0x65, 0x8c, 0xff, 0x81, - 0x4a, 0xfe, 0xc2, 0x41, 0xd6, 0x35, 0x5b, 0xdb, 0x5a, 0x1f, 0xb9, 0x17, 0x46, 0x13, - 0xc2, 0xe6, 0xf1, 0x1b, 0x18, 0xe, 0xd1, 0xda, 0x8, 0x13, 0x6c, 0x48, 0x1c, 0xe2, 0xa6, - 0x74, 0xac, 0x50, 0x4b, 0x84, 0x3d, 0xbd, 0xa0, 0x80, 0x45, 0x7f, 0x1c, 0xb1, 0xe7, - 0x40, 0x83, 0x65, 0x10, 0xab, 0x41, 0x28, 0xa1, 0x29, 0x3d, 0x8a, 0x11, 0x9b, 0x70, - 0xe5, 0x88, 0x2a, 0xf2, 0xc6, 0xb9, 0x17, 0xad, 0x79, 0x6e, 0xbb, 0xe, 0x8f, 0x8d, 0x76, - 0xc4, 0xfd, 0x6a, 0xb9, 0x69, 0xa7, 0xd2, 0xf4, 0x1a, 0x9a, 0x69, 0xb8, 0xbf, 0xca, - 0x92, 0xbb, 0x88, 0x5e, 0xb9, 0x5d, 0x9e, 0x7, 0xfa, 0xa4, 0x8, 0xf1, 0x40, 0x52, 0xf8, - 0x86, 0xfa, 0x6e, 0x78, 0x18, 0xc5, 0x9c, 0xd8, 0x15, 0x37, 0x1c, 0x2e, 0x13, 0xa9, - 0x32, 0x39, 0x57, 0xe1, 0x3d, 0xb, 0x2d, 0xbc, 0x41, 0x8e, 0x17, 0xe8, 0x60, 0xc4, 0x7a, - 0xcb, 0x93, 0x88, 0xf2, 0x3a, 0xe8, 0x7f, 0x4a, 0xd7, 0x69, 0x83, 0x9, 0x1a, 0x34, 0xb1, - 0x9f, 0x93, 0x79, 0x54, 0xcc, 0xa4, 0xe0, 0x8a, 0x5d, 0x44, 0x16, 0x25, 0x57, 0x54, - 0xfb, 0x16, 0xeb, 0x5d, 0x20, 0x36, 0xfe, 0x5a, 0x89, 0xb9, 0xdd, 0xb2, 0xfe, 0x90, - 0x94, 0x28, 0x23, 0xe, 0x95, 0x25, 0x8f, 0x4e, 0x6f, 0x62, 0xe1, 0x17, 0x5a, 0xdf, 0x6b, - 0xae, 0x30, 0x4c, 0x2c, 0x97, 0x93, 0x10, 0xd2, 0x88, 0x72, 0x17, 0xa4, 0x5a, 0x2c, - 0xab, 0xc2, 0xf2, 0x7e, 0xdc, 0x87, 0xd, 0x53, 0x82, 0x63, 0x74, 0xf1, 0x54, 0xf3, 0xb8, - 0xb, 0x7f, 0x3d, 0x68, 0x97, 0x53, 0xcf, 0x8f, 0x61, 0x36, 0x33, 0x94, 0x36, 0xed, 0xfe, - 0x4f, 0x3d, 0xa1, 0xd9, 0xec, 0xe0, 0x51, 0x13, 0x77, 0x38, 0xee, 0x40, 0x8d, 0xd6, - 0xb5, 0x3c, 0x7e, 0xd3, 0xa0, 0x84, 0xe0, 0xc1, 0x60, 0x83, 0x0, 0x98, 0x4b, 0x6b, 0x34, - 0xd9, 0xc8, 0xc5, 0xb1, 0x7, 0x11, 0xeb, 0x55, 0x13, 0x60, 0xad, 0x28, 0xcf, 0x30, 0x60, - 0x61, 0xff, 0x3, 0xfb, 0x73, 0xa9, 0x25, 0x39, 0x34, 0xa5, 0xa4, 0xa4, 0x55, 0x10, 0xb, - 0xfd, 0x35, 0xd7, 0xb7, 0x34, 0x59, 0xbb, 0x56, 0xeb, 0xa8, 0x6d, 0x5c, 0xe1, 0xe2, - 0xda, 0x47, 0xff, 0x10, 0x10, 0x75, 0x1a, 0xa3, 0x80, 0xc2, 0x8b, 0x3f, 0x11, 0xa4, - 0xf4, 0xbe, 0x24, 0x6, 0x5a, 0xe0, 0xa2, 0xe3, 0x76, 0x11, 0xc6, 0x33, 0xd0, 0x2a, 0x4d, - 0xd0, 0x1e, 0xc5, 0xa8, 0xb6, 0xb0, 0xdf, 0xd8, 0xf2, 0x60, 0x9b, 0x9, 0xe2, 0x9a, 0xed, - 0xac, 0x8c, 0xbd, 0x93, 0x36, 0x44, 0x4b, 0xb9, 0xf2, 0x52, 0x5e, 0xd5, 0x21, 0x37, - 0xca, 0xe6, 0xb9, 0x4, 0x35, 0x2a, 0xe2, 0xd9, 0xde, 0xf3, 0x73, 0xd9, 0xa8, 0xf8, 0xe, - 0x2d, 0xe6, 0x73, 0x6f, 0x24, 0xbc, 0xf6, 0xc6, 0xc1, 0xcd, 0xd0, 0xe1, 0x83, 0xe9, - 0x53, 0x4a, 0xfd, 0x2a, 0xb6, 0x11, 0x47, 0x2d, 0x4, 0x7, 0xfc, 0x66, 0xed, 0x0, 0x49, - 0x4a, 0xb5, 0xdd, 0x4e, 0x85, 0x3f, 0xbe, 0x78, 0xf3, 0x86, 0xfe, 0x51, 0x79, 0x1f, - 0x42, 0xf3, 0x42, 0x40, 0x9, 0x84, 0x29, 0xfa, 0xd, 0xa5, 0x28, 0xf1, 0x8e, 0x68, 0xe7, - 0x81, 0xff, 0xfd, 0x98, 0x89, 0xc6, 0x58, 0x77, 0x29, 0x68, 0x4b, 0x36, 0x36, 0x96, - 0x2f, 0x6f, 0x14, 0x15, 0xc1, 0x58, 0xcf, 0x10, 0xe3, 0x9, 0x9f, 0x4c, 0xe0, 0x35, 0xbf, - 0x8, 0xaf, 0xbf, 0x75, 0x77, 0xef, 0xa0, 0x83, 0xe, 0x3a, 0xe8, 0xa0, 0x83, 0xe, 0x3a, - 0xe8, 0xa0, 0x83, 0xe, 0x3a, 0xe8, 0xa0, 0x83, 0xe, 0x3a, 0xd8, 0xda, 0xfa, 0x17, 0xe2, - 0x8a, 0xf9, 0x39, 0x0, 0x28, 0x0, 0x0, - }, - }, - }, - }, - }, - }, - { - name: "nope.tgz", - args: args{ - namespaces: []string{"main"}, - policyPaths: []string{"../testdata/kubernetes.rego"}, - }, - inputFile: filepath.Join("testdata", "nope.tgz"), - want: nil, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - f, err := os.Open(tt.inputFile) - require.NoError(t, err) - defer func() { - _ = f.Close() - }() - - info, err := os.Stat(tt.inputFile) - require.NoError(t, err) - - a := helmConfigAnalyzer{} - ctx := context.Background() - got, err := a.Analyze(ctx, analyzer.AnalysisInput{ - FilePath: tt.inputFile, - Info: info, - Content: f, - }) - - if tt.wantErr != "" { - require.NotNil(t, err) - assert.Contains(t, err.Error(), tt.wantErr) - return - } - assert.NoError(t, err) - assert.Equal(t, tt.want, got) - }) - } -} - func Test_helmConfigAnalyzer_Required(t *testing.T) { tests := []struct { name string @@ -364,66 +16,60 @@ func Test_helmConfigAnalyzer_Required(t *testing.T) { }{ { name: "yaml", - filePath: "testdata/testchart/Chart.yaml", + filePath: "Chart.yaml", want: true, }, { name: "yaml - shorthand", - filePath: "testdata/testchart/templates/deployment.yml", + filePath: "templates/deployment.yml", want: true, }, { name: "tpl", - filePath: "testdata/testchart/templates/_helpers.tpl", + filePath: "templates/_helpers.tpl", want: true, }, { name: "json", - filePath: "testdata/testchart/values.yaml", + filePath: "values.json", want: true, }, { name: "NOTES.txt", - filePath: "testdata/testchart/templates/NOTES.txt", + filePath: "templates/NOTES.txt", want: false, }, { name: ".helmignore", - filePath: "testdata/testchart/.helmignore", + filePath: ".helmignore", want: true, }, { name: "testchart.tgz", - filePath: filepath.Join("testdata", "testchart.tgz"), + filePath: "testchart.tgz", want: true, }, { name: "testchart.tar.gz", - filePath: filepath.Join("testdata", "testchart.tar.gz"), + filePath: "testchart.tar.gz", want: true, }, { name: "nope.tgz", - filePath: filepath.Join("testdata", "nope.tgz"), - want: true, // its a tarball after all + filePath: "nope.tgz", + want: true, // it's a tarball after all }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { s := helmConfigAnalyzer{} - info, _ := os.Stat(tt.filePath) + // Create a dummy file info + info, err := os.Stat("./helm_test.go") + require.NoError(t, err) got := s.Required(tt.filePath, info) assert.Equal(t, tt.want, got) }) } } - -func Test_helmConfigAnalyzer_Type(t *testing.T) { - s := helmConfigAnalyzer{} - - want := analyzer.TypeHelm - got := s.Type() - assert.Equal(t, want, got) -} diff --git a/pkg/fanal/analyzer/config/helm/testdata/Chart.yaml b/pkg/fanal/analyzer/config/helm/testdata/Chart.yaml deleted file mode 100644 index 0ffb7d074a..0000000000 --- a/pkg/fanal/analyzer/config/helm/testdata/Chart.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: v2 -name: testchart -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" diff --git a/pkg/fanal/analyzer/config/helm/testdata/nope.tgz b/pkg/fanal/analyzer/config/helm/testdata/nope.tgz deleted file mode 100644 index a47332d93877da0074012f36f0100b4f8c7decb4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 114 zcmb2|=3oE==C|hzg_;Z)SPs~z>K6zpJhT2x}s z3`9Z}Yl>hAQnu>)e*5hLAPGKXDY0u$@3`$aA_y#Y7r^cUyF|i7;!bFsbYJ{w&;vNw z+rxkR2YWs9-wa;#cei)68sfJ}E4Z2{-nmM7}>H-?Ly!PG5H}Ku{dN<-Llr;wR+>ykc=2%ellS(frkwm zMS<^9#Iix49VHuqKZ9*Y7#XP)ZwMA4D;7sY($dyJ%ljnjani68ih z4_yJ(<1~~s2+2Dzr}_2Rj0HSmu|qBnN#J)<0Rxr;27QlrnwOn4f2h!;c>vndM`kP4&jge3ly zkvUJvlwL_(;bk^T1#+a)NBLxQztXY=&Sf$$8Z4!em!qR=0p9K#gi*W{c{ zX_)wKRuR!`PLd=NgKk%_DWGzAeA(TyX4$IfIen*BWxc{C&Pqj0KCmN(xN~XVFCDqf@uV` zUx7jZMGY1g+JO!z%0ZO_f{q?x)lBL+gt2tz1lVju#SMY;CaF?@ZRCK z>`LN=B6ya_iXK!|tNJrQmvs!>g$eb+4%(-nyoX@m?3`r5;+gL<2e(8DAGp|1gPeYR z`|iu|VrFVCv!^y9Lhe+fb`_>WQ zv^&zU{@nY;2{jRSHqpHc#H4qY#LgG40~^%sE!h<6(s~L@d6EKp#|uqz#r# zk_v}<9u5YRW%}XHmISyHE{6}sWnGgoSkHyEK<+1;djFyU4P6%BCwl-?Q3Kq8Oet(? zaf3w3m|c^pAEvmw&35P}N^(5ZM*wt2{Q#$`qp&IX3HDlem~f3Qi)^j<#NC~-7B;?m zoW;x~o$HlGZ=)E!^LJ;TkB=G#P@-F<-&HyUOqt>*_U$eFuNUH`4_mq$l8tp>2r^rR zq_-v4nIjjQT2l61!we``86dHhftB1YT#QEnXlNE64_8=?!;f#joF0C7R}ommy;UcC zKx|Nh&Q*S}<@mgd4c{~ivk>H~dOM&$L4m!g_J?v>xVWY)p&m`B#I1#?*gN`odU1Gs z`tGbr;{cPZ@wlEzsZsxE!NYbPY00%wUaVA7KEn2AU%+->4|IR~!0Ew%{lV+r>t1#o zFz0D(8W$THXOyo*F;dZi6@5)(&mbTB7_66tVk-CNHm!dNX!8HE_4Qb>fTjEYy@QJX zx8HC5zo&uH`G4QK0^cCxER26LT2MN*E zgbCTj)~JTCr8<6Yuos2em+DFN(vc61^)vgTtUQc1sLP?OjA~04!^S<~E)NFe;%KPL z6wA7jV0bvn<9nIPDxEB1?zxA%22JaK646JE11?$r{r#Qo`u=aPUH?x3m7N?-6E0jD z;1R#En=`e9QKdWE9)XI;)W-Kb_J2o5AsBM+P*-E|^QsX^!-ZV*XKbu+mDq_Q(FM(i z<%*5BGG>FJ7^~0%D^d*mk25Ac9Z}Lnrd*9Wx)Hru-l(Xf8?hwzT~j#Cqp0-V(eTqJ z3@QFDJ+L8*U0{!1<{PSxR<~zNfVT#!UunQtKMwMJl9GlSrFn_!;Bdxbz-M%_kd@Ua zG90;;IQ+n-Jf1%$lT(TDp?t1DSM%A#)2#m{{huAOJdyvm+uN<4|AGJ4#($m!mYn2u z^qgErO((amgEz|*Vctf}T=cWdv%iv0mQ{rQG0tD5!40Sovd>KnYHzCW{1Kl<80WVC z`KIsmUw3-Yp^v|uW;v5kAlL#kN{J`e5eq|~IRrwZnbw_WI zW*aEizaWCz2}POlVa!P|%UI zfoiutDER^YzQjagDKSUfj+BBh5o4cJm1O2jEHR8KNaQ6o_vrRC(xXh`w6C{j}J zjfDz*5&I~&&Snq3zrd~Z;@PtP71O1F3~@omg?V(pXdo0|Fdm8rrNW%>g?er!N~?X5 zEV5GVZLy*#gFJGml@guarP^mku+}_RZi}coU4aH&b(o!fEa|Q)?*=FPxg60Jo&}ox z|1T33M6hI>Bzg+}e`mK}J^$J7_xr8?{}gb0+kI)hyNPrpV3goMRFSu?_yrt+r_yHw zf9mU$;RJKtHb-+Qi?^reSQi7p=!$JQ*6|oq-7s3EkmMmtmANd-WF~A7V9Xgh$8>Ov zQv>mu+IvJ>?5K=wQYOvpWiC-&W#Ri(B(80d+|Go0tGDuy7BuE^P)MqQPeo^`@GFUn zVn0mA#QsinzC(w+giCe0`UGqKS(vB={Y&OtR(j_P)`$cQnk z6{ClFnz%Z0u7=a|jC^z}YfTJC+F-nnWlBT*Or$fK4@wbd9rJ1w+_@%x1<_Uc>@*T_ z-(Mr}wj!&_2T5%&(O z3QGDQRV8(gqIz94t3`Z*5?kNISdKm$okm8ahc$%Ba4v(X1}oJ!58|h8M!vz|C`4`= z`^o$WoJV$(G!s<}(s{XhGqqJm)O1FR1FU*fZy_xU=n)Hv>2H@YYroev!}wT)3xo?F zMtojzV%6(}UINz2NpR_~&aU=?Bb>RpHGEP2vP_?m&0Q6kk*J+P>5G&BdEKkLZ%XJ! z2>clfnGnO6k1By*?BzYHMRZk2y<07g5Hy@h z&|f223{u_9B=HcmS=aI=t;}wf?yM?qB*HI%p)#07Thm~AC*k$dPrAm9LGttQhXpM*t{3pym~Dpd#CXT!I~qkz#cvlti4#CLinK^bllJc5o$i zR^6YHPQRjiEZ)aFjRs^J^aRRyQAO9t{!+3h#EW^9{qB&%lN0$HO*CFKbH;h{Z@ltl zE`U};)^Qphic=n9g=+C9foWZRO-!CUramjkzf`s|`wdQsrPBbhn%=4m$e0EKvARWW zrENE!Mkw2}M(#-*E>=msViJfBbtQge1p6?uD6?clxh6X7hQg3BC*ZKNvI^&<W56$05?;(c;}CrW)qIfCc9k#)=aE#c7qY+ z>JB$5n9KnQT_&=N_+au7@RV6N^J5;OjD+%2V8$kao?sa%uZT$933mShJR%%W2Nxw? z1?O>cO~Vo=Mzb_j+^Xw5)U|JTLm*w9rBeEws==3oW$JLJKXl&_WCU5Aa`~nBN`%cmM!OGA)__ diff --git a/pkg/fanal/analyzer/config/helm/testdata/testchart.tgz b/pkg/fanal/analyzer/config/helm/testdata/testchart.tgz deleted file mode 100644 index 3a3f926214f928a66dd843a935eb039347041915..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1787 zcmV=G<#gQo2;48uT6 zw9SPgRg!Yz4%=_vcSy;0oTlwVn_`&GgKbfV=YF*p&gV03eQ)clK_7599MX4oIPAyo z{qWtwRyNohWP90Qm;o=#4hM%@Z20v=T0!kRw~TF>YoR}mC#-u2fgTq8(*MWwnJ### zzBCVV@WEhkpa1u=!z}o}KN#%o5BA{y{e#2d7VCd$K|TckbN};7zJ+4a=n1V<+JnZnQQZna6 zWryJ&XFE!cEw_uEcqdJ^iNFdi9y`XZ0CsMa5_vc`6G&H9ii0WhGrnY3@%)NUMZu)@ zhRvf+c;q&p$vGt4W;h38Au44SqKG`tq8u&1%9^<>1cSFHwPLlGN_q)aka|+VmivUrs!QE`-h(qyWFGCq$F_Pv{7`#)&n?To-Gc zgL1^$5~iH?=7Y(}o0`;4LUE)@(;~$w30j2r?pJ-ZGD0REUK&Vads_ zXA)AlHT4@)r}0(u#B^OEHkg~4S`|bWCb6Ov+QTY>=QHMIDcHi)tmIdLmY%j=S`jBH zCE2Hy`&naKXdciY-ckZwZelqGj<~5{QSbxF2|~zRvrAEO?PcB~5}C&{?<+Uz^_(a{ zDm8Z6dy>rCtmvm%+TT=tLrsdxD9qBaV?nf+*8I42K>hRV(^_YZQ+iuza^srfpnSjT zF<@IU5DF?r;7Y$ut)N5g=qBi%dU~`XH)ToD80?J1fy3A}lApb+4Lp`6Ah4`4kFqp7 zNc)eHZ(aC02(aq^=?ukPPBqHG7v}Ma{@)+&4>$V%AnOmh{{IlL%`Uk2=;E;l1^2V- znLq)nr3z)X>8vhoYoM#E%20oC2pPqu%8XH@5Jtd$hJt`XjgE_Zr~#{UlyVTzVj3&S z;|ldg-k@rPTJhbJl)cmn{ZWUTBoaeUWudVp>B;3Emmac##4+j(2)sSMWQDXYNvG2H z!e$@_)`%4JUtv1>h6;zdzhRxRd{b ztjqt0fNgdnCcIXdKmL@wNe)b7jSku zd13s86%Ml!UTw3N7*;G&FivKH4)c79Dn^rZ(WrN8W?fArDM$brgUO>th70hz6d1<5 z8?oPh4=Yj2BA{V0Pka+=f_#$9P0sn~=d0#wlJqG$)f4RXNdE8G$L>+f(MV;&=s+d*|xh&Z^V0rzwgW=JRE z^c`gh=7TgSU=^fgGvosw)+nc`?eIy!^3Yb*WY*kJm-3AStG zp12>asjCs&L!BTS--npDcYW~!kVi6y~~cy&cbh~Q&6LMWsLuUO8&w@)-_w( zS|7O=Miav3@f#Qp(b@gJlq_K`5aqJ&F;VaOH?9 z^2WIrt$A*{4v&p?#QkcyX{XZk8k%Xizsi!kh+er}o(KA*2=PEt_=fs!co@Z;*cCS% zE)%ITIalF53oX1sju+@)#Cpq^=D7qlD&pDR z^K;p#_zo@Rb8jTP_Qt`@(BXsWQ%e0Rwh>1y1PA=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} - {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} - {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} - {{- end }} -{{- end }} -{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} -apiVersion: networking.k8s.io/v1 -{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} -apiVersion: networking.k8s.io/v1beta1 -{{- else -}} -apiVersion: extensions/v1beta1 -{{- end }} -kind: Ingress -metadata: - name: {{ $fullName }} - labels: - {{- include "testchart.labels" . | nindent 4 }} - {{- with .Values.ingress.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -spec: - {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} - ingressClassName: {{ .Values.ingress.className }} - {{- end }} - {{- if .Values.ingress.tls }} - tls: - {{- range .Values.ingress.tls }} - - hosts: - {{- range .hosts }} - - {{ . | quote }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} - {{- end }} - rules: - {{- range .Values.ingress.hosts }} - - host: {{ .host | quote }} - http: - paths: - {{- range .paths }} - - path: {{ .path }} - {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} - pathType: {{ .pathType }} - {{- end }} - backend: - {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} - service: - name: {{ $fullName }} - port: - number: {{ $svcPort }} - {{- else }} - serviceName: {{ $fullName }} - servicePort: {{ $svcPort }} - {{- end }} - {{- end }} - {{- end }} -{{- end }} diff --git a/pkg/fanal/analyzer/config/helm/testdata/testchart/templates/service.yaml b/pkg/fanal/analyzer/config/helm/testdata/testchart/templates/service.yaml deleted file mode 100644 index 86baf14821..0000000000 --- a/pkg/fanal/analyzer/config/helm/testdata/testchart/templates/service.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ include "testchart.fullname" . }} - labels: - {{- include "testchart.labels" . | nindent 4 }} -spec: - type: {{ .Values.service.type }} - ports: - - port: {{ .Values.service.port }} - targetPort: http - protocol: TCP - name: http - selector: - {{- include "testchart.selectorLabels" . | nindent 4 }} diff --git a/pkg/fanal/analyzer/config/helm/testdata/testchart/templates/serviceaccount.yaml b/pkg/fanal/analyzer/config/helm/testdata/testchart/templates/serviceaccount.yaml deleted file mode 100644 index f728deb2a6..0000000000 --- a/pkg/fanal/analyzer/config/helm/testdata/testchart/templates/serviceaccount.yaml +++ /dev/null @@ -1,12 +0,0 @@ -{{- if .Values.serviceAccount.create -}} -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ include "testchart.serviceAccountName" . }} - labels: - {{- include "testchart.labels" . | nindent 4 }} - {{- with .Values.serviceAccount.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -{{- end }} diff --git a/pkg/fanal/analyzer/config/helm/testdata/testchart/templates/tests/test-connection.yaml b/pkg/fanal/analyzer/config/helm/testdata/testchart/templates/tests/test-connection.yaml deleted file mode 100644 index a391ef1c46..0000000000 --- a/pkg/fanal/analyzer/config/helm/testdata/testchart/templates/tests/test-connection.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: "{{ include "testchart.fullname" . }}-test-connection" - labels: - {{- include "testchart.labels" . | nindent 4 }} - annotations: - "helm.sh/hook": test -spec: - containers: - - name: wget - image: busybox - command: ['wget'] - args: ['{{ include "testchart.fullname" . }}:{{ .Values.service.port }}'] - restartPolicy: Never diff --git a/pkg/fanal/analyzer/config/helm/testdata/testchart/values.yaml b/pkg/fanal/analyzer/config/helm/testdata/testchart/values.yaml deleted file mode 100644 index 4acdf3c931..0000000000 --- a/pkg/fanal/analyzer/config/helm/testdata/testchart/values.yaml +++ /dev/null @@ -1,86 +0,0 @@ -# Default values for testchart. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. - -replicaCount: 1 - -image: - repository: nginx - pullPolicy: IfNotPresent - # Overrides the image tag whose default is the chart appVersion. - tag: "" - -imagePullSecrets: [] -nameOverride: "" -fullnameOverride: "" - -serviceAccount: - # Specifies whether a service account should be created - create: true - # Annotations to add to the service account - annotations: {} - # The name of the service account to use. - # If not set and create is true, a name is generated using the fullname template - name: "" - -podAnnotations: {} - -podSecurityContext: - {} - # fsGroup: 2000 - -securityContext: - {} - # capabilities: - # drop: - # - ALL - # readOnlyRootFilesystem: true - # runAsNonRoot: true - # runAsUser: 1000 - -service: - type: ClusterIP - port: 80 - -ingress: - enabled: false - className: "" - annotations: - {} - # kubernetes.io/ingress.class: nginx - # kubernetes.io/tls-acme: "true" - hosts: - - host: chart-example.local - paths: - - path: / - pathType: ImplementationSpecific - tls: [] - # - secretName: chart-example-tls - # hosts: - # - chart-example.local - -resources: - {} - # We usually recommend not to specify default resources and to leave this as a conscious - # choice for the user. This also increases chances charts run on environments with little - # resources, such as Minikube. If you do want to specify resources, uncomment the following - # lines, adjust them as necessary, and remove the curly braces after 'resources:'. - # limits: - # cpu: 100m - # memory: 128Mi - # requests: - # cpu: 100m - # memory: 128Mi - -autoscaling: - enabled: false - minReplicas: 1 - maxReplicas: 100 - targetCPUUtilizationPercentage: 80 - # targetMemoryUtilizationPercentage: 80 - -nodeSelector: {} - -tolerations: [] - -affinity: {} diff --git a/pkg/fanal/analyzer/config/helm/testdata/values.yaml b/pkg/fanal/analyzer/config/helm/testdata/values.yaml deleted file mode 100644 index b63948b2b0..0000000000 --- a/pkg/fanal/analyzer/config/helm/testdata/values.yaml +++ /dev/null @@ -1,86 +0,0 @@ -# Default values for testchart. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. - -replicaCount: 1 - -image: - repository: nginx - pullPolicy: IfNotPresent - # Overrides the image tag whose default is the chart appVersion. - tag: "" - -imagePullSecrets: [] -nameOverride: "" -fullnameOverride: "" - -serviceAccount: - # Specifies whether a service account should be created - create: true - # Annotations to add to the service account - annotations: {} - # The name of the service account to use. - # If not set and create is true, a name is generated using the fullname template - name: "" - -podAnnotations: {} - -podSecurityContext: - {} -# fsGroup: 2000 - -securityContext: - {} - # capabilities: - # drop: - # - ALL - # readOnlyRootFilesystem: true - # runAsNonRoot: true -# runAsUser: 1000 - -service: - type: ClusterIP - port: 80 - -ingress: - enabled: false - className: "" - annotations: - {} - # kubernetes.io/ingress.class: nginx - # kubernetes.io/tls-acme: "true" - hosts: - - host: chart-example.local - paths: - - path: / - pathType: ImplementationSpecific - tls: [] - # - secretName: chart-example-tls - # hosts: - # - chart-example.local - -resources: - {} - # We usually recommend not to specify default resources and to leave this as a conscious - # choice for the user. This also increases chances charts run on environments with little - # resources, such as Minikube. If you do want to specify resources, uncomment the following - # lines, adjust them as necessary, and remove the curly braces after 'resources:'. - # limits: - # cpu: 100m - # memory: 128Mi - # requests: - # cpu: 100m -# memory: 128Mi - -autoscaling: - enabled: false - minReplicas: 1 - maxReplicas: 100 - targetCPUUtilizationPercentage: 80 - # targetMemoryUtilizationPercentage: 80 - -nodeSelector: {} - -tolerations: [] - -affinity: {} diff --git a/pkg/fanal/analyzer/config/json/json.go b/pkg/fanal/analyzer/config/json/json.go deleted file mode 100644 index b591c58c39..0000000000 --- a/pkg/fanal/analyzer/config/json/json.go +++ /dev/null @@ -1,65 +0,0 @@ -package json - -import ( - "context" - "io" - "os" - "path/filepath" - - "golang.org/x/xerrors" - - "github.com/aquasecurity/trivy/pkg/fanal/analyzer" - "github.com/aquasecurity/trivy/pkg/fanal/types" -) - -func init() { - analyzer.RegisterAnalyzer(&jsonConfigAnalyzer{}) -} - -const version = 1 - -var ( - requiredExt = ".json" - excludedFiles = []string{types.NpmPkgLock, types.NuGetPkgsLock, types.NuGetPkgsConfig} -) - -type jsonConfigAnalyzer struct{} - -func (a jsonConfigAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) { - b, err := io.ReadAll(input.Content) - if err != nil { - return nil, xerrors.Errorf("failed to read %s: %w", input.FilePath, err) - } - - return &analyzer.AnalysisResult{ - Files: map[types.HandlerType][]types.File{ - // It will be passed to misconfig post handler - types.MisconfPostHandler: { - { - Type: types.JSON, - Path: input.FilePath, - Content: b, - }, - }, - }, - }, nil -} - -func (a jsonConfigAnalyzer) Required(filePath string, _ os.FileInfo) bool { - filename := filepath.Base(filePath) - for _, excludedFile := range excludedFiles { - if filename == excludedFile { - return false - } - } - - return filepath.Ext(filePath) == requiredExt -} - -func (jsonConfigAnalyzer) Type() analyzer.Type { - return analyzer.TypeJSON -} - -func (jsonConfigAnalyzer) Version() int { - return version -} diff --git a/pkg/fanal/analyzer/config/json/json_test.go b/pkg/fanal/analyzer/config/json/json_test.go deleted file mode 100644 index faf0512527..0000000000 --- a/pkg/fanal/analyzer/config/json/json_test.go +++ /dev/null @@ -1,98 +0,0 @@ -package json - -import ( - "context" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/fanal/analyzer" - "github.com/aquasecurity/trivy/pkg/fanal/types" -) - -func Test_jsonConfigAnalyzer_Analyze(t *testing.T) { - tests := []struct { - name string - inputFile string - want *analyzer.AnalysisResult - wantErr string - }{ - { - name: "happy path", - inputFile: "test.json", - want: &analyzer.AnalysisResult{ - Files: map[types.HandlerType][]types.File{ - types.MisconfPostHandler: { - { - Type: "json", - Path: "test.json", - Content: []byte(`{}`), - }, - }, - }, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := strings.NewReader("{}") - - s := jsonConfigAnalyzer{} - got, err := s.Analyze(context.Background(), analyzer.AnalysisInput{ - FilePath: tt.inputFile, - Content: r, - }) - - if tt.wantErr != "" { - require.NotNil(t, err) - assert.Contains(t, err.Error(), tt.wantErr) - return - } - assert.NoError(t, err) - assert.Equal(t, tt.want, got) - }) - } -} - -func Test_jsonConfigAnalyzer_Required(t *testing.T) { - tests := []struct { - name string - filePath string - want bool - }{ - { - name: "json", - filePath: "deployment.json", - want: true, - }, - { - name: "yaml", - filePath: "deployment.yaml", - want: false, - }, - { - name: "npm json", - filePath: "package-lock.json", - want: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - s := jsonConfigAnalyzer{} - - got := s.Required(tt.filePath, nil) - assert.Equal(t, tt.want, got) - }) - } -} - -func Test_jsonConfigAnalyzer_Type(t *testing.T) { - s := jsonConfigAnalyzer{} - - want := analyzer.TypeJSON - got := s.Type() - assert.Equal(t, want, got) -} diff --git a/pkg/fanal/analyzer/config/json/testdata/array.json b/pkg/fanal/analyzer/config/json/testdata/array.json deleted file mode 100644 index 0282a19c66..0000000000 --- a/pkg/fanal/analyzer/config/json/testdata/array.json +++ /dev/null @@ -1,22 +0,0 @@ -[ - { - "apiVersion": "apps/v1", - "kind": "Deployment", - "metadata": { - "name": "hello-kubernetes" - }, - "spec": { - "replicas": 4 - } - }, - { - "apiVersion": "apps/v2", - "kind": "Deployment", - "metadata": { - "name": "hello-kubernetes" - }, - "spec": { - "replicas": 5 - } - } -] diff --git a/pkg/fanal/analyzer/config/json/testdata/deployment.json b/pkg/fanal/analyzer/config/json/testdata/deployment.json deleted file mode 100644 index 54ec31bd21..0000000000 --- a/pkg/fanal/analyzer/config/json/testdata/deployment.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "apiVersion": "apps/v1", - "kind": "Deployment", - "metadata": { - "name": "hello-kubernetes" - }, - "spec": { - "replicas": 3 - } -} diff --git a/pkg/fanal/analyzer/config/json/testdata/deployment_deny.json b/pkg/fanal/analyzer/config/json/testdata/deployment_deny.json deleted file mode 100644 index ac8667d00a..0000000000 --- a/pkg/fanal/analyzer/config/json/testdata/deployment_deny.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "apiVersion": "apps/v1", - "kind": "Deployment", - "metadata": { - "name": "hello-kubernetes" - }, - "spec": { - "replicas": 4 - } -} diff --git a/pkg/fanal/analyzer/config/k8s/k8s.go b/pkg/fanal/analyzer/config/k8s/k8s.go new file mode 100644 index 0000000000..6f3a58af16 --- /dev/null +++ b/pkg/fanal/analyzer/config/k8s/k8s.go @@ -0,0 +1,30 @@ +package k8s + +import ( + "github.com/aquasecurity/trivy/pkg/fanal/analyzer" + "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config" + "github.com/aquasecurity/trivy/pkg/misconf" +) + +const ( + analyzerType = analyzer.TypeKubernetes + version = 1 +) + +func init() { + analyzer.RegisterPostAnalyzer(analyzerType, newKubernetesConfigAnalyzer) +} + +// kubernetesConfigAnalyzer is an analyzer for detecting misconfigurations in Kubernetes config files. +// It embeds config.Analyzer so it can implement analyzer.PostAnalyzer. +type kubernetesConfigAnalyzer struct { + *config.Analyzer +} + +func newKubernetesConfigAnalyzer(opts analyzer.AnalyzerOptions) (analyzer.PostAnalyzer, error) { + a, err := config.NewAnalyzer(analyzerType, version, misconf.NewKubernetesScanner, opts) + if err != nil { + return nil, err + } + return &kubernetesConfigAnalyzer{Analyzer: a}, nil +} diff --git a/pkg/fanal/analyzer/config/terraform/terraform.go b/pkg/fanal/analyzer/config/terraform/terraform.go index a0a89642aa..025b4b3dd8 100644 --- a/pkg/fanal/analyzer/config/terraform/terraform.go +++ b/pkg/fanal/analyzer/config/terraform/terraform.go @@ -1,56 +1,45 @@ package terraform import ( - "context" - "io" "os" "path/filepath" "golang.org/x/exp/slices" - "golang.org/x/xerrors" "github.com/aquasecurity/trivy/pkg/fanal/analyzer" - "github.com/aquasecurity/trivy/pkg/fanal/types" + "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config" + "github.com/aquasecurity/trivy/pkg/misconf" ) +const ( + analyzerType = analyzer.TypeTerraform + version = 1 +) + +var requiredExts = []string{ + ".tf", + ".tf.json", +} + func init() { - analyzer.RegisterAnalyzer(&terraformConfigAnalyzer{}) + analyzer.RegisterPostAnalyzer(analyzerType, newTerraformConfigAnalyzer) } -const version = 1 +// terraformConfigAnalyzer is an analyzer for detecting misconfigurations in Terraform files. +// It embeds config.Analyzer so it can implement analyzer.PostAnalyzer. +type terraformConfigAnalyzer struct { + *config.Analyzer +} -var requiredExts = []string{".tf", ".tf.json"} - -type terraformConfigAnalyzer struct{} - -// Analyze returns a name of Terraform file -func (a terraformConfigAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) { - b, err := io.ReadAll(input.Content) +func newTerraformConfigAnalyzer(opts analyzer.AnalyzerOptions) (analyzer.PostAnalyzer, error) { + a, err := config.NewAnalyzer(analyzerType, version, misconf.NewTerraformScanner, opts) if err != nil { - return nil, xerrors.Errorf("read error (%s): %w", input.FilePath, err) + return nil, err } - return &analyzer.AnalysisResult{ - Files: map[types.HandlerType][]types.File{ - // It will be passed to misconf post handler - types.MisconfPostHandler: { - { - Type: types.Terraform, - Path: input.FilePath, - Content: b, - }, - }, - }, - }, nil + return &terraformConfigAnalyzer{Analyzer: a}, nil } -func (a terraformConfigAnalyzer) Required(filePath string, _ os.FileInfo) bool { +// Required overrides config.Analyzer.Required() and checks if the given file is a Terraform file. +func (*terraformConfigAnalyzer) Required(filePath string, _ os.FileInfo) bool { return slices.Contains(requiredExts, filepath.Ext(filePath)) } - -func (terraformConfigAnalyzer) Type() analyzer.Type { - return analyzer.TypeTerraform -} - -func (terraformConfigAnalyzer) Version() int { - return version -} diff --git a/pkg/fanal/analyzer/config/terraform/terraform_test.go b/pkg/fanal/analyzer/config/terraform/terraform_test.go index 4e9efa7404..7a03c7aaa9 100644 --- a/pkg/fanal/analyzer/config/terraform/terraform_test.go +++ b/pkg/fanal/analyzer/config/terraform/terraform_test.go @@ -1,55 +1,11 @@ package terraform import ( - "bytes" - "context" "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/fanal/analyzer" - "github.com/aquasecurity/trivy/pkg/fanal/types" ) -func TestConfigAnalyzer_Analyze(t *testing.T) { - tests := []struct { - name string - input analyzer.AnalysisInput - want *analyzer.AnalysisResult - }{ - { - name: "happy path", - input: analyzer.AnalysisInput{ - Dir: "path/to/", - FilePath: "main.tf", - Content: bytes.NewReader(nil), - }, - want: &analyzer.AnalysisResult{ - Files: map[types.HandlerType][]types.File{ - types.MisconfPostHandler: { - { - Type: types.Terraform, - Path: "main.tf", - Content: []byte{}, - }, - }, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - a := terraformConfigAnalyzer{} - ctx := context.Background() - got, err := a.Analyze(ctx, tt.input) - - require.NoError(t, err) - assert.Equal(t, tt.want, got) - }) - } -} - func TestConfigAnalyzer_Required(t *testing.T) { tests := []struct { name string diff --git a/pkg/fanal/analyzer/config/testdata/docker_deny.rego b/pkg/fanal/analyzer/config/testdata/docker_deny.rego deleted file mode 100644 index c1196a06ad..0000000000 --- a/pkg/fanal/analyzer/config/testdata/docker_deny.rego +++ /dev/null @@ -1,21 +0,0 @@ -package users.dockerfile.xyz_100 - -__rego_metadata__ := { - "id": "XYZ-100", - "title": "Bad Dockerfile", - "version": "v1.0.0", - "severity": "HIGH", - "type": "Docker Security Check", -} - -denylist = [ - "foo" -] - -deny[res] { - input[i].Cmd == "from" - val := input[i].Value - contains(val[i], denylist[_]) - - res = {"type": "Docker Security Check", "msg": sprintf("deny: image found %s", [val]), "severity": "HIGH", "id": "RULE-100"} -} diff --git a/pkg/fanal/analyzer/config/testdata/docker_multi.rego b/pkg/fanal/analyzer/config/testdata/docker_multi.rego deleted file mode 100644 index 52cd013236..0000000000 --- a/pkg/fanal/analyzer/config/testdata/docker_multi.rego +++ /dev/null @@ -1,35 +0,0 @@ -package main.dockerfile - -denylist = [ - "foo" -] - -deny[res] { - input[i].Cmd == "from" - val := input[i].Value - contains(val[i], denylist[_]) - - res = { - "type": "Docker Security Check", - "msg": sprintf("deny: image found %s", [val]), - "severity": "HIGH", - "id": "RULE-100" - } -} - -warnlist = [ - "echo" -] - -warn[res] { - input[i].Cmd == "run" - val := input[i].Value - contains(val[_], warnlist[_]) - - res = { - "type": "Docker Security Check", - "msg": sprintf("warn: command %s contains banned: %s", [val, warnlist]), - "severity": "LOW", - "id": "RULE-10" - } -} diff --git a/pkg/fanal/analyzer/config/testdata/docker_non.rego b/pkg/fanal/analyzer/config/testdata/docker_non.rego deleted file mode 100644 index 86a93b0b6f..0000000000 --- a/pkg/fanal/analyzer/config/testdata/docker_non.rego +++ /dev/null @@ -1,20 +0,0 @@ -package main.dockerfile - -__rego_metadata__ := { - "id": "XYZ-100", - "title": "Bad Dockerfile", - "version": "v1.0.0", - "severity": "HIGH", - "type": "Docker Security Check", -} - -denylist = [ -] - -deny[msg] { - input[i].Cmd == "from" - val := input[i].Value - contains(val[i], denylist[_]) - - msg = sprintf("deny: image found %s", [val]) -} diff --git a/pkg/fanal/analyzer/config/testdata/docker_violation.rego b/pkg/fanal/analyzer/config/testdata/docker_violation.rego deleted file mode 100644 index 2e0092eb9e..0000000000 --- a/pkg/fanal/analyzer/config/testdata/docker_violation.rego +++ /dev/null @@ -1,13 +0,0 @@ -package main.dockerfile.id_100 - -violationlist = [ - "foo" -] - -violation[{"msg": msg, "details": {}}] { - input[i].Cmd == "from" - val := input[i].Value - contains(val[i], violationlist[_]) - - msg = sprintf("violation: image found %s", [val]) -} diff --git a/pkg/fanal/analyzer/config/testdata/docker_warn.rego b/pkg/fanal/analyzer/config/testdata/docker_warn.rego deleted file mode 100644 index b67b2df9b1..0000000000 --- a/pkg/fanal/analyzer/config/testdata/docker_warn.rego +++ /dev/null @@ -1,19 +0,0 @@ -package main.dockerfile.xyz_100 - -__rego_metadata__ := { - "id": "XYZ-100", - "title": "Bad Dockerfile", - "version": "v1.0.0", -} - -warnlist = [ - "foo" -] - -warn[msg] { - input[i].Cmd == "from" - val := input[i].Value - contains(val[i], warnlist[_]) - - msg = sprintf("warn: image found %s", [val]) -} diff --git a/pkg/fanal/analyzer/config/testdata/kubernetes.rego b/pkg/fanal/analyzer/config/testdata/kubernetes.rego deleted file mode 100644 index 1844d09582..0000000000 --- a/pkg/fanal/analyzer/config/testdata/kubernetes.rego +++ /dev/null @@ -1,15 +0,0 @@ -package main.kubernetes.xyz_100 - -__rego_metadata__ := { - "id": "XYZ-100", - "title": "Bad Kubernetes Replicas", - "version": "v1.0.0", - "severity": "HIGH", - "type": "Kubernetes Security Check", -} - -deny[msg] { - rpl = input.spec.replicas - rpl > 3 - msg = sprintf("too many replicas: %d", [rpl]) -} \ No newline at end of file diff --git a/pkg/fanal/analyzer/config/testdata/rego/policy.rego b/pkg/fanal/analyzer/config/testdata/rego/policy.rego new file mode 100644 index 0000000000..ab4daa223c --- /dev/null +++ b/pkg/fanal/analyzer/config/testdata/rego/policy.rego @@ -0,0 +1,32 @@ +package user.something + +__rego_metadata__ := { + "id": "TEST001", + "avd_id": "AVD-TEST-0001", + "title": "Test policy", + "short_code": "no-buckets", + "severity": "LOW", + "description": "This is a test policy.", + "recommended_actions": "Have a cup of tea.", + "url": "https://trivy.dev/", +} + +# taken from defsec rego lib to mimic behaviour +result(msg, cause) = result { + metadata := object.get(cause, "__defsec_metadata", cause) + result := { + "msg": msg, + "startline": object.get(metadata, "startline", object.get(metadata, "StartLine", 0)), + "endline": object.get(metadata, "endline", object.get(metadata, "EndLine", 0)), + "filepath": object.get(metadata, "filepath", object.get(metadata, "Path", "")), + "explicit": object.get(metadata, "explicit", false), + "managed": object.get(metadata, "managed", true), + "fskey": object.get(metadata, "fskey", ""), + "resource": object.get(metadata, "resource", ""), + } +} + +deny[res] { + cmd := input.stages[_][_] + res := result("No commands allowed!", cmd) +} \ No newline at end of file diff --git a/pkg/fanal/analyzer/config/testdata/src/Dockerfile b/pkg/fanal/analyzer/config/testdata/src/Dockerfile new file mode 100644 index 0000000000..6d4b4c49ea --- /dev/null +++ b/pkg/fanal/analyzer/config/testdata/src/Dockerfile @@ -0,0 +1 @@ +FROM ubuntu \ No newline at end of file diff --git a/pkg/fanal/analyzer/config/yaml/testdata/anchor.yaml b/pkg/fanal/analyzer/config/yaml/testdata/anchor.yaml deleted file mode 100644 index d001df66a3..0000000000 --- a/pkg/fanal/analyzer/config/yaml/testdata/anchor.yaml +++ /dev/null @@ -1,15 +0,0 @@ -default: &default - line: single line - -john: &J - john_name: john -fred: &F - fred_name: fred - -main: - <<: *default - name: - <<: [*J, *F] - comment: | - multi - line diff --git a/pkg/fanal/analyzer/config/yaml/testdata/broken.yaml b/pkg/fanal/analyzer/config/yaml/testdata/broken.yaml deleted file mode 100644 index aeb06d4c03..0000000000 --- a/pkg/fanal/analyzer/config/yaml/testdata/broken.yaml +++ /dev/null @@ -1 +0,0 @@ -apiVersion": foo: bar diff --git a/pkg/fanal/analyzer/config/yaml/testdata/circular_references.yaml b/pkg/fanal/analyzer/config/yaml/testdata/circular_references.yaml deleted file mode 100644 index 8b6290296c..0000000000 --- a/pkg/fanal/analyzer/config/yaml/testdata/circular_references.yaml +++ /dev/null @@ -1,3 +0,0 @@ -circular: &circular - name: - <<: *circular diff --git a/pkg/fanal/analyzer/config/yaml/testdata/deny.rego b/pkg/fanal/analyzer/config/yaml/testdata/deny.rego deleted file mode 100644 index e311d33a49..0000000000 --- a/pkg/fanal/analyzer/config/yaml/testdata/deny.rego +++ /dev/null @@ -1,13 +0,0 @@ -package main.yaml.xyz_123 - -__rego_metadata__ := { - "id": "XYZ-123", - "title": "Bad YAML", - "version": "v1.0.0", - "severity": "CRITICAL", - "type": "YAML Security Check", -} - -deny[msg]{ - msg := "bad" -} \ No newline at end of file diff --git a/pkg/fanal/analyzer/config/yaml/testdata/deployment.yaml b/pkg/fanal/analyzer/config/yaml/testdata/deployment.yaml deleted file mode 100644 index accd9e4691..0000000000 --- a/pkg/fanal/analyzer/config/yaml/testdata/deployment.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello-kubernetes -spec: - replicas: 3 diff --git a/pkg/fanal/analyzer/config/yaml/testdata/deployment_deny.yaml b/pkg/fanal/analyzer/config/yaml/testdata/deployment_deny.yaml deleted file mode 100644 index 7f9f5f1d52..0000000000 --- a/pkg/fanal/analyzer/config/yaml/testdata/deployment_deny.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello-kubernetes -spec: - replicas: 4 diff --git a/pkg/fanal/analyzer/config/yaml/testdata/incompatible_json.yaml b/pkg/fanal/analyzer/config/yaml/testdata/incompatible_json.yaml deleted file mode 100644 index 650119bed4..0000000000 --- a/pkg/fanal/analyzer/config/yaml/testdata/incompatible_json.yaml +++ /dev/null @@ -1,4 +0,0 @@ -replacements: - amd64: 64bit - 386: 32bit - arm: ARM diff --git a/pkg/fanal/analyzer/config/yaml/testdata/multiple.yaml b/pkg/fanal/analyzer/config/yaml/testdata/multiple.yaml deleted file mode 100644 index 23aa25b329..0000000000 --- a/pkg/fanal/analyzer/config/yaml/testdata/multiple.yaml +++ /dev/null @@ -1,18 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hello-kubernetes -spec: - replicas: 4 - ---- - -apiVersion: v1 -kind: Service -metadata: - name: hello-kubernetes -spec: - ports: - - protocol: TCP - port: 80 - targetPort: 8080 diff --git a/pkg/fanal/analyzer/config/yaml/yaml.go b/pkg/fanal/analyzer/config/yaml/yaml.go deleted file mode 100644 index e25c1e30b0..0000000000 --- a/pkg/fanal/analyzer/config/yaml/yaml.go +++ /dev/null @@ -1,61 +0,0 @@ -package yaml - -import ( - "context" - "io" - "os" - "path/filepath" - - "golang.org/x/xerrors" - - "github.com/aquasecurity/trivy/pkg/fanal/analyzer" - "github.com/aquasecurity/trivy/pkg/fanal/types" -) - -func init() { - analyzer.RegisterAnalyzer(&yamlConfigAnalyzer{}) -} - -const version = 1 - -var requiredExts = []string{".yaml", ".yml"} - -type yamlConfigAnalyzer struct{} - -func (a yamlConfigAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) { - b, err := io.ReadAll(input.Content) - if err != nil { - return nil, xerrors.Errorf("failed to read %s: %w", input.FilePath, err) - } - - return &analyzer.AnalysisResult{ - Files: map[types.HandlerType][]types.File{ - // it will be passed to misconfig post handler - types.MisconfPostHandler: { - { - Type: types.YAML, - Path: input.FilePath, - Content: b, - }, - }, - }, - }, nil -} - -func (a yamlConfigAnalyzer) Required(filePath string, _ os.FileInfo) bool { - ext := filepath.Ext(filePath) - for _, required := range requiredExts { - if ext == required { - return true - } - } - return false -} - -func (yamlConfigAnalyzer) Type() analyzer.Type { - return analyzer.TypeYaml -} - -func (yamlConfigAnalyzer) Version() int { - return version -} diff --git a/pkg/fanal/analyzer/config/yaml/yaml_test.go b/pkg/fanal/analyzer/config/yaml/yaml_test.go deleted file mode 100644 index fafe33e605..0000000000 --- a/pkg/fanal/analyzer/config/yaml/yaml_test.go +++ /dev/null @@ -1,97 +0,0 @@ -package yaml - -import ( - "context" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/fanal/analyzer" - "github.com/aquasecurity/trivy/pkg/fanal/types" -) - -func Test_yamlConfigAnalyzer_Analyze(t *testing.T) { - tests := []struct { - name string - inputFile string - want *analyzer.AnalysisResult - wantErr string - }{ - { - name: "happy path", - inputFile: "test.yaml", - want: &analyzer.AnalysisResult{ - Files: map[types.HandlerType][]types.File{ - types.MisconfPostHandler: { - { - Type: "yaml", - Path: "test.yaml", - Content: []byte(`- abc`), - }, - }, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := strings.NewReader("- abc") - a := yamlConfigAnalyzer{} - ctx := context.Background() - got, err := a.Analyze(ctx, analyzer.AnalysisInput{ - FilePath: tt.inputFile, - Content: r, - }) - - if tt.wantErr != "" { - require.NotNil(t, err) - assert.Contains(t, err.Error(), tt.wantErr) - return - } - assert.NoError(t, err) - assert.Equal(t, tt.want, got) - }) - } -} - -func Test_yamlConfigAnalyzer_Required(t *testing.T) { - tests := []struct { - name string - filePath string - want bool - }{ - { - name: "yaml", - filePath: "deployment.yaml", - want: true, - }, - { - name: "yml", - filePath: "deployment.yml", - want: true, - }, - { - name: "json", - filePath: "deployment.json", - want: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - s := yamlConfigAnalyzer{} - - got := s.Required(tt.filePath, nil) - assert.Equal(t, tt.want, got) - }) - } -} - -func Test_yamlConfigAnalyzer_Type(t *testing.T) { - s := yamlConfigAnalyzer{} - - want := analyzer.TypeYaml - got := s.Type() - assert.Equal(t, want, got) -} diff --git a/pkg/fanal/analyzer/config_analyzer.go b/pkg/fanal/analyzer/config_analyzer.go index 68af7c3041..798f73a182 100644 --- a/pkg/fanal/analyzer/config_analyzer.go +++ b/pkg/fanal/analyzer/config_analyzer.go @@ -7,9 +7,9 @@ import ( "golang.org/x/exp/slices" "golang.org/x/xerrors" - misconf "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config" "github.com/aquasecurity/trivy/pkg/fanal/types" "github.com/aquasecurity/trivy/pkg/log" + "github.com/aquasecurity/trivy/pkg/misconf" ) var configAnalyzerConstructors = map[Type]configAnalyzerConstructor{} diff --git a/pkg/fanal/analyzer/const.go b/pkg/fanal/analyzer/const.go index 7b429e876e..54fa1fd1f3 100644 --- a/pkg/fanal/analyzer/const.go +++ b/pkg/fanal/analyzer/const.go @@ -1,5 +1,7 @@ package analyzer +import "github.com/aquasecurity/defsec/pkg/detection" + type Type string const ( @@ -102,12 +104,12 @@ const ( // ================= // Structured Config // ================= - TypeYaml Type = "yaml" - TypeJSON Type = "json" - TypeDockerfile Type = "dockerfile" - TypeTerraform Type = "terraform" - TypeCloudFormation Type = "cloudFormation" - TypeHelm Type = "helm" + TypeAzureARM Type = Type(detection.FileTypeAzureARM) + TypeCloudFormation Type = Type(detection.FileTypeCloudFormation) + TypeDockerfile Type = Type(detection.FileTypeDockerfile) + TypeHelm Type = Type(detection.FileTypeHelm) + TypeKubernetes Type = Type(detection.FileTypeKubernetes) + TypeTerraform Type = Type(detection.FileTypeTerraform) // ======== // License @@ -211,11 +213,11 @@ var ( // TypeConfigFiles has all config file analyzers TypeConfigFiles = []Type{ - TypeYaml, - TypeJSON, - TypeDockerfile, - TypeTerraform, + TypeAzureARM, TypeCloudFormation, + TypeDockerfile, TypeHelm, + TypeKubernetes, + TypeTerraform, } ) diff --git a/pkg/fanal/analyzer/imgconf/dockerfile/dockerfile.go b/pkg/fanal/analyzer/imgconf/dockerfile/dockerfile.go index e61b85a89a..75cedf6db1 100644 --- a/pkg/fanal/analyzer/imgconf/dockerfile/dockerfile.go +++ b/pkg/fanal/analyzer/imgconf/dockerfile/dockerfile.go @@ -10,6 +10,7 @@ import ( "github.com/aquasecurity/trivy/pkg/fanal/analyzer" "github.com/aquasecurity/trivy/pkg/fanal/types" + "github.com/aquasecurity/trivy/pkg/mapfs" "github.com/aquasecurity/trivy/pkg/misconf" ) @@ -20,11 +21,11 @@ func init() { } type historyAnalyzer struct { - scanner misconf.Scanner + scanner *misconf.Scanner } func newHistoryAnalyzer(opts analyzer.ConfigAnalyzerOptions) (analyzer.ConfigAnalyzer, error) { - s, err := misconf.NewScanner(opts.FilePatterns, opts.MisconfScannerOption) + s, err := misconf.NewDockerfileScanner(opts.FilePatterns, opts.MisconfScannerOption) if err != nil { return nil, xerrors.Errorf("misconfiguration scanner error: %w", err) } @@ -70,15 +71,12 @@ func (a *historyAnalyzer) Analyze(ctx context.Context, input analyzer.ConfigAnal dockerfile.WriteString(strings.TrimSpace(createdBy) + "\n") } - files := []types.File{ - { - Type: types.Dockerfile, - Path: "Dockerfile", - Content: dockerfile.Bytes(), - }, + fsys := mapfs.New() + if err := fsys.WriteVirtualFile("Dockerfile", dockerfile.Bytes(), 0600); err != nil { + return nil, xerrors.Errorf("mapfs write error: %w", err) } - misconfs, err := a.scanner.Scan(ctx, files) + misconfs, err := a.scanner.Scan(ctx, fsys) if err != nil { return nil, xerrors.Errorf("history scan error: %w", err) } diff --git a/pkg/fanal/artifact/artifact.go b/pkg/fanal/artifact/artifact.go index 4af51fcff1..c4c7b95d13 100644 --- a/pkg/fanal/artifact/artifact.go +++ b/pkg/fanal/artifact/artifact.go @@ -5,9 +5,9 @@ import ( "sort" "github.com/aquasecurity/trivy/pkg/fanal/analyzer" - misconf "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config" "github.com/aquasecurity/trivy/pkg/fanal/types" "github.com/aquasecurity/trivy/pkg/fanal/walker" + "github.com/aquasecurity/trivy/pkg/misconf" ) type Option struct { diff --git a/pkg/fanal/artifact/image/image.go b/pkg/fanal/artifact/image/image.go index 024bdf5323..4193480b51 100644 --- a/pkg/fanal/artifact/image/image.go +++ b/pkg/fanal/artifact/image/image.go @@ -56,6 +56,7 @@ func NewArtifact(img types.Image, c cache.ArtifactCache, opt artifact.Option) (a Slow: opt.Slow, FilePatterns: opt.FilePatterns, DisabledAnalyzers: opt.DisabledAnalyzers, + MisconfScannerOption: opt.MisconfScannerOption, SecretScannerOption: opt.SecretScannerOption, LicenseScannerOption: opt.LicenseScannerOption, }) @@ -327,19 +328,20 @@ func (a Artifact) inspectLayer(ctx context.Context, layerInfo LayerInfo, disable result.Sort() blobInfo := types.BlobInfo{ - SchemaVersion: types.BlobJSONSchemaVersion, - Digest: layerDigest, - DiffID: layerInfo.DiffID, - CreatedBy: layerInfo.CreatedBy, - OpaqueDirs: opqDirs, - WhiteoutFiles: whFiles, - OS: result.OS, - Repository: result.Repository, - PackageInfos: result.PackageInfos, - Applications: result.Applications, - Secrets: result.Secrets, - Licenses: result.Licenses, - CustomResources: result.CustomResources, + SchemaVersion: types.BlobJSONSchemaVersion, + Digest: layerDigest, + DiffID: layerInfo.DiffID, + CreatedBy: layerInfo.CreatedBy, + OpaqueDirs: opqDirs, + WhiteoutFiles: whFiles, + OS: result.OS, + Repository: result.Repository, + PackageInfos: result.PackageInfos, + Applications: result.Applications, + Misconfigurations: result.Misconfigurations, + Secrets: result.Secrets, + Licenses: result.Licenses, + CustomResources: result.CustomResources, // For Red Hat BuildInfo: result.BuildInfo, diff --git a/pkg/fanal/artifact/image/image_test.go b/pkg/fanal/artifact/image/image_test.go index 3f707854ff..c180099e69 100644 --- a/pkg/fanal/artifact/image/image_test.go +++ b/pkg/fanal/artifact/image/image_test.go @@ -12,6 +12,12 @@ import ( "golang.org/x/xerrors" "github.com/aquasecurity/trivy/pkg/fanal/analyzer" + "github.com/aquasecurity/trivy/pkg/fanal/artifact" + image2 "github.com/aquasecurity/trivy/pkg/fanal/artifact/image" + "github.com/aquasecurity/trivy/pkg/fanal/cache" + "github.com/aquasecurity/trivy/pkg/fanal/image" + "github.com/aquasecurity/trivy/pkg/fanal/types" + _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/all" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/imgconf/apk" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/php/composer" @@ -23,13 +29,7 @@ import ( _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/pkg/dpkg" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/repo/apk" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/secret" - "github.com/aquasecurity/trivy/pkg/fanal/artifact" - image2 "github.com/aquasecurity/trivy/pkg/fanal/artifact/image" - "github.com/aquasecurity/trivy/pkg/fanal/cache" - _ "github.com/aquasecurity/trivy/pkg/fanal/handler/misconf" _ "github.com/aquasecurity/trivy/pkg/fanal/handler/sysfile" - "github.com/aquasecurity/trivy/pkg/fanal/image" - "github.com/aquasecurity/trivy/pkg/fanal/types" ) func TestArtifact_Inspect(t *testing.T) { @@ -217,17 +217,17 @@ func TestArtifact_Inspect(t *testing.T) { missingBlobsExpectation: cache.ArtifactCacheMissingBlobsExpectation{ Args: cache.ArtifactCacheMissingBlobsArgs{ ArtifactID: "sha256:c232b7d8ac8aa08aa767313d0b53084c4380d1c01a213a5971bdb039e6538313", - BlobIDs: []string{"sha256:1ee72875fbb6def206801205982d81b4c2be24974906823266224527badad8e3"}, + BlobIDs: []string{"sha256:61da8ea7801a711b5fdd7e11c47471bb98bc0537fb50bef3f46e7b67e2d90f46"}, }, Returns: cache.ArtifactCacheMissingBlobsReturns{ MissingArtifact: true, - MissingBlobIDs: []string{"sha256:1ee72875fbb6def206801205982d81b4c2be24974906823266224527badad8e3"}, + MissingBlobIDs: []string{"sha256:61da8ea7801a711b5fdd7e11c47471bb98bc0537fb50bef3f46e7b67e2d90f46"}, }, }, putBlobExpectations: []cache.ArtifactCachePutBlobExpectation{ { Args: cache.ArtifactCachePutBlobArgs{ - BlobID: "sha256:1ee72875fbb6def206801205982d81b4c2be24974906823266224527badad8e3", + BlobID: "sha256:61da8ea7801a711b5fdd7e11c47471bb98bc0537fb50bef3f46e7b67e2d90f46", BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, Digest: "", @@ -294,7 +294,7 @@ func TestArtifact_Inspect(t *testing.T) { Name: "../../test/testdata/alpine-311.tar.gz", Type: types.ArtifactContainerImage, ID: "sha256:c232b7d8ac8aa08aa767313d0b53084c4380d1c01a213a5971bdb039e6538313", - BlobIDs: []string{"sha256:1ee72875fbb6def206801205982d81b4c2be24974906823266224527badad8e3"}, + BlobIDs: []string{"sha256:61da8ea7801a711b5fdd7e11c47471bb98bc0537fb50bef3f46e7b67e2d90f46"}, ImageMetadata: types.ImageMetadata{ ID: "sha256:a187dde48cd289ac374ad8539930628314bc581a481cdb41409c9289419ddb72", DiffIDs: []string{ @@ -353,25 +353,25 @@ func TestArtifact_Inspect(t *testing.T) { Args: cache.ArtifactCacheMissingBlobsArgs{ ArtifactID: "sha256:33f9415ed2cd5a9cef5d5144333619745b9ec0f851f0684dd45fa79c6b26a650", BlobIDs: []string{ - "sha256:673f305ef9cede893bc9a1851da8152b1f7597321e06f551a1d875f20f947f5b", - "sha256:2886467019d514a49e74ce4507da571023c97798e3f0f3805e9c9826b5b993ef", - "sha256:f77cea0f8767d9520ea9001de1f1102e0e5e85ccf726c91271e3d63e963ab4d4", - "sha256:c5233a461c9ead1191adfa7a34d9cd66e6b319460939bbf0f085a3fa0faae635", + "sha256:1d02588865377e478a263c4ef2b020d8bf8d9919fdbd14243283b35249b91d4a", + "sha256:7b2d1df7e78b9e5c851676d9cc04bad8d7e86deb2661f0e15ff3d7f37bf53d53", + "sha256:57508fe06ce45edcad30f95a9da631edf746914b0ffa32fa13b83a133529828e", + "sha256:f8d6b5b326b6bad89cf20b94e1c98380187e536ec34795d18c00907f9a35aeb5", }, }, Returns: cache.ArtifactCacheMissingBlobsReturns{ MissingBlobIDs: []string{ - "sha256:673f305ef9cede893bc9a1851da8152b1f7597321e06f551a1d875f20f947f5b", - "sha256:2886467019d514a49e74ce4507da571023c97798e3f0f3805e9c9826b5b993ef", - "sha256:f77cea0f8767d9520ea9001de1f1102e0e5e85ccf726c91271e3d63e963ab4d4", - "sha256:c5233a461c9ead1191adfa7a34d9cd66e6b319460939bbf0f085a3fa0faae635", + "sha256:1d02588865377e478a263c4ef2b020d8bf8d9919fdbd14243283b35249b91d4a", + "sha256:7b2d1df7e78b9e5c851676d9cc04bad8d7e86deb2661f0e15ff3d7f37bf53d53", + "sha256:57508fe06ce45edcad30f95a9da631edf746914b0ffa32fa13b83a133529828e", + "sha256:f8d6b5b326b6bad89cf20b94e1c98380187e536ec34795d18c00907f9a35aeb5", }, }, }, putBlobExpectations: []cache.ArtifactCachePutBlobExpectation{ { Args: cache.ArtifactCachePutBlobArgs{ - BlobID: "sha256:673f305ef9cede893bc9a1851da8152b1f7597321e06f551a1d875f20f947f5b", + BlobID: "sha256:1d02588865377e478a263c4ef2b020d8bf8d9919fdbd14243283b35249b91d4a", BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, Digest: "", @@ -459,7 +459,7 @@ func TestArtifact_Inspect(t *testing.T) { }, { Args: cache.ArtifactCachePutBlobArgs{ - BlobID: "sha256:2886467019d514a49e74ce4507da571023c97798e3f0f3805e9c9826b5b993ef", + BlobID: "sha256:7b2d1df7e78b9e5c851676d9cc04bad8d7e86deb2661f0e15ff3d7f37bf53d53", BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, Digest: "", @@ -555,7 +555,7 @@ func TestArtifact_Inspect(t *testing.T) { }, { Args: cache.ArtifactCachePutBlobArgs{ - BlobID: "sha256:f77cea0f8767d9520ea9001de1f1102e0e5e85ccf726c91271e3d63e963ab4d4", + BlobID: "sha256:57508fe06ce45edcad30f95a9da631edf746914b0ffa32fa13b83a133529828e", BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, Digest: "", @@ -679,7 +679,7 @@ func TestArtifact_Inspect(t *testing.T) { }, { Args: cache.ArtifactCachePutBlobArgs{ - BlobID: "sha256:c5233a461c9ead1191adfa7a34d9cd66e6b319460939bbf0f085a3fa0faae635", + BlobID: "sha256:f8d6b5b326b6bad89cf20b94e1c98380187e536ec34795d18c00907f9a35aeb5", BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, Digest: "", @@ -1488,10 +1488,10 @@ func TestArtifact_Inspect(t *testing.T) { Type: types.ArtifactContainerImage, ID: "sha256:33f9415ed2cd5a9cef5d5144333619745b9ec0f851f0684dd45fa79c6b26a650", BlobIDs: []string{ - "sha256:673f305ef9cede893bc9a1851da8152b1f7597321e06f551a1d875f20f947f5b", - "sha256:2886467019d514a49e74ce4507da571023c97798e3f0f3805e9c9826b5b993ef", - "sha256:f77cea0f8767d9520ea9001de1f1102e0e5e85ccf726c91271e3d63e963ab4d4", - "sha256:c5233a461c9ead1191adfa7a34d9cd66e6b319460939bbf0f085a3fa0faae635", + "sha256:1d02588865377e478a263c4ef2b020d8bf8d9919fdbd14243283b35249b91d4a", + "sha256:7b2d1df7e78b9e5c851676d9cc04bad8d7e86deb2661f0e15ff3d7f37bf53d53", + "sha256:57508fe06ce45edcad30f95a9da631edf746914b0ffa32fa13b83a133529828e", + "sha256:f8d6b5b326b6bad89cf20b94e1c98380187e536ec34795d18c00907f9a35aeb5", }, ImageMetadata: types.ImageMetadata{ ID: "sha256:58701fd185bda36cab0557bb6438661831267aa4a9e0b54211c4d5317a48aff4", @@ -1585,25 +1585,25 @@ func TestArtifact_Inspect(t *testing.T) { Args: cache.ArtifactCacheMissingBlobsArgs{ ArtifactID: "sha256:33f9415ed2cd5a9cef5d5144333619745b9ec0f851f0684dd45fa79c6b26a650", BlobIDs: []string{ - "sha256:ce763fafc4c45bc6311188adfcd8b932fa42553f3324bb9ec8649e5f7c3f9f14", - "sha256:b3765fc11963a0c92cc8c8ef0c8a3c54c9a3111100ae69384049b2d7b15419ae", - "sha256:1bd6f23a3c252702080dd0e524f9ef13d8ff918e15b322fd8b5c2ceb9f5b8b4f", - "sha256:9589cedce50fd3d37c19f22a5653dece7a092edff293a598d15125eb2a4d8849", + "sha256:9a7c29b10391bcedce533e9609c58ec0e7b0132692fd287bd40592816d1bfbef", + "sha256:e15c92866a85305a909ae200974937d6febcd7a504aeb32ad0a01371c245c25e", + "sha256:6cfccd64a1b1ead1b517bad7dfda8aa0616f63a2d93e71921ff51cb70f447567", + "sha256:032128f06ff805d1ec38f171ea6ae60639175eb70bc80e2b3abc91f6fbfa343d", }, }, Returns: cache.ArtifactCacheMissingBlobsReturns{ MissingBlobIDs: []string{ - "sha256:ce763fafc4c45bc6311188adfcd8b932fa42553f3324bb9ec8649e5f7c3f9f14", - "sha256:b3765fc11963a0c92cc8c8ef0c8a3c54c9a3111100ae69384049b2d7b15419ae", - "sha256:1bd6f23a3c252702080dd0e524f9ef13d8ff918e15b322fd8b5c2ceb9f5b8b4f", - "sha256:9589cedce50fd3d37c19f22a5653dece7a092edff293a598d15125eb2a4d8849", + "sha256:9a7c29b10391bcedce533e9609c58ec0e7b0132692fd287bd40592816d1bfbef", + "sha256:e15c92866a85305a909ae200974937d6febcd7a504aeb32ad0a01371c245c25e", + "sha256:6cfccd64a1b1ead1b517bad7dfda8aa0616f63a2d93e71921ff51cb70f447567", + "sha256:032128f06ff805d1ec38f171ea6ae60639175eb70bc80e2b3abc91f6fbfa343d", }, }, }, putBlobExpectations: []cache.ArtifactCachePutBlobExpectation{ { Args: cache.ArtifactCachePutBlobArgs{ - BlobID: "sha256:ce763fafc4c45bc6311188adfcd8b932fa42553f3324bb9ec8649e5f7c3f9f14", + BlobID: "sha256:9a7c29b10391bcedce533e9609c58ec0e7b0132692fd287bd40592816d1bfbef", BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, Digest: "", @@ -1614,7 +1614,7 @@ func TestArtifact_Inspect(t *testing.T) { }, { Args: cache.ArtifactCachePutBlobArgs{ - BlobID: "sha256:b3765fc11963a0c92cc8c8ef0c8a3c54c9a3111100ae69384049b2d7b15419ae", + BlobID: "sha256:e15c92866a85305a909ae200974937d6febcd7a504aeb32ad0a01371c245c25e", BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, Digest: "", @@ -1625,7 +1625,7 @@ func TestArtifact_Inspect(t *testing.T) { }, { Args: cache.ArtifactCachePutBlobArgs{ - BlobID: "sha256:1bd6f23a3c252702080dd0e524f9ef13d8ff918e15b322fd8b5c2ceb9f5b8b4f", + BlobID: "sha256:6cfccd64a1b1ead1b517bad7dfda8aa0616f63a2d93e71921ff51cb70f447567", BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, Digest: "", @@ -1637,7 +1637,7 @@ func TestArtifact_Inspect(t *testing.T) { }, { Args: cache.ArtifactCachePutBlobArgs{ - BlobID: "sha256:9589cedce50fd3d37c19f22a5653dece7a092edff293a598d15125eb2a4d8849", + BlobID: "sha256:032128f06ff805d1ec38f171ea6ae60639175eb70bc80e2b3abc91f6fbfa343d", BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, Digest: "", @@ -1653,10 +1653,10 @@ func TestArtifact_Inspect(t *testing.T) { Type: types.ArtifactContainerImage, ID: "sha256:33f9415ed2cd5a9cef5d5144333619745b9ec0f851f0684dd45fa79c6b26a650", BlobIDs: []string{ - "sha256:ce763fafc4c45bc6311188adfcd8b932fa42553f3324bb9ec8649e5f7c3f9f14", - "sha256:b3765fc11963a0c92cc8c8ef0c8a3c54c9a3111100ae69384049b2d7b15419ae", - "sha256:1bd6f23a3c252702080dd0e524f9ef13d8ff918e15b322fd8b5c2ceb9f5b8b4f", - "sha256:9589cedce50fd3d37c19f22a5653dece7a092edff293a598d15125eb2a4d8849", + "sha256:9a7c29b10391bcedce533e9609c58ec0e7b0132692fd287bd40592816d1bfbef", + "sha256:e15c92866a85305a909ae200974937d6febcd7a504aeb32ad0a01371c245c25e", + "sha256:6cfccd64a1b1ead1b517bad7dfda8aa0616f63a2d93e71921ff51cb70f447567", + "sha256:032128f06ff805d1ec38f171ea6ae60639175eb70bc80e2b3abc91f6fbfa343d", }, ImageMetadata: types.ImageMetadata{ ID: "sha256:58701fd185bda36cab0557bb6438661831267aa4a9e0b54211c4d5317a48aff4", @@ -1739,7 +1739,7 @@ func TestArtifact_Inspect(t *testing.T) { missingBlobsExpectation: cache.ArtifactCacheMissingBlobsExpectation{ Args: cache.ArtifactCacheMissingBlobsArgs{ ArtifactID: "sha256:c232b7d8ac8aa08aa767313d0b53084c4380d1c01a213a5971bdb039e6538313", - BlobIDs: []string{"sha256:1ee72875fbb6def206801205982d81b4c2be24974906823266224527badad8e3"}, + BlobIDs: []string{"sha256:61da8ea7801a711b5fdd7e11c47471bb98bc0537fb50bef3f46e7b67e2d90f46"}, }, Returns: cache.ArtifactCacheMissingBlobsReturns{ Err: xerrors.New("MissingBlobs failed"), @@ -1753,16 +1753,16 @@ func TestArtifact_Inspect(t *testing.T) { missingBlobsExpectation: cache.ArtifactCacheMissingBlobsExpectation{ Args: cache.ArtifactCacheMissingBlobsArgs{ ArtifactID: "sha256:c232b7d8ac8aa08aa767313d0b53084c4380d1c01a213a5971bdb039e6538313", - BlobIDs: []string{"sha256:1ee72875fbb6def206801205982d81b4c2be24974906823266224527badad8e3"}, + BlobIDs: []string{"sha256:61da8ea7801a711b5fdd7e11c47471bb98bc0537fb50bef3f46e7b67e2d90f46"}, }, Returns: cache.ArtifactCacheMissingBlobsReturns{ - MissingBlobIDs: []string{"sha256:1ee72875fbb6def206801205982d81b4c2be24974906823266224527badad8e3"}, + MissingBlobIDs: []string{"sha256:61da8ea7801a711b5fdd7e11c47471bb98bc0537fb50bef3f46e7b67e2d90f46"}, }, }, putBlobExpectations: []cache.ArtifactCachePutBlobExpectation{ { Args: cache.ArtifactCachePutBlobArgs{ - BlobID: "sha256:1ee72875fbb6def206801205982d81b4c2be24974906823266224527badad8e3", + BlobID: "sha256:61da8ea7801a711b5fdd7e11c47471bb98bc0537fb50bef3f46e7b67e2d90f46", BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, Digest: "", @@ -1821,17 +1821,17 @@ func TestArtifact_Inspect(t *testing.T) { missingBlobsExpectation: cache.ArtifactCacheMissingBlobsExpectation{ Args: cache.ArtifactCacheMissingBlobsArgs{ ArtifactID: "sha256:c232b7d8ac8aa08aa767313d0b53084c4380d1c01a213a5971bdb039e6538313", - BlobIDs: []string{"sha256:1ee72875fbb6def206801205982d81b4c2be24974906823266224527badad8e3"}, + BlobIDs: []string{"sha256:61da8ea7801a711b5fdd7e11c47471bb98bc0537fb50bef3f46e7b67e2d90f46"}, }, Returns: cache.ArtifactCacheMissingBlobsReturns{ MissingArtifact: true, - MissingBlobIDs: []string{"sha256:1ee72875fbb6def206801205982d81b4c2be24974906823266224527badad8e3"}, + MissingBlobIDs: []string{"sha256:61da8ea7801a711b5fdd7e11c47471bb98bc0537fb50bef3f46e7b67e2d90f46"}, }, }, putBlobExpectations: []cache.ArtifactCachePutBlobExpectation{ { Args: cache.ArtifactCachePutBlobArgs{ - BlobID: "sha256:1ee72875fbb6def206801205982d81b4c2be24974906823266224527badad8e3", + BlobID: "sha256:61da8ea7801a711b5fdd7e11c47471bb98bc0537fb50bef3f46e7b67e2d90f46", BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, Digest: "", diff --git a/pkg/fanal/artifact/local/fs.go b/pkg/fanal/artifact/local/fs.go index 3c461be108..171d9e46ef 100644 --- a/pkg/fanal/artifact/local/fs.go +++ b/pkg/fanal/artifact/local/fs.go @@ -47,6 +47,7 @@ func NewArtifact(rootPath string, c cache.ArtifactCache, opt artifact.Option) (a Slow: opt.Slow, FilePatterns: opt.FilePatterns, DisabledAnalyzers: opt.DisabledAnalyzers, + MisconfScannerOption: opt.MisconfScannerOption, SecretScannerOption: opt.SecretScannerOption, LicenseScannerOption: opt.LicenseScannerOption, }) @@ -169,14 +170,15 @@ func (a Artifact) Inspect(ctx context.Context) (types.ArtifactReference, error) result.Sort() blobInfo := types.BlobInfo{ - SchemaVersion: types.BlobJSONSchemaVersion, - OS: result.OS, - Repository: result.Repository, - PackageInfos: result.PackageInfos, - Applications: result.Applications, - Secrets: result.Secrets, - Licenses: result.Licenses, - CustomResources: result.CustomResources, + SchemaVersion: types.BlobJSONSchemaVersion, + OS: result.OS, + Repository: result.Repository, + PackageInfos: result.PackageInfos, + Applications: result.Applications, + Misconfigurations: result.Misconfigurations, + Secrets: result.Secrets, + Licenses: result.Licenses, + CustomResources: result.CustomResources, } if err = a.handlerManager.PostHandle(ctx, result, &blobInfo); err != nil { diff --git a/pkg/fanal/artifact/local/fs_test.go b/pkg/fanal/artifact/local/fs_test.go index e3e2f8a68a..af81b3353e 100644 --- a/pkg/fanal/artifact/local/fs_test.go +++ b/pkg/fanal/artifact/local/fs_test.go @@ -12,17 +12,16 @@ import ( "golang.org/x/exp/slices" "github.com/aquasecurity/trivy/pkg/fanal/analyzer" - "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config" "github.com/aquasecurity/trivy/pkg/fanal/artifact" "github.com/aquasecurity/trivy/pkg/fanal/cache" "github.com/aquasecurity/trivy/pkg/fanal/types" + "github.com/aquasecurity/trivy/pkg/misconf" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/all" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/python/pip" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/os/alpine" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/pkg/apk" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/secret" - _ "github.com/aquasecurity/trivy/pkg/fanal/handler/misconf" _ "github.com/aquasecurity/trivy/pkg/fanal/handler/sysfile" ) @@ -34,7 +33,7 @@ func TestArtifact_Inspect(t *testing.T) { name string fields fields artifactOpt artifact.Option - scannerOpt config.ScannerOption + scannerOpt misconf.ScannerOption disabledAnalyzers []analyzer.Type disabledHandlers []types.HandlerType putBlobExpectation cache.ArtifactCachePutBlobExpectation @@ -48,7 +47,7 @@ func TestArtifact_Inspect(t *testing.T) { }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ - BlobID: "sha256:40ca14c99b2b22a5f78c1d1a2cbfeeaa3243e3fe1cf150839209ca3b5a897e62", + BlobID: "sha256:fc0c7d225197e1c103784139def1e34b642e8183cf54519cac79dd0cfdd19aba", BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, OS: types.OS{ @@ -77,9 +76,9 @@ func TestArtifact_Inspect(t *testing.T) { want: types.ArtifactReference{ Name: "host", Type: types.ArtifactFilesystem, - ID: "sha256:40ca14c99b2b22a5f78c1d1a2cbfeeaa3243e3fe1cf150839209ca3b5a897e62", + ID: "sha256:fc0c7d225197e1c103784139def1e34b642e8183cf54519cac79dd0cfdd19aba", BlobIDs: []string{ - "sha256:40ca14c99b2b22a5f78c1d1a2cbfeeaa3243e3fe1cf150839209ca3b5a897e62", + "sha256:fc0c7d225197e1c103784139def1e34b642e8183cf54519cac79dd0cfdd19aba", }, }, }, @@ -97,7 +96,7 @@ func TestArtifact_Inspect(t *testing.T) { }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ - BlobID: "sha256:8a4332f0b77c97330369206f2e1d144bfa4cd58ccba42a61d3618da8267435c8", + BlobID: "sha256:0fbf0f996ea580c0a7408a34290f2f061e6577995cd63c475ecf8b262a7622d1", BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, }, @@ -107,9 +106,9 @@ func TestArtifact_Inspect(t *testing.T) { want: types.ArtifactReference{ Name: "host", Type: types.ArtifactFilesystem, - ID: "sha256:8a4332f0b77c97330369206f2e1d144bfa4cd58ccba42a61d3618da8267435c8", + ID: "sha256:0fbf0f996ea580c0a7408a34290f2f061e6577995cd63c475ecf8b262a7622d1", BlobIDs: []string{ - "sha256:8a4332f0b77c97330369206f2e1d144bfa4cd58ccba42a61d3618da8267435c8", + "sha256:0fbf0f996ea580c0a7408a34290f2f061e6577995cd63c475ecf8b262a7622d1", }, }, }, @@ -120,7 +119,7 @@ func TestArtifact_Inspect(t *testing.T) { }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ - BlobID: "sha256:40ca14c99b2b22a5f78c1d1a2cbfeeaa3243e3fe1cf150839209ca3b5a897e62", + BlobID: "sha256:fc0c7d225197e1c103784139def1e34b642e8183cf54519cac79dd0cfdd19aba", BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, OS: types.OS{ @@ -164,7 +163,7 @@ func TestArtifact_Inspect(t *testing.T) { }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ - BlobID: "sha256:45358d29778e36270f6fafd84e45e175e7aae7c0101b72eef99cee6dc598f5d4", + BlobID: "sha256:874588e7714441c06344a11526d73fe4d8c386d85e6d5498eab3cde13cae05ac", BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, Applications: []types.Application{ @@ -186,9 +185,9 @@ func TestArtifact_Inspect(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/requirements.txt", Type: types.ArtifactFilesystem, - ID: "sha256:45358d29778e36270f6fafd84e45e175e7aae7c0101b72eef99cee6dc598f5d4", + ID: "sha256:874588e7714441c06344a11526d73fe4d8c386d85e6d5498eab3cde13cae05ac", BlobIDs: []string{ - "sha256:45358d29778e36270f6fafd84e45e175e7aae7c0101b72eef99cee6dc598f5d4", + "sha256:874588e7714441c06344a11526d73fe4d8c386d85e6d5498eab3cde13cae05ac", }, }, }, @@ -199,7 +198,7 @@ func TestArtifact_Inspect(t *testing.T) { }, putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ Args: cache.ArtifactCachePutBlobArgs{ - BlobID: "sha256:45358d29778e36270f6fafd84e45e175e7aae7c0101b72eef99cee6dc598f5d4", + BlobID: "sha256:874588e7714441c06344a11526d73fe4d8c386d85e6d5498eab3cde13cae05ac", BlobInfo: types.BlobInfo{ SchemaVersion: types.BlobJSONSchemaVersion, Applications: []types.Application{ @@ -221,9 +220,9 @@ func TestArtifact_Inspect(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/requirements.txt", Type: types.ArtifactFilesystem, - ID: "sha256:45358d29778e36270f6fafd84e45e175e7aae7c0101b72eef99cee6dc598f5d4", + ID: "sha256:874588e7714441c06344a11526d73fe4d8c386d85e6d5498eab3cde13cae05ac", BlobIDs: []string{ - "sha256:45358d29778e36270f6fafd84e45e175e7aae7c0101b72eef99cee6dc598f5d4", + "sha256:874588e7714441c06344a11526d73fe4d8c386d85e6d5498eab3cde13cae05ac", }, }, }, @@ -367,7 +366,7 @@ func TestTerraformMisconfigurationScan(t *testing.T) { dir: "./testdata/misconfig/terraform/single-failure/src", }, artifactOpt: artifact.Option{ - MisconfScannerOption: config.ScannerOption{ + MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/terraform/single-failure/rego"}, @@ -415,9 +414,9 @@ func TestTerraformMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/terraform/single-failure/src", Type: types.ArtifactFilesystem, - ID: "sha256:b3ae72efb468a0e17551fa4067c1f9d9dff9a1e520b9f8191f48829ab6e8356d", + ID: "sha256:b17b243265d2c555c049753f42c76d3dc478d55851cc9461cbd23e618cb8f0eb", BlobIDs: []string{ - "sha256:b3ae72efb468a0e17551fa4067c1f9d9dff9a1e520b9f8191f48829ab6e8356d", + "sha256:b17b243265d2c555c049753f42c76d3dc478d55851cc9461cbd23e618cb8f0eb", }, }, }, @@ -427,7 +426,7 @@ func TestTerraformMisconfigurationScan(t *testing.T) { dir: "./testdata/misconfig/terraform/multiple-failures/src", }, artifactOpt: artifact.Option{ - MisconfScannerOption: config.ScannerOption{ + MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/terraform/multiple-failures/rego"}, @@ -525,9 +524,9 @@ func TestTerraformMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/terraform/multiple-failures/src", Type: types.ArtifactFilesystem, - ID: "sha256:340197e6c02b644e4d1310647e5b2503c224caeaeb5be01187467b71827614ce", + ID: "sha256:4f9b3fe0f3d7b75fa7120740fe2f179eb5c250646d30186f47bc5eb148a77229", BlobIDs: []string{ - "sha256:340197e6c02b644e4d1310647e5b2503c224caeaeb5be01187467b71827614ce", + "sha256:4f9b3fe0f3d7b75fa7120740fe2f179eb5c250646d30186f47bc5eb148a77229", }, }, }, @@ -537,7 +536,7 @@ func TestTerraformMisconfigurationScan(t *testing.T) { dir: "./testdata/misconfig/terraform/no-results/src", }, artifactOpt: artifact.Option{ - MisconfScannerOption: config.ScannerOption{ + MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/terraform/no-results/rego"}, @@ -555,9 +554,9 @@ func TestTerraformMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/terraform/no-results/src", Type: types.ArtifactFilesystem, - ID: "sha256:1694d46ecb8151fde496faca988441a78c4fe40ddb3049f4f59467282ab9853e", + ID: "sha256:cf90e43f7fb29358faf6f486db722ee739122347ec94839c7f3861489f242213", BlobIDs: []string{ - "sha256:1694d46ecb8151fde496faca988441a78c4fe40ddb3049f4f59467282ab9853e", + "sha256:cf90e43f7fb29358faf6f486db722ee739122347ec94839c7f3861489f242213", }, }, }, @@ -567,7 +566,7 @@ func TestTerraformMisconfigurationScan(t *testing.T) { dir: "./testdata/misconfig/terraform/passed/src", }, artifactOpt: artifact.Option{ - MisconfScannerOption: config.ScannerOption{ + MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/terraform/passed/rego"}, @@ -611,9 +610,91 @@ func TestTerraformMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/terraform/passed/src", Type: types.ArtifactFilesystem, - ID: "sha256:ee80e0b4d07abe98c88119a1dc3d5cfd3b5f3cfda3b52cf3e566d7abc15f072b", + ID: "sha256:4a7baddfc7b3e06e3f246c0680d879aecf539a07a08b48ae5710e997ec486d75", BlobIDs: []string{ - "sha256:ee80e0b4d07abe98c88119a1dc3d5cfd3b5f3cfda3b52cf3e566d7abc15f072b", + "sha256:4a7baddfc7b3e06e3f246c0680d879aecf539a07a08b48ae5710e997ec486d75", + }, + }, + }, + { + name: "multiple failures busted relative paths", + fields: fields{ + dir: "./testdata/misconfig/terraform/busted-relative-paths/src/child/main.tf", + }, + artifactOpt: artifact.Option{ + MisconfScannerOption: misconf.ScannerOption{ + RegoOnly: true, + Namespaces: []string{"user"}, + PolicyPaths: []string{"./testdata/misconfig/terraform/busted-relative-paths/rego"}, + }, + }, + putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ + Args: cache.ArtifactCachePutBlobArgs{ + BlobIDAnything: true, + BlobInfo: types.BlobInfo{ + SchemaVersion: 2, + Misconfigurations: []types.Misconfiguration{ + { + FileType: "terraform", + FilePath: "main.tf", + Failures: types.MisconfResults{ + { + Namespace: "user.something", + Query: "data.user.something.deny", + Message: "No buckets allowed!", + PolicyMetadata: types.PolicyMetadata{ + ID: "TEST001", + AVDID: "AVD-TEST-0001", + Type: "Terraform Security Check", + Title: "Test policy", + Description: "This is a test policy.", + Severity: "LOW", + RecommendedActions: "Have a cup of tea.", + References: []string{"https://trivy.dev/"}, + }, + CauseMetadata: types.CauseMetadata{ + Resource: "aws_s3_bucket.one", + Provider: "Generic", + Service: "general", + StartLine: 1, + EndLine: 3, + }, + }, + { + Namespace: "user.something", + Query: "data.user.something.deny", + Message: "No buckets allowed!", + PolicyMetadata: types.PolicyMetadata{ + ID: "TEST001", + AVDID: "AVD-TEST-0001", + Type: "Terraform Security Check", + Title: "Test policy", + Description: "This is a test policy.", + Severity: "LOW", + RecommendedActions: "Have a cup of tea.", + References: []string{"https://trivy.dev/"}, + }, + CauseMetadata: types.CauseMetadata{ + Resource: "aws_s3_bucket.two", + Provider: "Generic", + Service: "general", + StartLine: 5, + EndLine: 7, + }, + }, + }, + }, + }, + }, + }, + Returns: cache.ArtifactCachePutBlobReturns{}, + }, + want: types.ArtifactReference{ + Name: "testdata/misconfig/terraform/busted-relative-paths/src/child/main.tf", + Type: types.ArtifactFilesystem, + ID: "sha256:3f85f73698c7f29b181030749808d634575547aecab68d17c114fefaaa67f990", + BlobIDs: []string{ + "sha256:3f85f73698c7f29b181030749808d634575547aecab68d17c114fefaaa67f990", }, }, }, @@ -652,7 +733,7 @@ func TestCloudFormationMisconfigurationScan(t *testing.T) { dir: "./testdata/misconfig/cloudformation/single-failure/src", }, artifactOpt: artifact.Option{ - MisconfScannerOption: config.ScannerOption{ + MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/cloudformation/single-failure/rego"}, @@ -700,9 +781,9 @@ func TestCloudFormationMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/cloudformation/single-failure/src", Type: types.ArtifactFilesystem, - ID: "sha256:f276c65a15cc7e54ec100f4abb9446b44da8cc39ff02596109faa44baf3e88b5", + ID: "sha256:3b083e0be1a8bfd270abc53a573d5491c3a39c41b88f4e978b0c48e79754e12a", BlobIDs: []string{ - "sha256:f276c65a15cc7e54ec100f4abb9446b44da8cc39ff02596109faa44baf3e88b5", + "sha256:3b083e0be1a8bfd270abc53a573d5491c3a39c41b88f4e978b0c48e79754e12a", }, }, }, @@ -712,7 +793,7 @@ func TestCloudFormationMisconfigurationScan(t *testing.T) { dir: "./testdata/misconfig/cloudformation/multiple-failures/src", }, artifactOpt: artifact.Option{ - MisconfScannerOption: config.ScannerOption{ + MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/cloudformation/multiple-failures/rego"}, @@ -782,9 +863,9 @@ func TestCloudFormationMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/cloudformation/multiple-failures/src", Type: types.ArtifactFilesystem, - ID: "sha256:e4676a9d8ff2a2da3c63f61e34759cc53f504588c72625f241f6f0ee43db8ef9", + ID: "sha256:e167a0b5edc1a723a6f6e37adfd72ebf5cd05a578d69a564cba2a2954f47ea5e", BlobIDs: []string{ - "sha256:e4676a9d8ff2a2da3c63f61e34759cc53f504588c72625f241f6f0ee43db8ef9", + "sha256:e167a0b5edc1a723a6f6e37adfd72ebf5cd05a578d69a564cba2a2954f47ea5e", }, }, }, @@ -794,7 +875,7 @@ func TestCloudFormationMisconfigurationScan(t *testing.T) { dir: "./testdata/misconfig/cloudformation/no-results/src", }, artifactOpt: artifact.Option{ - MisconfScannerOption: config.ScannerOption{ + MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/cloudformation/no-results/rego"}, @@ -812,9 +893,9 @@ func TestCloudFormationMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/cloudformation/no-results/src", Type: types.ArtifactFilesystem, - ID: "sha256:1694d46ecb8151fde496faca988441a78c4fe40ddb3049f4f59467282ab9853e", + ID: "sha256:cf90e43f7fb29358faf6f486db722ee739122347ec94839c7f3861489f242213", BlobIDs: []string{ - "sha256:1694d46ecb8151fde496faca988441a78c4fe40ddb3049f4f59467282ab9853e", + "sha256:cf90e43f7fb29358faf6f486db722ee739122347ec94839c7f3861489f242213", }, }, }, @@ -824,7 +905,7 @@ func TestCloudFormationMisconfigurationScan(t *testing.T) { dir: "./testdata/misconfig/cloudformation/passed/src", }, artifactOpt: artifact.Option{ - MisconfScannerOption: config.ScannerOption{ + MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/cloudformation/passed/rego"}, @@ -868,9 +949,9 @@ func TestCloudFormationMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/cloudformation/passed/src", Type: types.ArtifactFilesystem, - ID: "sha256:dda92729eda80706a78c3bf2f118948c175a818305c7bd9f71336b9b795d2776", + ID: "sha256:68de62641f1c26e9973cc699aa7f84f3cb02a305d73238eba6cace5d749e4549", BlobIDs: []string{ - "sha256:dda92729eda80706a78c3bf2f118948c175a818305c7bd9f71336b9b795d2776", + "sha256:68de62641f1c26e9973cc699aa7f84f3cb02a305d73238eba6cace5d749e4549", }, }, }, @@ -909,7 +990,7 @@ func TestDockerfileMisconfigurationScan(t *testing.T) { dir: "./testdata/misconfig/dockerfile/single-failure/src", }, artifactOpt: artifact.Option{ - MisconfScannerOption: config.ScannerOption{ + MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/dockerfile/single-failure/rego"}, @@ -954,9 +1035,9 @@ func TestDockerfileMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/dockerfile/single-failure/src", Type: types.ArtifactFilesystem, - ID: "sha256:80337e1de2fb019bd8e43c88cb532f4715cf58384063ef7c63ef5f55e7eb4a5c", + ID: "sha256:998908fee16ac8aa658138e5bda73f5ffba4f1d194e9d3b3e274a8082b4af580", BlobIDs: []string{ - "sha256:80337e1de2fb019bd8e43c88cb532f4715cf58384063ef7c63ef5f55e7eb4a5c", + "sha256:998908fee16ac8aa658138e5bda73f5ffba4f1d194e9d3b3e274a8082b4af580", }, }, }, @@ -966,7 +1047,7 @@ func TestDockerfileMisconfigurationScan(t *testing.T) { dir: "./testdata/misconfig/dockerfile/multiple-failures/src", }, artifactOpt: artifact.Option{ - MisconfScannerOption: config.ScannerOption{ + MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/dockerfile/multiple-failures/rego"}, @@ -1011,9 +1092,9 @@ func TestDockerfileMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/dockerfile/multiple-failures/src", Type: types.ArtifactFilesystem, - ID: "sha256:80337e1de2fb019bd8e43c88cb532f4715cf58384063ef7c63ef5f55e7eb4a5c", + ID: "sha256:998908fee16ac8aa658138e5bda73f5ffba4f1d194e9d3b3e274a8082b4af580", BlobIDs: []string{ - "sha256:80337e1de2fb019bd8e43c88cb532f4715cf58384063ef7c63ef5f55e7eb4a5c", + "sha256:998908fee16ac8aa658138e5bda73f5ffba4f1d194e9d3b3e274a8082b4af580", }, }, }, @@ -1023,7 +1104,7 @@ func TestDockerfileMisconfigurationScan(t *testing.T) { dir: "./testdata/misconfig/dockerfile/no-results/src", }, artifactOpt: artifact.Option{ - MisconfScannerOption: config.ScannerOption{ + MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/dockerfile/no-results/rego"}, @@ -1041,9 +1122,9 @@ func TestDockerfileMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/dockerfile/no-results/src", Type: types.ArtifactFilesystem, - ID: "sha256:1694d46ecb8151fde496faca988441a78c4fe40ddb3049f4f59467282ab9853e", + ID: "sha256:cf90e43f7fb29358faf6f486db722ee739122347ec94839c7f3861489f242213", BlobIDs: []string{ - "sha256:1694d46ecb8151fde496faca988441a78c4fe40ddb3049f4f59467282ab9853e", + "sha256:cf90e43f7fb29358faf6f486db722ee739122347ec94839c7f3861489f242213", }, }, }, @@ -1053,7 +1134,7 @@ func TestDockerfileMisconfigurationScan(t *testing.T) { dir: "./testdata/misconfig/dockerfile/passed/src", }, artifactOpt: artifact.Option{ - MisconfScannerOption: config.ScannerOption{ + MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/dockerfile/passed/rego"}, @@ -1100,9 +1181,9 @@ func TestDockerfileMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/dockerfile/passed/src", Type: types.ArtifactFilesystem, - ID: "sha256:165d6b849191f10ab1e2834cea9da9decbd6bf005efdb2e4afcef6df0ec53955", + ID: "sha256:836ab9fec50d3ff799f01dee1db9d5340294fa0348011370d55848be04696f6b", BlobIDs: []string{ - "sha256:165d6b849191f10ab1e2834cea9da9decbd6bf005efdb2e4afcef6df0ec53955", + "sha256:836ab9fec50d3ff799f01dee1db9d5340294fa0348011370d55848be04696f6b", }, }, }, @@ -1141,7 +1222,7 @@ func TestKubernetesMisconfigurationScan(t *testing.T) { dir: "./testdata/misconfig/kubernetes/single-failure/src", }, artifactOpt: artifact.Option{ - MisconfScannerOption: config.ScannerOption{ + MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/kubernetes/single-failure/rego"}, @@ -1191,9 +1272,9 @@ func TestKubernetesMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/kubernetes/single-failure/src", Type: types.ArtifactFilesystem, - ID: "sha256:6502a485fddeaac944a70b7e25dec5a779ae7dc10a64dbb8acfc08bec5a207a0", + ID: "sha256:c2ff22ba22599a7b5423c1b275b013aab56cc22eb732624db4b1bfbdf6b62743", BlobIDs: []string{ - "sha256:6502a485fddeaac944a70b7e25dec5a779ae7dc10a64dbb8acfc08bec5a207a0", + "sha256:c2ff22ba22599a7b5423c1b275b013aab56cc22eb732624db4b1bfbdf6b62743", }, }, }, @@ -1203,7 +1284,7 @@ func TestKubernetesMisconfigurationScan(t *testing.T) { dir: "./testdata/misconfig/kubernetes/multiple-failures/src", }, artifactOpt: artifact.Option{ - MisconfScannerOption: config.ScannerOption{ + MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/kubernetes/multiple-failures/rego"}, @@ -1276,9 +1357,9 @@ func TestKubernetesMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/kubernetes/multiple-failures/src", Type: types.ArtifactFilesystem, - ID: "sha256:12db0860b146463e15a2e5143742c7268e1de1d3f3655f669891d7f532934734", + ID: "sha256:485e85ab412143ca5ab48f09c2a3dcacf8283c28c4f451a4b63d377ba2a21c15", BlobIDs: []string{ - "sha256:12db0860b146463e15a2e5143742c7268e1de1d3f3655f669891d7f532934734", + "sha256:485e85ab412143ca5ab48f09c2a3dcacf8283c28c4f451a4b63d377ba2a21c15", }, }, }, @@ -1288,7 +1369,7 @@ func TestKubernetesMisconfigurationScan(t *testing.T) { dir: "./testdata/misconfig/kubernetes/no-results/src", }, artifactOpt: artifact.Option{ - MisconfScannerOption: config.ScannerOption{ + MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/kubernetes/no-results/rego"}, @@ -1306,9 +1387,9 @@ func TestKubernetesMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/kubernetes/no-results/src", Type: types.ArtifactFilesystem, - ID: "sha256:20043d42935fe45a25fd24949d6efad9d7fd52674bad6b8d29a4af97ed485e7a", + ID: "sha256:e68b0e0ac19f7ef311025a3dd587cab0512e51cd19f80e3a8f7dde342979933a", BlobIDs: []string{ - "sha256:20043d42935fe45a25fd24949d6efad9d7fd52674bad6b8d29a4af97ed485e7a", + "sha256:e68b0e0ac19f7ef311025a3dd587cab0512e51cd19f80e3a8f7dde342979933a", }, }, }, @@ -1318,7 +1399,7 @@ func TestKubernetesMisconfigurationScan(t *testing.T) { dir: "./testdata/misconfig/kubernetes/passed/src", }, artifactOpt: artifact.Option{ - MisconfScannerOption: config.ScannerOption{ + MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/kubernetes/passed/rego"}, @@ -1365,9 +1446,9 @@ func TestKubernetesMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/kubernetes/passed/src", Type: types.ArtifactFilesystem, - ID: "sha256:7f3de08246eabb3277e4ec95e65d8e15b6fe4b50eb0414fd043690b94c08cbb3", + ID: "sha256:f6d3e8b62915ad822e643fabf146214e8e3a8349ea2ba509366748e858a42159", BlobIDs: []string{ - "sha256:7f3de08246eabb3277e4ec95e65d8e15b6fe4b50eb0414fd043690b94c08cbb3", + "sha256:f6d3e8b62915ad822e643fabf146214e8e3a8349ea2ba509366748e858a42159", }, }, }, @@ -1406,7 +1487,7 @@ func TestAzureARMMisconfigurationScan(t *testing.T) { dir: "./testdata/misconfig/azurearm/single-failure/src", }, artifactOpt: artifact.Option{ - MisconfScannerOption: config.ScannerOption{ + MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/azurearm/single-failure/rego"}, @@ -1454,9 +1535,9 @@ func TestAzureARMMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/azurearm/single-failure/src", Type: types.ArtifactFilesystem, - ID: "sha256:8d6ebb66f1cb65e92f90114b5b4555d53f8c8051a8a27b5a717644668d76e3a3", + ID: "sha256:96697231b9abb6529c3fab2df31316730edd53ec2e8fbb5f7dbd2179e1c8bf3b", BlobIDs: []string{ - "sha256:8d6ebb66f1cb65e92f90114b5b4555d53f8c8051a8a27b5a717644668d76e3a3", + "sha256:96697231b9abb6529c3fab2df31316730edd53ec2e8fbb5f7dbd2179e1c8bf3b", }, }, }, @@ -1466,7 +1547,7 @@ func TestAzureARMMisconfigurationScan(t *testing.T) { dir: "./testdata/misconfig/azurearm/multiple-failures/src", }, artifactOpt: artifact.Option{ - MisconfScannerOption: config.ScannerOption{ + MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/azurearm/multiple-failures/rego"}, @@ -1536,9 +1617,9 @@ func TestAzureARMMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/azurearm/multiple-failures/src", Type: types.ArtifactFilesystem, - ID: "sha256:640e36de479b97e639dc361863540467ad80caf6b0024555f64171e31a055cc5", + ID: "sha256:682771ea4115a19d2835e83c0b5b49caf9a5f97664c69c2f9f5c18eae34cac88", BlobIDs: []string{ - "sha256:640e36de479b97e639dc361863540467ad80caf6b0024555f64171e31a055cc5", + "sha256:682771ea4115a19d2835e83c0b5b49caf9a5f97664c69c2f9f5c18eae34cac88", }, }, }, @@ -1548,7 +1629,7 @@ func TestAzureARMMisconfigurationScan(t *testing.T) { dir: "./testdata/misconfig/azurearm/no-results/src", }, artifactOpt: artifact.Option{ - MisconfScannerOption: config.ScannerOption{ + MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/azurearm/no-results/rego"}, @@ -1566,9 +1647,9 @@ func TestAzureARMMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/azurearm/no-results/src", Type: types.ArtifactFilesystem, - ID: "sha256:1694d46ecb8151fde496faca988441a78c4fe40ddb3049f4f59467282ab9853e", + ID: "sha256:cf90e43f7fb29358faf6f486db722ee739122347ec94839c7f3861489f242213", BlobIDs: []string{ - "sha256:1694d46ecb8151fde496faca988441a78c4fe40ddb3049f4f59467282ab9853e", + "sha256:cf90e43f7fb29358faf6f486db722ee739122347ec94839c7f3861489f242213", }, }, }, @@ -1578,7 +1659,7 @@ func TestAzureARMMisconfigurationScan(t *testing.T) { dir: "./testdata/misconfig/azurearm/passed/src", }, artifactOpt: artifact.Option{ - MisconfScannerOption: config.ScannerOption{ + MisconfScannerOption: misconf.ScannerOption{ RegoOnly: true, Namespaces: []string{"user"}, PolicyPaths: []string{"./testdata/misconfig/azurearm/passed/rego"}, @@ -1622,9 +1703,9 @@ func TestAzureARMMisconfigurationScan(t *testing.T) { want: types.ArtifactReference{ Name: "testdata/misconfig/azurearm/passed/src", Type: types.ArtifactFilesystem, - ID: "sha256:f90eabc657cedc83e3ebfd3c8f8840d4fea55a73f813e0a904ac4b0ae76f39ce", + ID: "sha256:e266ca6dc704e6d71a55370d65bd72c5a6bcbb38eb2cff19db827863c4af68f3", BlobIDs: []string{ - "sha256:f90eabc657cedc83e3ebfd3c8f8840d4fea55a73f813e0a904ac4b0ae76f39ce", + "sha256:e266ca6dc704e6d71a55370d65bd72c5a6bcbb38eb2cff19db827863c4af68f3", }, }, }, @@ -1645,3 +1726,121 @@ func TestAzureARMMisconfigurationScan(t *testing.T) { }) } } + +func TestMixedConfigurationScan(t *testing.T) { + type fields struct { + dir string + } + tests := []struct { + name string + fields fields + putBlobExpectation cache.ArtifactCachePutBlobExpectation + artifactOpt artifact.Option + want types.ArtifactReference + }{ + { + name: "single failure each within terraform and cloudformation", + fields: fields{ + dir: "./testdata/misconfig/mixed/src", + }, + artifactOpt: artifact.Option{ + MisconfScannerOption: misconf.ScannerOption{ + RegoOnly: true, + Namespaces: []string{"user"}, + PolicyPaths: []string{"./testdata/misconfig/mixed/rego"}, + }, + }, + putBlobExpectation: cache.ArtifactCachePutBlobExpectation{ + Args: cache.ArtifactCachePutBlobArgs{ + BlobIDAnything: true, + BlobInfo: types.BlobInfo{ + SchemaVersion: 2, + Misconfigurations: []types.Misconfiguration{ + { + FileType: "terraform", + FilePath: "main.tf", + Failures: types.MisconfResults{ + { + Namespace: "user.something", + Query: "data.user.something.deny", + Message: "No buckets allowed!", + PolicyMetadata: types.PolicyMetadata{ + ID: "TEST001", + AVDID: "AVD-TEST-0001", + Type: "Terraform Security Check", + Title: "Test policy", + Description: "This is a test policy.", + Severity: "LOW", + RecommendedActions: "Have a cup of tea.", + References: []string{"https://trivy.dev/"}, + }, + CauseMetadata: types.CauseMetadata{ + Resource: "aws_s3_bucket.asd", + Provider: "Generic", + Service: "general", + StartLine: 1, + EndLine: 3, + }, + }, + }, + }, + { + FileType: "cloudformation", + FilePath: "main.yaml", + Failures: types.MisconfResults{ + { + Namespace: "user.something", + Query: "data.user.something.deny", + Message: "No buckets allowed!", + PolicyMetadata: types.PolicyMetadata{ + ID: "TEST001", + AVDID: "AVD-TEST-0001", + Type: "CloudFormation Security Check", + Title: "Test policy", + Description: "This is a test policy.", + Severity: "LOW", + RecommendedActions: "Have a cup of tea.", + References: []string{"https://trivy.dev/"}, + }, + CauseMetadata: types.CauseMetadata{ + Resource: "main.yaml:3-6", + Provider: "Generic", + Service: "general", + StartLine: 3, + EndLine: 6, + }, + }, + }, + }, + }, + }, + }, + Returns: cache.ArtifactCachePutBlobReturns{}, + }, + want: types.ArtifactReference{ + Name: "testdata/misconfig/mixed/src", + Type: types.ArtifactFilesystem, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := new(cache.MockArtifactCache) + c.ApplyPutBlobExpectation(tt.putBlobExpectation) + tt.artifactOpt.DisabledHandlers = []types.HandlerType{ + types.SystemFileFilteringPostHandler, + } + a, err := NewArtifact(tt.fields.dir, c, tt.artifactOpt) + require.NoError(t, err) + + got, err := a.Inspect(context.Background()) + require.NoError(t, err) + require.NotNil(t, got) + + assert.Equal(t, tt.want.Name, got.Name) + assert.Equal(t, tt.want.Type, got.Type) + }) + } + +} diff --git a/pkg/fanal/artifact/local/testdata/misconfig/mixed/rego/policy.rego b/pkg/fanal/artifact/local/testdata/misconfig/mixed/rego/policy.rego new file mode 100644 index 0000000000..a9399362d2 --- /dev/null +++ b/pkg/fanal/artifact/local/testdata/misconfig/mixed/rego/policy.rego @@ -0,0 +1,32 @@ +package user.something + + __rego_metadata__ := { + "id": "TEST001", + "avd_id": "AVD-TEST-0001", + "title": "Test policy", + "short_code": "no-buckets", + "severity": "LOW", + "description": "This is a test policy.", + "recommended_actions": "Have a cup of tea.", + "url": "https://trivy.dev/", + } + + # taken from defsec rego lib to mimic behaviour + result(msg, cause) = result { + metadata := object.get(cause, "__defsec_metadata", cause) + result := { + "msg": msg, + "startline": object.get(metadata, "startline", 0), + "endline": object.get(metadata, "endline", 0), + "filepath": object.get(metadata, "filepath", ""), + "explicit": object.get(metadata, "explicit", false), + "managed": object.get(metadata, "managed", true), + "fskey": object.get(metadata, "fskey", ""), + "resource": object.get(metadata, "resource", ""), + } + } + + deny[res] { + bucket := input.aws.s3.buckets[_] + res := result("No buckets allowed!", bucket) + } \ No newline at end of file diff --git a/pkg/fanal/artifact/local/testdata/misconfig/mixed/src/main.tf b/pkg/fanal/artifact/local/testdata/misconfig/mixed/src/main.tf new file mode 100644 index 0000000000..62697f2ab0 --- /dev/null +++ b/pkg/fanal/artifact/local/testdata/misconfig/mixed/src/main.tf @@ -0,0 +1,3 @@ +resource "aws_s3_bucket" "asd" { + + } \ No newline at end of file diff --git a/pkg/fanal/artifact/local/testdata/misconfig/mixed/src/main.yaml b/pkg/fanal/artifact/local/testdata/misconfig/mixed/src/main.yaml new file mode 100644 index 0000000000..c3e9adcc2f --- /dev/null +++ b/pkg/fanal/artifact/local/testdata/misconfig/mixed/src/main.yaml @@ -0,0 +1,6 @@ +--- +Resources: + S3Bucket: + Type: 'AWS::S3::Bucket' + Properties: + BucketName: public-bucket \ No newline at end of file diff --git a/pkg/fanal/artifact/local/testdata/misconfig/terraform/busted-relative-paths/rego/policy.rego b/pkg/fanal/artifact/local/testdata/misconfig/terraform/busted-relative-paths/rego/policy.rego new file mode 100644 index 0000000000..a9399362d2 --- /dev/null +++ b/pkg/fanal/artifact/local/testdata/misconfig/terraform/busted-relative-paths/rego/policy.rego @@ -0,0 +1,32 @@ +package user.something + + __rego_metadata__ := { + "id": "TEST001", + "avd_id": "AVD-TEST-0001", + "title": "Test policy", + "short_code": "no-buckets", + "severity": "LOW", + "description": "This is a test policy.", + "recommended_actions": "Have a cup of tea.", + "url": "https://trivy.dev/", + } + + # taken from defsec rego lib to mimic behaviour + result(msg, cause) = result { + metadata := object.get(cause, "__defsec_metadata", cause) + result := { + "msg": msg, + "startline": object.get(metadata, "startline", 0), + "endline": object.get(metadata, "endline", 0), + "filepath": object.get(metadata, "filepath", ""), + "explicit": object.get(metadata, "explicit", false), + "managed": object.get(metadata, "managed", true), + "fskey": object.get(metadata, "fskey", ""), + "resource": object.get(metadata, "resource", ""), + } + } + + deny[res] { + bucket := input.aws.s3.buckets[_] + res := result("No buckets allowed!", bucket) + } \ No newline at end of file diff --git a/pkg/fanal/artifact/local/testdata/misconfig/terraform/busted-relative-paths/src/child/main.tf b/pkg/fanal/artifact/local/testdata/misconfig/terraform/busted-relative-paths/src/child/main.tf new file mode 100644 index 0000000000..192ffbe802 --- /dev/null +++ b/pkg/fanal/artifact/local/testdata/misconfig/terraform/busted-relative-paths/src/child/main.tf @@ -0,0 +1,11 @@ +resource "aws_s3_bucket" "one" { + + } + + resource "aws_s3_bucket" "two" { + + } + + module "module_in_parent_dir" { + source = "../does not exist anywhere/" +} \ No newline at end of file diff --git a/pkg/fanal/artifact/local/testdata/misconfig/terraform/busted-relative-paths/src/parent/more.tf b/pkg/fanal/artifact/local/testdata/misconfig/terraform/busted-relative-paths/src/parent/more.tf new file mode 100644 index 0000000000..b06a4528a4 --- /dev/null +++ b/pkg/fanal/artifact/local/testdata/misconfig/terraform/busted-relative-paths/src/parent/more.tf @@ -0,0 +1,3 @@ +resource "aws_s3_bucket" "three" { + + } \ No newline at end of file diff --git a/pkg/fanal/artifact/remote/git_test.go b/pkg/fanal/artifact/remote/git_test.go index 6b8dcff052..484c12ddfa 100644 --- a/pkg/fanal/artifact/remote/git_test.go +++ b/pkg/fanal/artifact/remote/git_test.go @@ -184,9 +184,9 @@ func TestArtifact_Inspect(t *testing.T) { want: types.ArtifactReference{ Name: ts.URL + "/test.git", Type: types.ArtifactRemoteRepository, - ID: "sha256:43256f1a50997b78fd91690ac248cde42d56ca996201a596282e9d84e1dccaeb", + ID: "sha256:37247f99bb62bd4b866758a2aff29374eba956dc82a73430efbf405f5a2fd60b", BlobIDs: []string{ - "sha256:43256f1a50997b78fd91690ac248cde42d56ca996201a596282e9d84e1dccaeb", + "sha256:37247f99bb62bd4b866758a2aff29374eba956dc82a73430efbf405f5a2fd60b", }, }, }, diff --git a/pkg/fanal/artifact/vm/vm.go b/pkg/fanal/artifact/vm/vm.go index a09104c249..cf4aa2a6e7 100644 --- a/pkg/fanal/artifact/vm/vm.go +++ b/pkg/fanal/artifact/vm/vm.go @@ -90,6 +90,7 @@ func NewArtifact(target string, c cache.ArtifactCache, opt artifact.Option) (art Group: opt.AnalyzerGroup, FilePatterns: opt.FilePatterns, DisabledAnalyzers: opt.DisabledAnalyzers, + MisconfScannerOption: opt.MisconfScannerOption, SecretScannerOption: opt.SecretScannerOption, LicenseScannerOption: opt.LicenseScannerOption, }) diff --git a/pkg/fanal/artifact/vm/vm_test.go b/pkg/fanal/artifact/vm/vm_test.go index 0d58ec6a6d..aa393b2405 100644 --- a/pkg/fanal/artifact/vm/vm_test.go +++ b/pkg/fanal/artifact/vm/vm_test.go @@ -12,11 +12,11 @@ import ( "github.com/aquasecurity/trivy/internal/testutil" "github.com/aquasecurity/trivy/pkg/fanal/analyzer" - "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config" "github.com/aquasecurity/trivy/pkg/fanal/artifact" "github.com/aquasecurity/trivy/pkg/fanal/artifact/vm" "github.com/aquasecurity/trivy/pkg/fanal/cache" "github.com/aquasecurity/trivy/pkg/fanal/types" + "github.com/aquasecurity/trivy/pkg/misconf" ebsfile "github.com/masahiro331/go-ebs-file" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/os/amazonlinux" @@ -72,7 +72,7 @@ func TestArtifact_Inspect(t *testing.T) { name string filePath string artifactOpt artifact.Option - scannerOpt config.ScannerOption + scannerOpt misconf.ScannerOption disabledAnalyzers []analyzer.Type disabledHandlers []types.HandlerType missingBlobsExpectation cache.ArtifactCacheMissingBlobsExpectation diff --git a/pkg/fanal/cache/key_test.go b/pkg/fanal/cache/key_test.go index 0dddc38110..ed0a1aa21d 100644 --- a/pkg/fanal/cache/key_test.go +++ b/pkg/fanal/cache/key_test.go @@ -1,14 +1,14 @@ package cache import ( - "github.com/aquasecurity/trivy/pkg/fanal/analyzer" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config" + "github.com/aquasecurity/trivy/pkg/fanal/analyzer" "github.com/aquasecurity/trivy/pkg/fanal/artifact" + "github.com/aquasecurity/trivy/pkg/misconf" ) func TestCalcKey(t *testing.T) { @@ -183,7 +183,7 @@ func TestCalcKey(t *testing.T) { SkipDirs: tt.args.skipDirs, FilePatterns: tt.args.patterns, - MisconfScannerOption: config.ScannerOption{ + MisconfScannerOption: misconf.ScannerOption{ PolicyPaths: tt.args.policy, DataPaths: tt.args.data, }, diff --git a/pkg/fanal/external/config_scan.go b/pkg/fanal/external/config_scan.go index 3e01f21f79..237b344e0a 100644 --- a/pkg/fanal/external/config_scan.go +++ b/pkg/fanal/external/config_scan.go @@ -5,12 +5,12 @@ import ( "errors" "github.com/aquasecurity/trivy/pkg/fanal/analyzer" - "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config" "github.com/aquasecurity/trivy/pkg/fanal/applier" "github.com/aquasecurity/trivy/pkg/fanal/artifact" "github.com/aquasecurity/trivy/pkg/fanal/artifact/local" "github.com/aquasecurity/trivy/pkg/fanal/cache" "github.com/aquasecurity/trivy/pkg/fanal/types" + "github.com/aquasecurity/trivy/pkg/misconf" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/all" ) @@ -41,7 +41,7 @@ func NewConfigScanner(cacheDir string, policyPaths, dataPaths, namespaces []stri func (s ConfigScanner) Scan(dir string) ([]types.Misconfiguration, error) { art, err := local.NewArtifact(dir, s.cache, artifact.Option{ - MisconfScannerOption: config.ScannerOption{ + MisconfScannerOption: misconf.ScannerOption{ PolicyPaths: s.policyPaths, DataPaths: s.dataPaths, Namespaces: s.namespaces, diff --git a/pkg/fanal/external/config_scan_test.go b/pkg/fanal/external/config_scan_test.go index 49f24c9ad1..094fdf41ac 100644 --- a/pkg/fanal/external/config_scan_test.go +++ b/pkg/fanal/external/config_scan_test.go @@ -8,8 +8,9 @@ import ( "github.com/stretchr/testify/require" "github.com/aquasecurity/trivy/pkg/fanal/external" - _ "github.com/aquasecurity/trivy/pkg/fanal/handler/misconf" "github.com/aquasecurity/trivy/pkg/fanal/types" + + _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/all" ) func TestConfigScanner_Scan(t *testing.T) { diff --git a/pkg/fanal/handler/all/import.go b/pkg/fanal/handler/all/import.go index 51169adfb3..37c7c55457 100644 --- a/pkg/fanal/handler/all/import.go +++ b/pkg/fanal/handler/all/import.go @@ -1,7 +1,6 @@ package all import ( - _ "github.com/aquasecurity/trivy/pkg/fanal/handler/misconf" _ "github.com/aquasecurity/trivy/pkg/fanal/handler/sysfile" _ "github.com/aquasecurity/trivy/pkg/fanal/handler/unpackaged" ) diff --git a/pkg/fanal/handler/misconf/misconf.go b/pkg/fanal/handler/misconf/misconf.go deleted file mode 100644 index 9945638eda..0000000000 --- a/pkg/fanal/handler/misconf/misconf.go +++ /dev/null @@ -1,63 +0,0 @@ -package misconf - -import ( - "context" - _ "embed" - - "golang.org/x/xerrors" - - "github.com/aquasecurity/trivy/pkg/fanal/analyzer" - "github.com/aquasecurity/trivy/pkg/fanal/artifact" - "github.com/aquasecurity/trivy/pkg/fanal/handler" - "github.com/aquasecurity/trivy/pkg/fanal/types" - "github.com/aquasecurity/trivy/pkg/misconf" -) - -func init() { - handler.RegisterPostHandlerInit(types.MisconfPostHandler, newMisconfPostHandler) -} - -const version = 1 - -type misconfPostHandler struct { - scanner misconf.Scanner -} - -func newMisconfPostHandler(artifactOpt artifact.Option) (handler.PostHandler, error) { - s, err := misconf.NewScanner(artifactOpt.FilePatterns, artifactOpt.MisconfScannerOption) - if err != nil { - return nil, xerrors.Errorf("scanner init error: %w", err) - } - return misconfPostHandler{ - scanner: s, - }, nil -} - -// Handle detects misconfigurations. -func (h misconfPostHandler) Handle(ctx context.Context, result *analyzer.AnalysisResult, blob *types.BlobInfo) error { - files, ok := result.Files[h.Type()] - if !ok { - return nil - } - - misconfs, err := h.scanner.Scan(ctx, files) - if err != nil { - return xerrors.Errorf("misconfiguration scan error: %w", err) - } - - blob.Misconfigurations = misconfs - - return nil -} - -func (h misconfPostHandler) Version() int { - return version -} - -func (h misconfPostHandler) Type() types.HandlerType { - return types.MisconfPostHandler -} - -func (h misconfPostHandler) Priority() int { - return types.MisconfPostHandlerPriority -} diff --git a/pkg/fanal/types/handler.go b/pkg/fanal/types/handler.go index bd2110ef9a..7d60d1d68d 100644 --- a/pkg/fanal/types/handler.go +++ b/pkg/fanal/types/handler.go @@ -4,13 +4,11 @@ type HandlerType string const ( SystemFileFilteringPostHandler HandlerType = "system-file-filter" - MisconfPostHandler HandlerType = "misconf" UnpackagedPostHandler HandlerType = "unpackaged" // SystemFileFilteringPostHandlerPriority should be higher than other handlers. // Otherwise, other handlers need to process unnecessary files. SystemFileFilteringPostHandlerPriority = 100 - MisconfPostHandlerPriority = 50 UnpackagedPostHandlerPriority = 50 ) diff --git a/pkg/fanal/types/misconf.go b/pkg/fanal/types/misconf.go index 7a5f3ff426..7f021cd922 100644 --- a/pkg/fanal/types/misconf.go +++ b/pkg/fanal/types/misconf.go @@ -84,10 +84,14 @@ func (r MisconfResults) Less(i, j int) bool { switch { case r[i].Type != r[j].Type: return r[i].Type < r[j].Type + case r[i].AVDID != r[j].AVDID: + return r[i].AVDID < r[j].AVDID case r[i].ID != r[j].ID: return r[i].ID < r[j].ID case r[i].Severity != r[j].Severity: return r[i].Severity < r[j].Severity + case r[i].Resource != r[j].Resource: + return r[i].Resource < r[j].Resource } return r[i].Message < r[j].Message } diff --git a/pkg/mapfs/file.go b/pkg/mapfs/file.go index 9b2d32aa72..7dd990a5c8 100644 --- a/pkg/mapfs/file.go +++ b/pkg/mapfs/file.go @@ -21,10 +21,10 @@ var separator = "/" // - a virtual file // - a virtual dir type file struct { - path string // underlying file path - data []byte // virtual file, only either of 'path' or 'data' has a value. - stat fileStat - files syncx.Map[string, *file] + underlyingPath string // underlying file path + data []byte // virtual file, only either of 'path' or 'data' has a value. + stat fileStat + files syncx.Map[string, *file] } func (f *file) isVirtual() bool { @@ -55,7 +55,7 @@ func (f *file) open() (fs.File, error) { return nil, xerrors.Errorf("read dir error: %w", err) } return &mapDir{ - path: f.path, + path: f.underlyingPath, fileStat: f.stat, entry: entries, }, nil @@ -66,7 +66,7 @@ func (f *file) open() (fs.File, error) { offset: 0, }, nil default: // Real file - return os.Open(f.path) + return os.Open(f.underlyingPath) } } @@ -140,7 +140,7 @@ func (f *file) ReadDir(name string) ([]fs.DirEntry, error) { entries = append(entries, &value.stat) } else { var fi os.FileInfo - fi, err = os.Stat(value.path) + fi, err = os.Stat(value.underlyingPath) if err != nil { return false } @@ -208,7 +208,7 @@ func (f *file) WriteFile(path, underlyingPath string) error { if len(parts) == 1 { f.files.Store(parts[0], &file{ - path: underlyingPath, + underlyingPath: underlyingPath, }) return nil } @@ -345,7 +345,7 @@ func (f *openMapFile) ReadAt(b []byte, offset int64) (int, error) { return n, nil } -// A mapDir is a directory fs.File (so also an fs.ReadDirFile) open for reading. +// A mapDir is a directory fs.File (so also fs.ReadDirFile) open for reading. type mapDir struct { path string fileStat diff --git a/pkg/mapfs/fs.go b/pkg/mapfs/fs.go index d075b02d23..114c5f0697 100644 --- a/pkg/mapfs/fs.go +++ b/pkg/mapfs/fs.go @@ -50,6 +50,13 @@ func (m *FS) Filter(skippedFiles []string) (*FS, error) { if len(skippedFiles) == 0 { return m, nil } + filter := func(path string, _ fs.DirEntry) (bool, error) { + return slices.Contains(skippedFiles, path), nil + } + return m.FilterFunc(filter) +} + +func (m *FS) FilterFunc(fn func(path string, d fs.DirEntry) (bool, error)) (*FS, error) { newFS := New() err := fs.WalkDir(m, ".", func(path string, d fs.DirEntry, err error) error { if err != nil { @@ -60,7 +67,9 @@ func (m *FS) Filter(skippedFiles []string) (*FS, error) { return newFS.MkdirAll(path, d.Type().Perm()) } - if slices.Contains(skippedFiles, path) { + if filtered, err := fn(path, d); err != nil { + return err + } else if filtered { return nil } @@ -68,7 +77,11 @@ func (m *FS) Filter(skippedFiles []string) (*FS, error) { if err != nil { return xerrors.Errorf("unable to get %s: %w", path, err) } - return newFS.WriteFile(path, f.path) + // Virtual file + if f.underlyingPath == "" { + return newFS.WriteVirtualFile(path, f.data, f.stat.mode) + } + return newFS.WriteFile(path, f.underlyingPath) }) if err != nil { return nil, xerrors.Errorf("walk error", err) @@ -102,7 +115,7 @@ func (m *FS) Stat(name string) (fs.FileInfo, error) { if f.isVirtual() { return &f.stat, nil } - return os.Stat(f.path) + return os.Stat(f.underlyingPath) } // ReadDir reads the named directory diff --git a/pkg/misconf/scanner.go b/pkg/misconf/scanner.go index 901e20d194..4141f9ffd6 100644 --- a/pkg/misconf/scanner.go +++ b/pkg/misconf/scanner.go @@ -1,12 +1,11 @@ package misconf import ( - "bytes" "context" _ "embed" "fmt" + "io" "io/fs" - "os" "path/filepath" "sort" "strings" @@ -25,8 +24,6 @@ import ( k8sscanner "github.com/aquasecurity/defsec/pkg/scanners/kubernetes" "github.com/aquasecurity/defsec/pkg/scanners/options" tfscanner "github.com/aquasecurity/defsec/pkg/scanners/terraform" - "github.com/aquasecurity/memoryfs" - "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config" "github.com/aquasecurity/trivy/pkg/fanal/types" "github.com/aquasecurity/trivy/pkg/log" "github.com/aquasecurity/trivy/pkg/mapfs" @@ -41,12 +38,155 @@ var enabledDefsecTypes = map[detection.FileType]string{ detection.FileTypeHelm: types.Helm, } -type Scanner struct { - filePatterns []string - scanners map[string]scanners.FSScanner +type ScannerOption struct { + Trace bool + RegoOnly bool + Namespaces []string + PolicyPaths []string + DataPaths []string + DisableEmbeddedPolicies bool + + HelmValues []string + HelmValueFiles []string + HelmFileValues []string + HelmStringValues []string + TerraformTFVars []string + K8sVersion string } -func NewScanner(filePatterns []string, opt config.ScannerOption) (Scanner, error) { +func (o *ScannerOption) Sort() { + sort.Strings(o.Namespaces) + sort.Strings(o.PolicyPaths) + sort.Strings(o.DataPaths) +} + +type Scanner struct { + fileType detection.FileType + scanner scanners.FSScanner + hasFilePattern bool +} + +func NewAzureARMScanner(filePatterns []string, opt ScannerOption) (*Scanner, error) { + return newScanner(detection.FileTypeAzureARM, filePatterns, opt) +} + +func NewCloudFormationScanner(filePatterns []string, opt ScannerOption) (*Scanner, error) { + return newScanner(detection.FileTypeCloudFormation, filePatterns, opt) +} + +func NewDockerfileScanner(filePatterns []string, opt ScannerOption) (*Scanner, error) { + return newScanner(detection.FileTypeDockerfile, filePatterns, opt) +} + +func NewHelmScanner(filePatterns []string, opt ScannerOption) (*Scanner, error) { + return newScanner(detection.FileTypeHelm, filePatterns, opt) +} + +func NewKubernetesScanner(filePatterns []string, opt ScannerOption) (*Scanner, error) { + return newScanner(detection.FileTypeKubernetes, filePatterns, opt) +} + +func NewTerraformScanner(filePatterns []string, opt ScannerOption) (*Scanner, error) { + return newScanner(detection.FileTypeTerraform, filePatterns, opt) +} + +func newScanner(t detection.FileType, filePatterns []string, opt ScannerOption) (*Scanner, error) { + opts, err := scannerOptions(t, opt) + if err != nil { + return nil, err + } + + var scanner scanners.FSScanner + switch t { + case detection.FileTypeAzureARM: + scanner = arm.New(opts...) + case detection.FileTypeCloudFormation: + scanner = cfscanner.New(opts...) + case detection.FileTypeDockerfile: + scanner = dfscanner.NewScanner(opts...) + case detection.FileTypeHelm: + scanner = helm.New(opts...) + case detection.FileTypeKubernetes: + scanner = k8sscanner.NewScanner(opts...) + case detection.FileTypeTerraform: + scanner = tfscanner.New(opts...) + } + + return &Scanner{ + fileType: t, + scanner: scanner, + hasFilePattern: hasFilePattern(t, filePatterns), + }, nil +} + +func (s *Scanner) Scan(ctx context.Context, fsys fs.FS) ([]types.Misconfiguration, error) { + newfs, err := s.filterFS(fsys) + if err != nil { + return nil, xerrors.Errorf("fs filter error: %w", err) + } else if newfs == nil { + // Skip scanning if no relevant files are found + return nil, nil + } + + log.Logger.Debugf("Scanning %s files for misconfigurations...", s.scanner.Name()) + results, err := s.scanner.ScanFS(ctx, newfs, ".") + if err != nil { + if _, ok := err.(*cfparser.InvalidContentError); ok { + log.Logger.Errorf("scan %q was broken with InvalidContentError: %v", s.scanner.Name(), err) + return nil, nil + } + return nil, xerrors.Errorf("scan config error: %w", err) + } + + configType := enabledDefsecTypes[s.fileType] + misconfs := ResultsToMisconf(configType, s.scanner.Name(), results) + + // Sort misconfigurations + for _, misconf := range misconfs { + sort.Sort(misconf.Successes) + sort.Sort(misconf.Warnings) + sort.Sort(misconf.Failures) + } + + return misconfs, nil +} + +func (s *Scanner) filterFS(fsys fs.FS) (fs.FS, error) { + mfs, ok := fsys.(*mapfs.FS) + if !ok { + // Unable to filter this filesystem + return fsys, nil + } + + var foundRelevantFile bool + filter := func(path string, d fs.DirEntry) (bool, error) { + file, err := fsys.Open(path) + if err != nil { + return false, err + } + rs, ok := file.(io.ReadSeeker) + if !ok { + return false, xerrors.Errorf("type assertion error: %w", err) + } + defer file.Close() + + if !s.hasFilePattern && !detection.IsType(path, rs, s.fileType) { + return true, nil + } + foundRelevantFile = true + return false, nil + } + newfs, err := mfs.FilterFunc(filter) + if err != nil { + return nil, xerrors.Errorf("fs filter error: %w", err) + } + if !foundRelevantFile { + return nil, nil + } + return newfs, nil +} + +func scannerOptions(t detection.FileType, opt ScannerOption) ([]options.ScannerOption, error) { opts := []options.ScannerOption{ options.ScannerWithSkipRequiredCheck(true), options.ScannerWithEmbeddedPolicies(!opt.DisableEmbeddedPolicies), @@ -54,7 +194,7 @@ func NewScanner(filePatterns []string, opt config.ScannerOption) (Scanner, error policyFS, policyPaths, err := createPolicyFS(opt.PolicyPaths) if err != nil { - return Scanner{}, err + return nil, err } if policyFS != nil { opts = append(opts, options.ScannerWithPolicyFilesystem(policyFS)) @@ -62,7 +202,7 @@ func NewScanner(filePatterns []string, opt config.ScannerOption) (Scanner, error dataFS, dataPaths, err := createDataFS(opt.DataPaths, opt.K8sVersion) if err != nil { - return Scanner{}, err + return nil, err } opts = append(opts, options.ScannerWithDataDirs(dataPaths...)) opts = append(opts, options.ScannerWithDataFilesystem(dataFS)) @@ -87,23 +227,26 @@ func NewScanner(filePatterns []string, opt config.ScannerOption) (Scanner, error opts = append(opts, options.ScannerWithPolicyNamespaces(opt.Namespaces...)) } - helmOpts := addHelmOpts(opts, opt) - tfOpts := addTFOpts(opts, opt) - - return Scanner{ - filePatterns: filePatterns, - scanners: map[string]scanners.FSScanner{ - types.AzureARM: arm.New(opts...), - types.Terraform: tfscanner.New(tfOpts...), - types.CloudFormation: cfscanner.New(opts...), - types.Dockerfile: dfscanner.NewScanner(opts...), - types.Kubernetes: k8sscanner.NewScanner(opts...), - types.Helm: helm.New(helmOpts...), - }, - }, nil + switch t { + case detection.FileTypeHelm: + return addHelmOpts(opts, opt), nil + case detection.FileTypeTerraform: + return addTFOpts(opts, opt), nil + default: + return opts, nil + } } -func addTFOpts(opts []options.ScannerOption, scannerOption config.ScannerOption) []options.ScannerOption { +func hasFilePattern(t detection.FileType, filePatterns []string) bool { + for _, pattern := range filePatterns { + if strings.HasPrefix(pattern, fmt.Sprintf("%s:", t)) { + return true + } + } + return false +} + +func addTFOpts(opts []options.ScannerOption, scannerOption ScannerOption) []options.ScannerOption { if len(scannerOption.TerraformTFVars) > 0 { opts = append(opts, tfscanner.ScannerWithTFVarsPaths(scannerOption.TerraformTFVars...)) } @@ -111,7 +254,7 @@ func addTFOpts(opts []options.ScannerOption, scannerOption config.ScannerOption) return opts } -func addHelmOpts(opts []options.ScannerOption, scannerOption config.ScannerOption) []options.ScannerOption { +func addHelmOpts(opts []options.ScannerOption, scannerOption ScannerOption) []options.ScannerOption { if len(scannerOption.HelmValueFiles) > 0 { opts = append(opts, helm.ScannerWithValuesFile(scannerOption.HelmValueFiles...)) } @@ -178,84 +321,7 @@ func createDataFS(dataPaths []string, k8sVersion string) (fs.FS, []string, error return fsys, dataPaths, nil } -func (s *Scanner) hasCustomPatternForType(t string) bool { - for _, pattern := range s.filePatterns { - if strings.HasPrefix(pattern, t+":") { - return true - } - } - return false -} - -// Scan detects misconfigurations. -func (s *Scanner) Scan(ctx context.Context, files []types.File) ([]types.Misconfiguration, error) { - mapMemoryFS := make(map[string]*memoryfs.FS) - for t := range s.scanners { - mapMemoryFS[t] = memoryfs.New() - } - - for _, file := range files { - for defsecType, localType := range enabledDefsecTypes { - buffer := bytes.NewReader(file.Content) - if !s.hasCustomPatternForType(localType) && !detection.IsType(file.Path, buffer, defsecType) { - continue - } - // Replace with more detailed config type - file.Type = localType - - if memfs, ok := mapMemoryFS[file.Type]; ok { - if filepath.Dir(file.Path) != "." { - if err := memfs.MkdirAll(filepath.Dir(file.Path), os.ModePerm); err != nil { - return nil, xerrors.Errorf("memoryfs mkdir error: %w", err) - } - } - if err := memfs.WriteFile(file.Path, file.Content, os.ModePerm); err != nil { - return nil, xerrors.Errorf("memoryfs write error: %w", err) - } - } - } - } - - var misconfs []types.Misconfiguration - for t, scanner := range s.scanners { - results, err := scanner.ScanFS(ctx, mapMemoryFS[t], ".") - if err != nil { - if _, ok := err.(*cfparser.InvalidContentError); ok { - log.Logger.Errorf("scan %q was broken with InvalidContentError: %v", scanner.Name(), err) - continue - } - return nil, xerrors.Errorf("scan config error: %w", err) - } - - misconfs = append(misconfs, ResultsToMisconf(t, scanner.Name(), results)...) - } - - // Sort misconfigurations - for _, misconf := range misconfs { - sort.Slice(misconf.Successes, func(i, j int) bool { - if misconf.Successes[i].AVDID == misconf.Successes[j].AVDID { - return misconf.Successes[i].StartLine < misconf.Successes[j].StartLine - } - return misconf.Successes[i].AVDID < misconf.Successes[j].AVDID - }) - sort.Slice(misconf.Warnings, func(i, j int) bool { - if misconf.Warnings[i].AVDID == misconf.Warnings[j].AVDID { - return misconf.Warnings[i].StartLine < misconf.Warnings[j].StartLine - } - return misconf.Warnings[i].AVDID < misconf.Warnings[j].AVDID - }) - sort.Slice(misconf.Failures, func(i, j int) bool { - if misconf.Failures[i].AVDID == misconf.Failures[j].AVDID { - return misconf.Failures[i].StartLine < misconf.Failures[j].StartLine - } - return misconf.Failures[i].AVDID < misconf.Failures[j].AVDID - }) - } - - return misconfs, nil -} - -// This function is exported for trivy-plugin-aqua purposes only +// ResultsToMisconf is exported for trivy-plugin-aqua purposes only func ResultsToMisconf(configType string, scannerName string, results scan.Results) []types.Misconfiguration { misconfs := map[string]types.Misconfiguration{} diff --git a/pkg/misconf/scanner_test.go b/pkg/misconf/scanner_test.go index 850e05861b..f42897303e 100644 --- a/pkg/misconf/scanner_test.go +++ b/pkg/misconf/scanner_test.go @@ -9,25 +9,85 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config" "github.com/aquasecurity/trivy/pkg/fanal/types" + "github.com/aquasecurity/trivy/pkg/mapfs" ) +func TestScannerOption_Sort(t *testing.T) { + type fields struct { + Namespaces []string + PolicyPaths []string + DataPaths []string + } + tests := []struct { + name string + fields fields + want ScannerOption + }{ + { + name: "happy path", + fields: fields{ + Namespaces: []string{"main", "custom", "default"}, + PolicyPaths: []string{"policy"}, + DataPaths: []string{"data/b", "data/c", "data/a"}, + }, + want: ScannerOption{ + Namespaces: []string{"custom", "default", "main"}, + PolicyPaths: []string{"policy"}, + DataPaths: []string{"data/a", "data/b", "data/c"}, + }, + }, + { + name: "missing some fields", + fields: fields{ + Namespaces: []string{"main"}, + PolicyPaths: nil, + DataPaths: nil, + }, + want: ScannerOption{ + Namespaces: []string{"main"}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + o := ScannerOption{ + Namespaces: tt.fields.Namespaces, + PolicyPaths: tt.fields.PolicyPaths, + DataPaths: tt.fields.DataPaths, + } + o.Sort() + + assert.Equal(t, tt.want, o) + }) + } +} + func TestScanner_Scan(t *testing.T) { + type fields struct { + filePatterns []string + opt ScannerOption + } + type file struct { + path string + content []byte + } tests := []struct { name string - files []types.File - filePatterns []string + fields fields + files []file wantFilePath string wantFileType string }{ { name: "happy path. Dockerfile", - files: []types.File{ + fields: fields{ + opt: ScannerOption{}, + }, + files: []file{ { - Path: "Dockerfile", - Type: types.Dockerfile, - Content: []byte(`FROM alpine`), + path: "Dockerfile", + content: []byte(`FROM alpine`), }, }, wantFilePath: "Dockerfile", @@ -35,26 +95,35 @@ func TestScanner_Scan(t *testing.T) { }, { name: "happy path. Dockerfile with custom file name", - files: []types.File{ + fields: fields{ + filePatterns: []string{"dockerfile:dockerf"}, + opt: ScannerOption{}, + }, + files: []file{ { - Path: "dockerf", - Type: types.Dockerfile, - Content: []byte(`FROM alpine`), + path: "dockerf", + content: []byte(`FROM alpine`), }, }, - filePatterns: []string{"dockerfile:dockerf"}, wantFilePath: "dockerf", wantFileType: types.Dockerfile, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - s, err := NewScanner(tt.filePatterns, config.ScannerOption{}) + // Create a virtual filesystem for testing + fsys := mapfs.New() + for _, f := range tt.files { + err := fsys.WriteVirtualFile(f.path, f.content, 0666) + require.NoError(t, err) + } + + s, err := NewDockerfileScanner(tt.fields.filePatterns, tt.fields.opt) require.NoError(t, err) - misconfs, err := s.Scan(context.Background(), tt.files) + misconfs, err := s.Scan(context.Background(), fsys) require.NoError(t, err) - assert.Equal(t, 1, len(misconfs), "wrong number of misconfigurations found") + require.Equal(t, 1, len(misconfs), "wrong number of misconfigurations found") assert.Equal(t, tt.wantFilePath, misconfs[0].FilePath, "filePaths don't equal") assert.Equal(t, tt.wantFileType, misconfs[0].FileType, "fileTypes don't equal") })