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  }