github.com/aquasecurity/trivy-iac@v0.8.1-0.20240127024015-3d8e412cf0ab/pkg/scanners/azure/arm/scanner.go (about) 1 package arm 2 3 import ( 4 "context" 5 "fmt" 6 7 "io" 8 "io/fs" 9 "sync" 10 11 "github.com/aquasecurity/defsec/pkg/debug" 12 "github.com/aquasecurity/defsec/pkg/framework" 13 "github.com/aquasecurity/defsec/pkg/rego" 14 "github.com/aquasecurity/defsec/pkg/rules" 15 "github.com/aquasecurity/defsec/pkg/scan" 16 "github.com/aquasecurity/defsec/pkg/scanners/options" 17 "github.com/aquasecurity/defsec/pkg/state" 18 "github.com/aquasecurity/defsec/pkg/types" 19 20 "github.com/aquasecurity/trivy-iac/internal/adapters/arm" 21 "github.com/aquasecurity/trivy-iac/pkg/scanners" 22 "github.com/aquasecurity/trivy-iac/pkg/scanners/azure" 23 "github.com/aquasecurity/trivy-iac/pkg/scanners/azure/arm/parser" 24 ) 25 26 var _ scanners.FSScanner = (*Scanner)(nil) 27 var _ options.ConfigurableScanner = (*Scanner)(nil) 28 29 type Scanner struct { 30 scannerOptions []options.ScannerOption 31 parserOptions []options.ParserOption 32 debug debug.Logger 33 frameworks []framework.Framework 34 skipRequired bool 35 regoOnly bool 36 loadEmbeddedPolicies bool 37 loadEmbeddedLibraries bool 38 policyDirs []string 39 policyReaders []io.Reader 40 regoScanner *rego.Scanner 41 spec string 42 sync.Mutex 43 } 44 45 func (s *Scanner) SetSpec(spec string) { 46 s.spec = spec 47 } 48 49 func (s *Scanner) SetRegoOnly(regoOnly bool) { 50 s.regoOnly = regoOnly 51 } 52 53 func New(opts ...options.ScannerOption) *Scanner { 54 scanner := &Scanner{ 55 scannerOptions: opts, 56 } 57 for _, opt := range opts { 58 opt(scanner) 59 } 60 return scanner 61 } 62 63 func (s *Scanner) Name() string { 64 return "Azure ARM" 65 } 66 67 func (s *Scanner) SetDebugWriter(writer io.Writer) { 68 s.debug = debug.New(writer, "azure", "arm") 69 s.parserOptions = append(s.parserOptions, options.ParserWithDebug(writer)) 70 } 71 72 func (s *Scanner) SetPolicyDirs(dirs ...string) { 73 s.policyDirs = dirs 74 } 75 76 func (s *Scanner) SetSkipRequiredCheck(skipRequired bool) { 77 s.skipRequired = skipRequired 78 } 79 func (s *Scanner) SetPolicyReaders(readers []io.Reader) { 80 s.policyReaders = readers 81 } 82 83 func (s *Scanner) SetPolicyFilesystem(_ fs.FS) { 84 // handled by rego when option is passed on 85 } 86 func (s *Scanner) SetDataFilesystem(_ fs.FS) { 87 // handled by rego when option is passed on 88 } 89 90 func (s *Scanner) SetUseEmbeddedPolicies(b bool) { 91 s.loadEmbeddedPolicies = b 92 } 93 94 func (s *Scanner) SetUseEmbeddedLibraries(b bool) { 95 s.loadEmbeddedLibraries = b 96 } 97 98 func (s *Scanner) SetFrameworks(frameworks []framework.Framework) { 99 s.frameworks = frameworks 100 } 101 102 func (s *Scanner) SetTraceWriter(io.Writer) {} 103 func (s *Scanner) SetPerResultTracingEnabled(bool) {} 104 func (s *Scanner) SetDataDirs(...string) {} 105 func (s *Scanner) SetPolicyNamespaces(...string) {} 106 func (s *Scanner) SetRegoErrorLimit(_ int) {} 107 108 func (s *Scanner) initRegoScanner(srcFS fs.FS) error { 109 s.Lock() 110 defer s.Unlock() 111 if s.regoScanner != nil { 112 return nil 113 } 114 regoScanner := rego.NewScanner(types.SourceCloud, s.scannerOptions...) 115 regoScanner.SetParentDebugLogger(s.debug) 116 if err := regoScanner.LoadPolicies(s.loadEmbeddedLibraries, s.loadEmbeddedPolicies, srcFS, s.policyDirs, s.policyReaders); err != nil { 117 return err 118 } 119 s.regoScanner = regoScanner 120 return nil 121 } 122 123 func (s *Scanner) ScanFS(ctx context.Context, fs fs.FS, dir string) (scan.Results, error) { 124 p := parser.New(fs, s.parserOptions...) 125 deployments, err := p.ParseFS(ctx, dir) 126 if err != nil { 127 return nil, err 128 } 129 if err := s.initRegoScanner(fs); err != nil { 130 return nil, err 131 } 132 133 return s.scanDeployments(ctx, deployments, fs) 134 } 135 136 func (s *Scanner) scanDeployments(ctx context.Context, deployments []azure.Deployment, f fs.FS) (scan.Results, error) { 137 138 var results scan.Results 139 140 for _, deployment := range deployments { 141 142 result, err := s.scanDeployment(ctx, deployment, f) 143 if err != nil { 144 return nil, err 145 } 146 results = append(results, result...) 147 } 148 149 return results, nil 150 } 151 152 func (s *Scanner) scanDeployment(ctx context.Context, deployment azure.Deployment, fs fs.FS) (scan.Results, error) { 153 var results scan.Results 154 deploymentState := s.adaptDeployment(ctx, deployment) 155 if !s.regoOnly { 156 for _, rule := range rules.GetRegistered(s.frameworks...) { 157 select { 158 case <-ctx.Done(): 159 return nil, ctx.Err() 160 default: 161 } 162 if rule.GetRule().RegoPackage != "" { 163 continue 164 } 165 ruleResults := rule.Evaluate(deploymentState) 166 s.debug.Log("Found %d results for %s", len(ruleResults), rule.GetRule().AVDID) 167 if len(ruleResults) > 0 { 168 results = append(results, ruleResults...) 169 } 170 } 171 } 172 173 regoResults, err := s.regoScanner.ScanInput(ctx, rego.Input{ 174 Path: deployment.Metadata.Range().GetFilename(), 175 FS: fs, 176 Contents: deploymentState.ToRego(), 177 }) 178 if err != nil { 179 return nil, fmt.Errorf("rego scan error: %w", err) 180 } 181 182 return append(results, regoResults...), nil 183 } 184 185 func (s *Scanner) adaptDeployment(ctx context.Context, deployment azure.Deployment) *state.State { 186 return arm.Adapt(ctx, deployment) 187 }