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