github.com/devseccon/trivy@v0.47.1-0.20231123133102-bd902a0bd996/pkg/commands/artifact/run.go (about)

     1  package artifact
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  
     8  	"github.com/hashicorp/go-multierror"
     9  	"github.com/spf13/viper"
    10  	"golang.org/x/exp/slices"
    11  	"golang.org/x/xerrors"
    12  
    13  	"github.com/aquasecurity/go-version/pkg/semver"
    14  	"github.com/aquasecurity/trivy-db/pkg/db"
    15  	tcache "github.com/devseccon/trivy/pkg/cache"
    16  	"github.com/devseccon/trivy/pkg/commands/operation"
    17  	"github.com/devseccon/trivy/pkg/fanal/analyzer"
    18  	"github.com/devseccon/trivy/pkg/fanal/artifact"
    19  	"github.com/devseccon/trivy/pkg/fanal/cache"
    20  	ftypes "github.com/devseccon/trivy/pkg/fanal/types"
    21  	"github.com/devseccon/trivy/pkg/flag"
    22  	"github.com/devseccon/trivy/pkg/javadb"
    23  	"github.com/devseccon/trivy/pkg/log"
    24  	"github.com/devseccon/trivy/pkg/misconf"
    25  	"github.com/devseccon/trivy/pkg/module"
    26  	"github.com/devseccon/trivy/pkg/policy"
    27  	pkgReport "github.com/devseccon/trivy/pkg/report"
    28  	"github.com/devseccon/trivy/pkg/result"
    29  	"github.com/devseccon/trivy/pkg/rpc/client"
    30  	"github.com/devseccon/trivy/pkg/scanner"
    31  	"github.com/devseccon/trivy/pkg/types"
    32  	"github.com/devseccon/trivy/pkg/utils/fsutils"
    33  )
    34  
    35  // TargetKind represents what kind of artifact Trivy scans
    36  type TargetKind string
    37  
    38  const (
    39  	TargetContainerImage TargetKind = "image"
    40  	TargetFilesystem     TargetKind = "fs"
    41  	TargetRootfs         TargetKind = "rootfs"
    42  	TargetRepository     TargetKind = "repo"
    43  	TargetImageArchive   TargetKind = "archive"
    44  	TargetSBOM           TargetKind = "sbom"
    45  	TargetVM             TargetKind = "vm"
    46  
    47  	devVersion = "dev"
    48  )
    49  
    50  var (
    51  	defaultPolicyNamespaces = []string{
    52  		"appshield",
    53  		"defsec",
    54  		"builtin",
    55  	}
    56  	SkipScan = errors.New("skip subsequent processes")
    57  )
    58  
    59  // InitializeScanner defines the initialize function signature of scanner
    60  type InitializeScanner func(context.Context, ScannerConfig) (scanner.Scanner, func(), error)
    61  
    62  type ScannerConfig struct {
    63  	// e.g. image name and file path
    64  	Target string
    65  
    66  	// Cache
    67  	ArtifactCache      cache.ArtifactCache
    68  	LocalArtifactCache cache.LocalArtifactCache
    69  
    70  	// Client/Server options
    71  	ServerOption client.ScannerOption
    72  
    73  	// Artifact options
    74  	ArtifactOption artifact.Option
    75  }
    76  
    77  type Runner interface {
    78  	// ScanImage scans an image
    79  	ScanImage(ctx context.Context, opts flag.Options) (types.Report, error)
    80  	// ScanFilesystem scans a filesystem
    81  	ScanFilesystem(ctx context.Context, opts flag.Options) (types.Report, error)
    82  	// ScanRootfs scans rootfs
    83  	ScanRootfs(ctx context.Context, opts flag.Options) (types.Report, error)
    84  	// ScanRepository scans repository
    85  	ScanRepository(ctx context.Context, opts flag.Options) (types.Report, error)
    86  	// ScanSBOM scans SBOM
    87  	ScanSBOM(ctx context.Context, opts flag.Options) (types.Report, error)
    88  	// ScanVM scans VM
    89  	ScanVM(ctx context.Context, opts flag.Options) (types.Report, error)
    90  	// Filter filter a report
    91  	Filter(ctx context.Context, opts flag.Options, report types.Report) (types.Report, error)
    92  	// Report a writes a report
    93  	Report(opts flag.Options, report types.Report) error
    94  	// Close closes runner
    95  	Close(ctx context.Context) error
    96  }
    97  
    98  type runner struct {
    99  	cache  cache.Cache
   100  	dbOpen bool
   101  
   102  	// WASM modules
   103  	module *module.Manager
   104  }
   105  
   106  type runnerOption func(*runner)
   107  
   108  // WithCacheClient takes a custom cache implementation
   109  // It is useful when Trivy is imported as a library.
   110  func WithCacheClient(c cache.Cache) runnerOption {
   111  	return func(r *runner) {
   112  		r.cache = c
   113  	}
   114  }
   115  
   116  // NewRunner initializes Runner that provides scanning functionalities.
   117  // It is possible to return SkipScan and it must be handled by caller.
   118  func NewRunner(ctx context.Context, cliOptions flag.Options, opts ...runnerOption) (Runner, error) {
   119  	r := &runner{}
   120  	for _, opt := range opts {
   121  		opt(r)
   122  	}
   123  
   124  	if err := r.initCache(cliOptions); err != nil {
   125  		return nil, xerrors.Errorf("cache error: %w", err)
   126  	}
   127  
   128  	// Update the vulnerability database if needed.
   129  	if err := r.initDB(ctx, cliOptions); err != nil {
   130  		return nil, xerrors.Errorf("DB error: %w", err)
   131  	}
   132  
   133  	// Initialize WASM modules
   134  	m, err := module.NewManager(ctx, module.Options{
   135  		Dir:            cliOptions.ModuleDir,
   136  		EnabledModules: cliOptions.EnabledModules,
   137  	})
   138  	if err != nil {
   139  		return nil, xerrors.Errorf("WASM module error: %w", err)
   140  	}
   141  	m.Register()
   142  	r.module = m
   143  
   144  	return r, nil
   145  }
   146  
   147  // Close closes everything
   148  func (r *runner) Close(ctx context.Context) error {
   149  	var errs error
   150  	if err := r.cache.Close(); err != nil {
   151  		errs = multierror.Append(errs, err)
   152  	}
   153  
   154  	if r.dbOpen {
   155  		if err := db.Close(); err != nil {
   156  			errs = multierror.Append(errs, err)
   157  		}
   158  	}
   159  
   160  	if err := r.module.Close(ctx); err != nil {
   161  		errs = multierror.Append(errs, err)
   162  	}
   163  	return errs
   164  }
   165  
   166  func (r *runner) ScanImage(ctx context.Context, opts flag.Options) (types.Report, error) {
   167  	// Disable the lock file scanning
   168  	opts.DisabledAnalyzers = analyzer.TypeLockfiles
   169  
   170  	var s InitializeScanner
   171  	switch {
   172  	case opts.Input != "" && opts.ServerAddr == "":
   173  		// Scan image tarball in standalone mode
   174  		s = archiveStandaloneScanner
   175  	case opts.Input != "" && opts.ServerAddr != "":
   176  		// Scan image tarball in client/server mode
   177  		s = archiveRemoteScanner
   178  	case opts.Input == "" && opts.ServerAddr == "":
   179  		// Scan container image in standalone mode
   180  		s = imageStandaloneScanner
   181  	case opts.Input == "" && opts.ServerAddr != "":
   182  		// Scan container image in client/server mode
   183  		s = imageRemoteScanner
   184  	}
   185  
   186  	return r.scanArtifact(ctx, opts, s)
   187  }
   188  
   189  func (r *runner) ScanFilesystem(ctx context.Context, opts flag.Options) (types.Report, error) {
   190  	// Disable scanning of individual package and SBOM files
   191  	opts.DisabledAnalyzers = append(opts.DisabledAnalyzers, analyzer.TypeIndividualPkgs...)
   192  	opts.DisabledAnalyzers = append(opts.DisabledAnalyzers, analyzer.TypeSBOM)
   193  
   194  	return r.scanFS(ctx, opts)
   195  }
   196  
   197  func (r *runner) ScanRootfs(ctx context.Context, opts flag.Options) (types.Report, error) {
   198  	// Disable the lock file scanning
   199  	opts.DisabledAnalyzers = append(opts.DisabledAnalyzers, analyzer.TypeLockfiles...)
   200  
   201  	return r.scanFS(ctx, opts)
   202  }
   203  
   204  func (r *runner) scanFS(ctx context.Context, opts flag.Options) (types.Report, error) {
   205  	var s InitializeScanner
   206  	if opts.ServerAddr == "" {
   207  		// Scan filesystem in standalone mode
   208  		s = filesystemStandaloneScanner
   209  	} else {
   210  		// Scan filesystem in client/server mode
   211  		s = filesystemRemoteScanner
   212  	}
   213  
   214  	return r.scanArtifact(ctx, opts, s)
   215  }
   216  
   217  func (r *runner) ScanRepository(ctx context.Context, opts flag.Options) (types.Report, error) {
   218  	// Do not scan OS packages
   219  	opts.VulnType = []string{types.VulnTypeLibrary}
   220  
   221  	// Disable the OS analyzers, individual package analyzers and SBOM analyzer
   222  	opts.DisabledAnalyzers = append(analyzer.TypeIndividualPkgs, analyzer.TypeOSes...)
   223  	opts.DisabledAnalyzers = append(opts.DisabledAnalyzers, analyzer.TypeSBOM)
   224  
   225  	var s InitializeScanner
   226  	if opts.ServerAddr == "" {
   227  		// Scan repository in standalone mode
   228  		s = repositoryStandaloneScanner
   229  	} else {
   230  		// Scan repository in client/server mode
   231  		s = repositoryRemoteScanner
   232  	}
   233  	return r.scanArtifact(ctx, opts, s)
   234  }
   235  
   236  func (r *runner) ScanSBOM(ctx context.Context, opts flag.Options) (types.Report, error) {
   237  	var s InitializeScanner
   238  	if opts.ServerAddr == "" {
   239  		// Scan cycloneDX in standalone mode
   240  		s = sbomStandaloneScanner
   241  	} else {
   242  		// Scan cycloneDX in client/server mode
   243  		s = sbomRemoteScanner
   244  	}
   245  
   246  	return r.scanArtifact(ctx, opts, s)
   247  }
   248  
   249  func (r *runner) ScanVM(ctx context.Context, opts flag.Options) (types.Report, error) {
   250  	// TODO: Does VM scan disable lock file..?
   251  	opts.DisabledAnalyzers = analyzer.TypeLockfiles
   252  
   253  	var s InitializeScanner
   254  	if opts.ServerAddr == "" {
   255  		// Scan virtual machine in standalone mode
   256  		s = vmStandaloneScanner
   257  	} else {
   258  		// Scan virtual machine in client/server mode
   259  		s = vmRemoteScanner
   260  	}
   261  
   262  	return r.scanArtifact(ctx, opts, s)
   263  }
   264  
   265  func (r *runner) scanArtifact(ctx context.Context, opts flag.Options, initializeScanner InitializeScanner) (types.Report, error) {
   266  	report, err := scan(ctx, opts, initializeScanner, r.cache)
   267  	if err != nil {
   268  		return types.Report{}, xerrors.Errorf("scan error: %w", err)
   269  	}
   270  
   271  	return report, nil
   272  }
   273  
   274  func (r *runner) Filter(ctx context.Context, opts flag.Options, report types.Report) (types.Report, error) {
   275  	// Filter results
   276  	if err := result.Filter(ctx, report, opts.FilterOpts()); err != nil {
   277  		return types.Report{}, xerrors.Errorf("filtering error: %w", err)
   278  	}
   279  	return report, nil
   280  }
   281  
   282  func (r *runner) Report(opts flag.Options, report types.Report) error {
   283  	if err := pkgReport.Write(report, opts); err != nil {
   284  		return xerrors.Errorf("unable to write results: %w", err)
   285  	}
   286  
   287  	return nil
   288  }
   289  
   290  func (r *runner) initDB(ctx context.Context, opts flag.Options) error {
   291  	if err := r.initJavaDB(opts); err != nil {
   292  		return err
   293  	}
   294  
   295  	// When scanning config files or running as client mode, it doesn't need to download the vulnerability database.
   296  	if opts.ServerAddr != "" || !opts.Scanners.Enabled(types.VulnerabilityScanner) {
   297  		return nil
   298  	}
   299  
   300  	// download the database file
   301  	noProgress := opts.Quiet || opts.NoProgress
   302  	if err := operation.DownloadDB(ctx, opts.AppVersion, opts.CacheDir, opts.DBRepository, noProgress, opts.SkipDBUpdate, opts.RegistryOpts()); err != nil {
   303  		return err
   304  	}
   305  
   306  	if opts.DownloadDBOnly {
   307  		return SkipScan
   308  	}
   309  
   310  	if err := db.Init(opts.CacheDir); err != nil {
   311  		return xerrors.Errorf("error in vulnerability DB initialize: %w", err)
   312  	}
   313  	r.dbOpen = true
   314  
   315  	return nil
   316  }
   317  
   318  func (r *runner) initJavaDB(opts flag.Options) error {
   319  	// When running as server mode, it doesn't need to download the Java database.
   320  	if opts.Listen != "" {
   321  		return nil
   322  	}
   323  
   324  	// If vulnerability scanning and SBOM generation are disabled, it doesn't need to download the Java database.
   325  	if !opts.Scanners.Enabled(types.VulnerabilityScanner) &&
   326  		!slices.Contains(types.SupportedSBOMFormats, opts.Format) {
   327  		return nil
   328  	}
   329  
   330  	// Update the Java DB
   331  	noProgress := opts.Quiet || opts.NoProgress
   332  	javadb.Init(opts.CacheDir, opts.JavaDBRepository, opts.SkipJavaDBUpdate, noProgress, opts.RegistryOpts())
   333  	if opts.DownloadJavaDBOnly {
   334  		if err := javadb.Update(); err != nil {
   335  			return xerrors.Errorf("Java DB error: %w", err)
   336  		}
   337  		return SkipScan
   338  	}
   339  
   340  	return nil
   341  }
   342  
   343  func (r *runner) initCache(opts flag.Options) error {
   344  	// Skip initializing cache when custom cache is passed
   345  	if r.cache != nil {
   346  		return nil
   347  	}
   348  
   349  	// client/server mode
   350  	if opts.ServerAddr != "" {
   351  		remoteCache := tcache.NewRemoteCache(opts.ServerAddr, opts.CustomHeaders, opts.Insecure)
   352  		r.cache = tcache.NopCache(remoteCache)
   353  		return nil
   354  	}
   355  
   356  	// standalone mode
   357  	fsutils.SetCacheDir(opts.CacheDir)
   358  	cacheClient, err := operation.NewCache(opts.CacheOptions)
   359  	if err != nil {
   360  		return xerrors.Errorf("unable to initialize the cache: %w", err)
   361  	}
   362  	log.Logger.Debugf("cache dir:  %s", fsutils.CacheDir())
   363  
   364  	if opts.Reset {
   365  		defer cacheClient.Close()
   366  		if err = cacheClient.Reset(); err != nil {
   367  			return xerrors.Errorf("cache reset error: %w", err)
   368  		}
   369  		return SkipScan
   370  	}
   371  
   372  	if opts.ResetPolicyBundle {
   373  		c, err := policy.NewClient(fsutils.CacheDir(), true, opts.MisconfOptions.PolicyBundleRepository)
   374  		if err != nil {
   375  			return xerrors.Errorf("failed to instantiate policy client: %w", err)
   376  		}
   377  		if err := c.Clear(); err != nil {
   378  			return xerrors.Errorf("failed to remove the cache: %w", err)
   379  		}
   380  		return SkipScan
   381  	}
   382  
   383  	if opts.ClearCache {
   384  		defer cacheClient.Close()
   385  		if err = cacheClient.ClearArtifacts(); err != nil {
   386  			return xerrors.Errorf("cache clear error: %w", err)
   387  		}
   388  		return SkipScan
   389  	}
   390  
   391  	r.cache = cacheClient
   392  	return nil
   393  }
   394  
   395  // Run performs artifact scanning
   396  func Run(ctx context.Context, opts flag.Options, targetKind TargetKind) (err error) {
   397  	ctx, cancel := context.WithTimeout(ctx, opts.Timeout)
   398  	defer cancel()
   399  
   400  	defer func() {
   401  		if errors.Is(err, context.DeadlineExceeded) {
   402  			log.Logger.Warn("Increase --timeout value")
   403  		}
   404  	}()
   405  
   406  	if opts.GenerateDefaultConfig {
   407  		log.Logger.Info("Writing the default config to trivy-default.yaml...")
   408  		return viper.SafeWriteConfigAs("trivy-default.yaml")
   409  	}
   410  
   411  	r, err := NewRunner(ctx, opts)
   412  	if err != nil {
   413  		if errors.Is(err, SkipScan) {
   414  			return nil
   415  		}
   416  		return xerrors.Errorf("init error: %w", err)
   417  	}
   418  	defer r.Close(ctx)
   419  
   420  	var report types.Report
   421  	switch targetKind {
   422  	case TargetContainerImage, TargetImageArchive:
   423  		if report, err = r.ScanImage(ctx, opts); err != nil {
   424  			return xerrors.Errorf("image scan error: %w", err)
   425  		}
   426  	case TargetFilesystem:
   427  		if report, err = r.ScanFilesystem(ctx, opts); err != nil {
   428  			return xerrors.Errorf("filesystem scan error: %w", err)
   429  		}
   430  	case TargetRootfs:
   431  		if report, err = r.ScanRootfs(ctx, opts); err != nil {
   432  			return xerrors.Errorf("rootfs scan error: %w", err)
   433  		}
   434  	case TargetRepository:
   435  		if report, err = r.ScanRepository(ctx, opts); err != nil {
   436  			return xerrors.Errorf("repository scan error: %w", err)
   437  		}
   438  	case TargetSBOM:
   439  		if report, err = r.ScanSBOM(ctx, opts); err != nil {
   440  			return xerrors.Errorf("sbom scan error: %w", err)
   441  		}
   442  	case TargetVM:
   443  		if report, err = r.ScanVM(ctx, opts); err != nil {
   444  			return xerrors.Errorf("vm scan error: %w", err)
   445  		}
   446  	}
   447  
   448  	report, err = r.Filter(ctx, opts, report)
   449  	if err != nil {
   450  		return xerrors.Errorf("filter error: %w", err)
   451  	}
   452  
   453  	if err = r.Report(opts, report); err != nil {
   454  		return xerrors.Errorf("report error: %w", err)
   455  	}
   456  
   457  	operation.ExitOnEOL(opts, report.Metadata)
   458  	operation.Exit(opts, report.Results.Failed())
   459  
   460  	return nil
   461  }
   462  
   463  func disabledAnalyzers(opts flag.Options) []analyzer.Type {
   464  	// Specified analyzers to be disabled depending on scanning modes
   465  	// e.g. The 'image' subcommand should disable the lock file scanning.
   466  	analyzers := opts.DisabledAnalyzers
   467  
   468  	// It doesn't analyze apk commands by default.
   469  	if !opts.ScanRemovedPkgs {
   470  		analyzers = append(analyzers, analyzer.TypeApkCommand)
   471  	}
   472  
   473  	// Do not analyze programming language packages when not running in 'library'
   474  	if !slices.Contains(opts.VulnType, types.VulnTypeLibrary) {
   475  		analyzers = append(analyzers, analyzer.TypeLanguages...)
   476  	}
   477  
   478  	// Do not perform secret scanning when it is not specified.
   479  	if !opts.Scanners.Enabled(types.SecretScanner) {
   480  		analyzers = append(analyzers, analyzer.TypeSecret)
   481  	}
   482  
   483  	// Do not perform misconfiguration scanning when it is not specified.
   484  	if !opts.Scanners.AnyEnabled(types.MisconfigScanner, types.RBACScanner) {
   485  		analyzers = append(analyzers, analyzer.TypeConfigFiles...)
   486  	}
   487  
   488  	// Scanning file headers and license files is expensive.
   489  	// It is performed only when '--scanners license' and '--license-full' are specified together.
   490  	if !opts.Scanners.Enabled(types.LicenseScanner) || !opts.LicenseFull {
   491  		analyzers = append(analyzers, analyzer.TypeLicenseFile)
   492  	}
   493  
   494  	// Parsing jar files requires Java-db client
   495  	// But we don't create client if vulnerability analysis is disabled and SBOM format is not used
   496  	// We need to disable jar analyzer to avoid errors
   497  	// TODO disable all languages that don't contain license information for this case
   498  	if !opts.Scanners.Enabled(types.VulnerabilityScanner) && !slices.Contains(types.SupportedSBOMFormats, opts.Format) {
   499  		analyzers = append(analyzers, analyzer.TypeJar)
   500  	}
   501  
   502  	// Do not perform misconfiguration scanning on container image config
   503  	// when it is not specified.
   504  	if !opts.ImageConfigScanners.Enabled(types.MisconfigScanner) {
   505  		analyzers = append(analyzers, analyzer.TypeHistoryDockerfile)
   506  	}
   507  
   508  	if len(opts.SBOMSources) == 0 {
   509  		analyzers = append(analyzers, analyzer.TypeExecutable)
   510  	}
   511  
   512  	return analyzers
   513  }
   514  
   515  func initScannerConfig(opts flag.Options, cacheClient cache.Cache) (ScannerConfig, types.ScanOptions, error) {
   516  	target := opts.Target
   517  	if opts.Input != "" {
   518  		target = opts.Input
   519  	}
   520  
   521  	if opts.Compliance.Spec.ID != "" {
   522  		// set scanners types by spec
   523  		scanners, err := opts.Compliance.Scanners()
   524  		if err != nil {
   525  			return ScannerConfig{}, types.ScanOptions{}, xerrors.Errorf("scanner error: %w", err)
   526  		}
   527  
   528  		opts.Scanners = scanners
   529  		opts.ImageConfigScanners = nil
   530  		// TODO: define image-config-scanners in the spec
   531  		if opts.Compliance.Spec.ID == "docker-cis" {
   532  			opts.Scanners = types.Scanners{types.VulnerabilityScanner}
   533  			opts.ImageConfigScanners = types.Scanners{
   534  				types.MisconfigScanner,
   535  				types.SecretScanner,
   536  			}
   537  		}
   538  	}
   539  
   540  	scanOptions := types.ScanOptions{
   541  		VulnType:            opts.VulnType,
   542  		Scanners:            opts.Scanners,
   543  		ImageConfigScanners: opts.ImageConfigScanners, // this is valid only for 'image' subcommand
   544  		ScanRemovedPackages: opts.ScanRemovedPkgs,     // this is valid only for 'image' subcommand
   545  		ListAllPackages:     opts.ListAllPkgs,
   546  		LicenseCategories:   opts.LicenseCategories,
   547  		FilePatterns:        opts.FilePatterns,
   548  		IncludeDevDeps:      opts.IncludeDevDeps,
   549  	}
   550  
   551  	if len(opts.ImageConfigScanners) != 0 {
   552  		log.Logger.Infof("Container image config scanners: %q", opts.ImageConfigScanners)
   553  	}
   554  
   555  	if opts.Scanners.Enabled(types.VulnerabilityScanner) {
   556  		log.Logger.Info("Vulnerability scanning is enabled")
   557  		log.Logger.Debugf("Vulnerability type:  %s", scanOptions.VulnType)
   558  	}
   559  
   560  	// ScannerOption is filled only when config scanning is enabled.
   561  	var configScannerOptions misconf.ScannerOption
   562  	if opts.Scanners.Enabled(types.MisconfigScanner) || opts.ImageConfigScanners.Enabled(types.MisconfigScanner) {
   563  		log.Logger.Info("Misconfiguration scanning is enabled")
   564  
   565  		var downloadedPolicyPaths []string
   566  		var disableEmbedded bool
   567  		downloadedPolicyPaths, err := operation.InitBuiltinPolicies(context.Background(), opts.CacheDir, opts.Quiet, opts.SkipPolicyUpdate, opts.MisconfOptions.PolicyBundleRepository)
   568  		if err != nil {
   569  			if !opts.SkipPolicyUpdate {
   570  				log.Logger.Errorf("Falling back to embedded policies: %s", err)
   571  			}
   572  		} else {
   573  			log.Logger.Debug("Policies successfully loaded from disk")
   574  			disableEmbedded = true
   575  		}
   576  		configScannerOptions = misconf.ScannerOption{
   577  			Debug:                    opts.Debug,
   578  			Trace:                    opts.Trace,
   579  			Namespaces:               append(opts.PolicyNamespaces, defaultPolicyNamespaces...),
   580  			PolicyPaths:              append(opts.PolicyPaths, downloadedPolicyPaths...),
   581  			DataPaths:                opts.DataPaths,
   582  			HelmValues:               opts.HelmValues,
   583  			HelmValueFiles:           opts.HelmValueFiles,
   584  			HelmFileValues:           opts.HelmFileValues,
   585  			HelmStringValues:         opts.HelmStringValues,
   586  			TerraformTFVars:          opts.TerraformTFVars,
   587  			CloudFormationParamVars:  opts.CloudFormationParamVars,
   588  			K8sVersion:               opts.K8sVersion,
   589  			DisableEmbeddedPolicies:  disableEmbedded,
   590  			DisableEmbeddedLibraries: disableEmbedded,
   591  			TfExcludeDownloaded:      opts.TfExcludeDownloaded,
   592  		}
   593  	}
   594  
   595  	// Do not load config file for secret scanning
   596  	if opts.Scanners.Enabled(types.SecretScanner) {
   597  		ver := canonicalVersion(opts.AppVersion)
   598  		log.Logger.Info("Secret scanning is enabled")
   599  		log.Logger.Info("If your scanning is slow, please try '--scanners vuln' to disable secret scanning")
   600  		log.Logger.Infof("Please see also https://aquasecurity.github.io/trivy/%s/docs/scanner/secret/#recommendation for faster secret detection", ver)
   601  	} else {
   602  		opts.SecretConfigPath = ""
   603  	}
   604  
   605  	if opts.Scanners.Enabled(types.LicenseScanner) {
   606  		if opts.LicenseFull {
   607  			log.Logger.Info("Full license scanning is enabled")
   608  		} else {
   609  			log.Logger.Info("License scanning is enabled")
   610  		}
   611  	}
   612  
   613  	// SPDX needs to calculate digests for package files
   614  	var fileChecksum bool
   615  	if opts.Format == types.FormatSPDXJSON || opts.Format == types.FormatSPDX {
   616  		fileChecksum = true
   617  	}
   618  
   619  	return ScannerConfig{
   620  		Target:             target,
   621  		ArtifactCache:      cacheClient,
   622  		LocalArtifactCache: cacheClient,
   623  		ServerOption: client.ScannerOption{
   624  			RemoteURL:     opts.ServerAddr,
   625  			CustomHeaders: opts.CustomHeaders,
   626  			Insecure:      opts.Insecure,
   627  		},
   628  		ArtifactOption: artifact.Option{
   629  			DisabledAnalyzers: disabledAnalyzers(opts),
   630  			SkipFiles:         opts.SkipFiles,
   631  			SkipDirs:          opts.SkipDirs,
   632  			FilePatterns:      opts.FilePatterns,
   633  			Offline:           opts.OfflineScan,
   634  			NoProgress:        opts.NoProgress || opts.Quiet,
   635  			Insecure:          opts.Insecure,
   636  			RepoBranch:        opts.RepoBranch,
   637  			RepoCommit:        opts.RepoCommit,
   638  			RepoTag:           opts.RepoTag,
   639  			SBOMSources:       opts.SBOMSources,
   640  			RekorURL:          opts.RekorURL,
   641  			//Platform:          opts.Platform,
   642  			Parallel:     opts.Parallel,
   643  			AWSRegion:    opts.Region,
   644  			AWSEndpoint:  opts.Endpoint,
   645  			FileChecksum: fileChecksum,
   646  
   647  			// For image scanning
   648  			ImageOption: ftypes.ImageOptions{
   649  				RegistryOptions: opts.RegistryOpts(),
   650  				DockerOptions: ftypes.DockerOptions{
   651  					Host: opts.DockerHost,
   652  				},
   653  				ImageSources: opts.ImageSources,
   654  			},
   655  
   656  			// For misconfiguration scanning
   657  			MisconfScannerOption: configScannerOptions,
   658  
   659  			// For secret scanning
   660  			SecretScannerOption: analyzer.SecretScannerOption{
   661  				ConfigPath: opts.SecretConfigPath,
   662  			},
   663  
   664  			// For license scanning
   665  			LicenseScannerOption: analyzer.LicenseScannerOption{
   666  				Full:                      opts.LicenseFull,
   667  				ClassifierConfidenceLevel: opts.LicenseConfidenceLevel,
   668  			},
   669  		},
   670  	}, scanOptions, nil
   671  }
   672  
   673  func scan(ctx context.Context, opts flag.Options, initializeScanner InitializeScanner, cacheClient cache.Cache) (
   674  	types.Report, error) {
   675  	scannerConfig, scanOptions, err := initScannerConfig(opts, cacheClient)
   676  	if err != nil {
   677  		return types.Report{}, err
   678  	}
   679  	s, cleanup, err := initializeScanner(ctx, scannerConfig)
   680  	if err != nil {
   681  		return types.Report{}, xerrors.Errorf("unable to initialize a scanner: %w", err)
   682  	}
   683  	defer cleanup()
   684  
   685  	report, err := s.ScanArtifact(ctx, scanOptions)
   686  	if err != nil {
   687  		return types.Report{}, xerrors.Errorf("scan failed: %w", err)
   688  	}
   689  	return report, nil
   690  }
   691  
   692  func canonicalVersion(ver string) string {
   693  	if ver == devVersion {
   694  		return ver
   695  	}
   696  	v, err := semver.Parse(ver)
   697  	if err != nil {
   698  		return devVersion
   699  	}
   700  	// Replace pre-release with "dev"
   701  	// e.g. v0.34.0-beta1+snapshot-1
   702  	if v.IsPreRelease() || v.Metadata() != "" {
   703  		return devVersion
   704  	}
   705  	// Add "v" prefix and cut a patch number, "0.34.0" => "v0.34" for the url
   706  	return fmt.Sprintf("v%d.%d", v.Major(), v.Minor())
   707  }