github.com/aquasecurity/trivy-iac@v0.8.1-0.20240127024015-3d8e412cf0ab/pkg/scanners/helm/scanner.go (about) 1 package helm 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "io/fs" 8 "path/filepath" 9 "strings" 10 11 "github.com/aquasecurity/defsec/pkg/debug" 12 "github.com/aquasecurity/defsec/pkg/framework" 13 "github.com/aquasecurity/defsec/pkg/scan" 14 "github.com/aquasecurity/defsec/pkg/types" 15 "github.com/liamg/memoryfs" 16 17 "github.com/aquasecurity/defsec/pkg/rego" 18 "github.com/aquasecurity/defsec/pkg/scanners/options" 19 "github.com/aquasecurity/trivy-iac/pkg/detection" 20 "github.com/aquasecurity/trivy-iac/pkg/scanners" 21 "github.com/aquasecurity/trivy-iac/pkg/scanners/helm/parser" 22 kparser "github.com/aquasecurity/trivy-iac/pkg/scanners/kubernetes/parser" 23 ) 24 25 var _ scanners.FSScanner = (*Scanner)(nil) 26 var _ options.ConfigurableScanner = (*Scanner)(nil) 27 28 type Scanner struct { 29 policyDirs []string 30 dataDirs []string 31 debug debug.Logger 32 options []options.ScannerOption 33 parserOptions []options.ParserOption 34 policyReaders []io.Reader 35 loadEmbeddedLibraries bool 36 loadEmbeddedPolicies bool 37 policyFS fs.FS 38 skipRequired bool 39 frameworks []framework.Framework 40 spec string 41 } 42 43 func (s *Scanner) SetSpec(spec string) { 44 s.spec = spec 45 } 46 47 func (s *Scanner) SetRegoOnly(bool) { 48 } 49 50 func (s *Scanner) SetFrameworks(frameworks []framework.Framework) { 51 s.frameworks = frameworks 52 } 53 54 // New creates a new Scanner 55 func New(options ...options.ScannerOption) *Scanner { 56 s := &Scanner{ 57 options: options, 58 } 59 60 for _, option := range options { 61 option(s) 62 } 63 return s 64 } 65 66 func (s *Scanner) AddParserOptions(options ...options.ParserOption) { 67 s.parserOptions = append(s.parserOptions, options...) 68 } 69 70 func (s *Scanner) SetUseEmbeddedPolicies(b bool) { 71 s.loadEmbeddedPolicies = b 72 } 73 74 func (s *Scanner) SetUseEmbeddedLibraries(b bool) { 75 s.loadEmbeddedLibraries = b 76 } 77 78 func (s *Scanner) Name() string { 79 return "Helm" 80 } 81 82 func (s *Scanner) SetPolicyReaders(readers []io.Reader) { 83 s.policyReaders = readers 84 } 85 86 func (s *Scanner) SetSkipRequiredCheck(skip bool) { 87 s.skipRequired = skip 88 } 89 90 func (s *Scanner) SetDebugWriter(writer io.Writer) { 91 s.debug = debug.New(writer, "helm", "scanner") 92 } 93 94 func (s *Scanner) SetTraceWriter(_ io.Writer) { 95 // handled by rego later - nothing to do for now... 96 } 97 98 func (s *Scanner) SetPerResultTracingEnabled(_ bool) { 99 // handled by rego later - nothing to do for now... 100 } 101 102 func (s *Scanner) SetPolicyDirs(dirs ...string) { 103 s.policyDirs = dirs 104 } 105 106 func (s *Scanner) SetDataDirs(dirs ...string) { 107 s.dataDirs = dirs 108 } 109 110 func (s *Scanner) SetPolicyNamespaces(namespaces ...string) { 111 // handled by rego later - nothing to do for now... 112 } 113 114 func (s *Scanner) SetPolicyFilesystem(policyFS fs.FS) { 115 s.policyFS = policyFS 116 } 117 118 func (s *Scanner) SetDataFilesystem(_ fs.FS) {} 119 func (s *Scanner) SetRegoErrorLimit(_ int) {} 120 121 func (s *Scanner) ScanFS(ctx context.Context, target fs.FS, path string) (scan.Results, error) { 122 123 var results []scan.Result 124 if err := fs.WalkDir(target, path, func(path string, d fs.DirEntry, err error) error { 125 select { 126 case <-ctx.Done(): 127 return ctx.Err() 128 default: 129 } 130 131 if err != nil { 132 return err 133 } 134 135 if d.IsDir() { 136 return nil 137 } 138 139 if detection.IsArchive(path) { 140 if scanResults, err := s.getScanResults(path, ctx, target); err != nil { 141 return err 142 } else { 143 results = append(results, scanResults...) 144 } 145 } 146 147 if strings.HasSuffix(path, "Chart.yaml") { 148 if scanResults, err := s.getScanResults(filepath.Dir(path), ctx, target); err != nil { 149 return err 150 } else { 151 results = append(results, scanResults...) 152 } 153 } 154 155 return nil 156 }); err != nil { 157 return nil, err 158 } 159 160 return results, nil 161 162 } 163 164 func (s *Scanner) getScanResults(path string, ctx context.Context, target fs.FS) (results []scan.Result, err error) { 165 helmParser := parser.New(path, s.parserOptions...) 166 167 if err := helmParser.ParseFS(ctx, target, path); err != nil { 168 return nil, err 169 } 170 171 chartFiles, err := helmParser.RenderedChartFiles() 172 if err != nil { // not valid helm, maybe some other yaml etc., abort 173 s.debug.Log("Failed to render Chart files: %s", err) 174 return nil, nil 175 } 176 177 regoScanner := rego.NewScanner(types.SourceKubernetes, s.options...) 178 policyFS := target 179 if s.policyFS != nil { 180 policyFS = s.policyFS 181 } 182 if err := regoScanner.LoadPolicies(s.loadEmbeddedLibraries, s.loadEmbeddedPolicies, policyFS, s.policyDirs, s.policyReaders); err != nil { 183 return nil, fmt.Errorf("policies load: %w", err) 184 } 185 for _, file := range chartFiles { 186 file := file 187 s.debug.Log("Processing rendered chart file: %s", file.TemplateFilePath) 188 189 manifests, err := kparser.New().Parse(strings.NewReader(file.ManifestContent), file.TemplateFilePath) 190 if err != nil { 191 return nil, fmt.Errorf("unmarshal yaml: %w", err) 192 } 193 for _, manifest := range manifests { 194 fileResults, err := regoScanner.ScanInput(ctx, rego.Input{ 195 Path: file.TemplateFilePath, 196 Contents: manifest, 197 FS: target, 198 }) 199 if err != nil { 200 return nil, fmt.Errorf("scanning error: %w", err) 201 } 202 203 if len(fileResults) > 0 { 204 renderedFS := memoryfs.New() 205 if err := renderedFS.MkdirAll(filepath.Dir(file.TemplateFilePath), fs.ModePerm); err != nil { 206 return nil, err 207 } 208 if err := renderedFS.WriteLazyFile(file.TemplateFilePath, func() (io.Reader, error) { 209 return strings.NewReader(file.ManifestContent), nil 210 }, fs.ModePerm); err != nil { 211 return nil, err 212 } 213 fileResults.SetSourceAndFilesystem(helmParser.ChartSource, renderedFS, detection.IsArchive(helmParser.ChartSource)) 214 } 215 216 results = append(results, fileResults...) 217 } 218 219 } 220 return results, nil 221 }