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 }