github.com/devseccon/trivy@v0.47.1-0.20231123133102-bd902a0bd996/pkg/detector/ospkg/chainguard/chainguard.go (about)

     1  package chainguard
     2  
     3  import (
     4  	version "github.com/knqyf263/go-apk-version"
     5  	"golang.org/x/xerrors"
     6  	"k8s.io/utils/clock"
     7  
     8  	dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
     9  	"github.com/aquasecurity/trivy-db/pkg/vulnsrc/chainguard"
    10  	ftypes "github.com/devseccon/trivy/pkg/fanal/types"
    11  	"github.com/devseccon/trivy/pkg/log"
    12  	"github.com/devseccon/trivy/pkg/scanner/utils"
    13  	"github.com/devseccon/trivy/pkg/types"
    14  )
    15  
    16  type options struct {
    17  	clock clock.Clock
    18  }
    19  
    20  type option func(*options)
    21  
    22  func WithClock(c clock.Clock) option {
    23  	return func(opts *options) {
    24  		opts.clock = c
    25  	}
    26  }
    27  
    28  // Scanner implements the Chainguard scanner
    29  type Scanner struct {
    30  	vs chainguard.VulnSrc
    31  	*options
    32  }
    33  
    34  // NewScanner is the factory method for Scanner
    35  func NewScanner(opts ...option) *Scanner {
    36  	o := &options{
    37  		clock: clock.RealClock{},
    38  	}
    39  
    40  	for _, opt := range opts {
    41  		opt(o)
    42  	}
    43  	return &Scanner{
    44  		vs:      chainguard.NewVulnSrc(),
    45  		options: o,
    46  	}
    47  }
    48  
    49  // Detect vulnerabilities in package using Chainguard scanner
    50  func (s *Scanner) Detect(_ string, _ *ftypes.Repository, pkgs []ftypes.Package) ([]types.DetectedVulnerability, error) {
    51  	log.Logger.Info("Detecting Chainguard vulnerabilities...")
    52  
    53  	log.Logger.Debugf("chainguard: the number of packages: %d", len(pkgs))
    54  
    55  	var vulns []types.DetectedVulnerability
    56  	for _, pkg := range pkgs {
    57  		srcName := pkg.SrcName
    58  		if srcName == "" {
    59  			srcName = pkg.Name
    60  		}
    61  		advisories, err := s.vs.Get("", srcName)
    62  		if err != nil {
    63  			return nil, xerrors.Errorf("failed to get Chainguard advisories: %w", err)
    64  		}
    65  
    66  		installed := utils.FormatVersion(pkg)
    67  		installedVersion, err := version.NewVersion(installed)
    68  		if err != nil {
    69  			log.Logger.Debugf("failed to parse Chainguard installed package version: %s", err)
    70  			continue
    71  		}
    72  
    73  		for _, adv := range advisories {
    74  			if !s.isVulnerable(installedVersion, adv) {
    75  				continue
    76  			}
    77  			vulns = append(vulns, types.DetectedVulnerability{
    78  				VulnerabilityID:  adv.VulnerabilityID,
    79  				PkgID:            pkg.ID,
    80  				PkgName:          pkg.Name,
    81  				InstalledVersion: installed,
    82  				FixedVersion:     adv.FixedVersion,
    83  				Layer:            pkg.Layer,
    84  				PkgRef:           pkg.Ref,
    85  				Custom:           adv.Custom,
    86  				DataSource:       adv.DataSource,
    87  			})
    88  		}
    89  	}
    90  	return vulns, nil
    91  }
    92  
    93  func (s *Scanner) isVulnerable(installedVersion version.Version, adv dbTypes.Advisory) bool {
    94  	// Compare versions for fixed vulnerabilities
    95  	fixedVersion, err := version.NewVersion(adv.FixedVersion)
    96  	if err != nil {
    97  		log.Logger.Debugf("failed to parse Chainguard fixed version: %s", err)
    98  		return false
    99  	}
   100  
   101  	// It means the fixed vulnerability
   102  	return installedVersion.LessThan(fixedVersion)
   103  }
   104  
   105  // IsSupportedVersion checks if the version is supported.
   106  func (s *Scanner) IsSupportedVersion(_ ftypes.OSType, _ string) bool {
   107  	// Chainguard doesn't have versions, so there is no case where a given input yields a
   108  	// result of an unsupported Chainguard version.
   109  
   110  	return true
   111  }