github.com/khulnasoft-lab/defsec@v1.0.5-0.20230827010352-5e9f46893d95/pkg/scanners/cloudformation/scanner.go (about) 1 package cloudformation 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "io/fs" 8 "sort" 9 "sync" 10 11 "github.com/khulnasoft-lab/defsec/pkg/types" 12 13 "github.com/khulnasoft-lab/defsec/pkg/debug" 14 "github.com/khulnasoft-lab/defsec/pkg/framework" 15 "github.com/khulnasoft-lab/defsec/pkg/scanners/options" 16 17 "github.com/khulnasoft-lab/defsec/pkg/scanners/cloudformation/parser" 18 19 "github.com/khulnasoft-lab/defsec/pkg/scan" 20 21 adapter "github.com/khulnasoft-lab/defsec/internal/adapters/cloudformation" 22 "github.com/khulnasoft-lab/defsec/internal/rules" 23 "github.com/khulnasoft-lab/defsec/pkg/rego" 24 _ "github.com/khulnasoft-lab/defsec/pkg/rules" 25 "github.com/khulnasoft-lab/defsec/pkg/scanners" 26 ) 27 28 var _ scanners.FSScanner = (*Scanner)(nil) 29 30 type Scanner struct { 31 debug debug.Logger 32 policyDirs []string 33 policyReaders []io.Reader 34 parser *parser.Parser 35 regoScanner *rego.Scanner 36 skipRequired bool 37 regoOnly bool 38 loadEmbeddedPolicies bool 39 loadEmbeddedLibraries bool 40 options []options.ScannerOption 41 frameworks []framework.Framework 42 spec string 43 sync.Mutex 44 } 45 46 func (s *Scanner) SetFrameworks(frameworks []framework.Framework) { 47 s.frameworks = frameworks 48 } 49 50 func (s *Scanner) SetSpec(spec string) { 51 s.spec = spec 52 } 53 54 func (s *Scanner) SetUseEmbeddedPolicies(b bool) { 55 s.loadEmbeddedPolicies = b 56 } 57 58 func (s *Scanner) SetUseEmbeddedLibraries(b bool) { 59 s.loadEmbeddedLibraries = b 60 } 61 62 func (s *Scanner) SetRegoOnly(regoOnly bool) { 63 s.regoOnly = regoOnly 64 } 65 66 func (s *Scanner) Name() string { 67 return "CloudFormation" 68 } 69 70 func (s *Scanner) SetPolicyReaders(readers []io.Reader) { 71 s.policyReaders = readers 72 } 73 74 func (s *Scanner) SetSkipRequiredCheck(skip bool) { 75 s.skipRequired = skip 76 } 77 78 func (s *Scanner) SetDebugWriter(writer io.Writer) { 79 s.debug = debug.New(writer, "cloudformation", "scanner") 80 } 81 82 func (s *Scanner) SetPolicyDirs(dirs ...string) { 83 s.policyDirs = dirs 84 } 85 86 func (s *Scanner) SetPolicyFilesystem(_ fs.FS) { 87 // handled by rego when option is passed on 88 } 89 90 func (s *Scanner) SetDataFilesystem(_ fs.FS) { 91 // handled by rego when option is passed on 92 } 93 func (s *Scanner) SetRegoErrorLimit(_ int) {} 94 95 func (s *Scanner) SetTraceWriter(_ io.Writer) {} 96 func (s *Scanner) SetPerResultTracingEnabled(_ bool) {} 97 func (s *Scanner) SetDataDirs(_ ...string) {} 98 func (s *Scanner) SetPolicyNamespaces(_ ...string) {} 99 100 // New creates a new Scanner 101 func New(opts ...options.ScannerOption) *Scanner { 102 s := &Scanner{ 103 options: opts, 104 } 105 for _, opt := range opts { 106 opt(s) 107 } 108 s.parser = parser.New(options.ParserWithSkipRequiredCheck(s.skipRequired)) 109 return s 110 } 111 112 func (s *Scanner) initRegoScanner(srcFS fs.FS) (*rego.Scanner, error) { 113 s.Lock() 114 defer s.Unlock() 115 if s.regoScanner != nil { 116 return s.regoScanner, nil 117 } 118 regoScanner := rego.NewScanner(types.SourceCloud, s.options...) 119 regoScanner.SetParentDebugLogger(s.debug) 120 if err := regoScanner.LoadPolicies(s.loadEmbeddedLibraries, s.loadEmbeddedPolicies, srcFS, s.policyDirs, s.policyReaders); err != nil { 121 return nil, err 122 } 123 s.regoScanner = regoScanner 124 return regoScanner, nil 125 } 126 127 func (s *Scanner) ScanFS(ctx context.Context, fs fs.FS, dir string) (results scan.Results, err error) { 128 129 contexts, err := s.parser.ParseFS(ctx, fs, dir) 130 if err != nil { 131 return nil, err 132 } 133 134 if len(contexts) == 0 { 135 return nil, nil 136 } 137 138 regoScanner, err := s.initRegoScanner(fs) 139 if err != nil { 140 return nil, err 141 } 142 143 for _, cfCtx := range contexts { 144 if cfCtx == nil { 145 continue 146 } 147 fileResults, err := s.scanFileContext(ctx, regoScanner, cfCtx, fs) 148 if err != nil { 149 return nil, err 150 } 151 results = append(results, fileResults...) 152 } 153 sort.Slice(results, func(i, j int) bool { 154 return results[i].Rule().AVDID < results[j].Rule().AVDID 155 }) 156 return results, nil 157 } 158 159 func (s *Scanner) ScanFile(ctx context.Context, fs fs.FS, path string) (scan.Results, error) { 160 161 cfCtx, err := s.parser.ParseFile(ctx, fs, path) 162 if err != nil { 163 return nil, err 164 } 165 166 regoScanner, err := s.initRegoScanner(fs) 167 if err != nil { 168 return nil, err 169 } 170 171 results, err := s.scanFileContext(ctx, regoScanner, cfCtx, fs) 172 if err != nil { 173 return nil, err 174 } 175 results.SetSourceAndFilesystem("", fs, false) 176 177 sort.Slice(results, func(i, j int) bool { 178 return results[i].Rule().AVDID < results[j].Rule().AVDID 179 }) 180 return results, nil 181 } 182 183 func (s *Scanner) scanFileContext(ctx context.Context, regoScanner *rego.Scanner, cfCtx *parser.FileContext, fs fs.FS) (results scan.Results, err error) { 184 state := adapter.Adapt(*cfCtx) 185 if state == nil { 186 return nil, nil 187 } 188 if !s.regoOnly { 189 for _, rule := range rules.GetFrameworkRules(s.frameworks...) { 190 select { 191 case <-ctx.Done(): 192 return nil, ctx.Err() 193 default: 194 } 195 if rule.Rule().RegoPackage != "" { 196 continue 197 } 198 evalResult := rule.Evaluate(state) 199 if len(evalResult) > 0 { 200 s.debug.Log("Found %d results for %s", len(evalResult), rule.Rule().AVDID) 201 for _, scanResult := range evalResult { 202 203 ref := scanResult.Metadata().Reference() 204 205 if ref == "" && scanResult.Metadata().Parent() != nil { 206 ref = scanResult.Metadata().Parent().Reference() 207 } 208 209 description := getDescription(scanResult, ref) 210 scanResult.OverrideDescription(description) 211 results = append(results, scanResult) 212 } 213 } 214 } 215 } 216 regoResults, err := regoScanner.ScanInput(ctx, rego.Input{ 217 Path: cfCtx.Metadata().Range().GetFilename(), 218 FS: fs, 219 Contents: state.ToRego(), 220 }) 221 if err != nil { 222 return nil, fmt.Errorf("rego scan error: %w", err) 223 } 224 return append(results, regoResults...), nil 225 } 226 227 func getDescription(scanResult scan.Result, ref string) string { 228 switch scanResult.Status() { 229 case scan.StatusPassed: 230 return fmt.Sprintf("Resource '%s' passed check: %s", ref, scanResult.Rule().Summary) 231 case scan.StatusIgnored: 232 return fmt.Sprintf("Resource '%s' had check ignored: %s", ref, scanResult.Rule().Summary) 233 default: 234 return scanResult.Description() 235 } 236 }