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

     1  package library
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"golang.org/x/xerrors"
     8  
     9  	"github.com/aquasecurity/trivy-db/pkg/db"
    10  	dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
    11  	"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
    12  	"github.com/devseccon/trivy/pkg/detector/library/compare"
    13  	"github.com/devseccon/trivy/pkg/detector/library/compare/maven"
    14  	"github.com/devseccon/trivy/pkg/detector/library/compare/npm"
    15  	"github.com/devseccon/trivy/pkg/detector/library/compare/pep440"
    16  	"github.com/devseccon/trivy/pkg/detector/library/compare/rubygems"
    17  	ftypes "github.com/devseccon/trivy/pkg/fanal/types"
    18  	"github.com/devseccon/trivy/pkg/log"
    19  	"github.com/devseccon/trivy/pkg/types"
    20  )
    21  
    22  // NewDriver returns a driver according to the library type
    23  func NewDriver(libType ftypes.LangType) (Driver, bool) {
    24  	var ecosystem dbTypes.Ecosystem
    25  	var comparer compare.Comparer
    26  
    27  	switch libType {
    28  	case ftypes.Bundler, ftypes.GemSpec:
    29  		ecosystem = vulnerability.RubyGems
    30  		comparer = rubygems.Comparer{}
    31  	case ftypes.RustBinary, ftypes.Cargo:
    32  		ecosystem = vulnerability.Cargo
    33  		comparer = compare.GenericComparer{}
    34  	case ftypes.Composer:
    35  		ecosystem = vulnerability.Composer
    36  		comparer = compare.GenericComparer{}
    37  	case ftypes.GoBinary, ftypes.GoModule:
    38  		ecosystem = vulnerability.Go
    39  		comparer = compare.GenericComparer{}
    40  	case ftypes.Jar, ftypes.Pom, ftypes.Gradle:
    41  		ecosystem = vulnerability.Maven
    42  		comparer = maven.Comparer{}
    43  	case ftypes.Npm, ftypes.Yarn, ftypes.Pnpm, ftypes.NodePkg, ftypes.JavaScript:
    44  		ecosystem = vulnerability.Npm
    45  		comparer = npm.Comparer{}
    46  	case ftypes.NuGet, ftypes.DotNetCore:
    47  		ecosystem = vulnerability.NuGet
    48  		comparer = compare.GenericComparer{}
    49  	case ftypes.Pipenv, ftypes.Poetry, ftypes.Pip, ftypes.PythonPkg:
    50  		ecosystem = vulnerability.Pip
    51  		comparer = pep440.Comparer{}
    52  	case ftypes.Pub:
    53  		ecosystem = vulnerability.Pub
    54  		comparer = compare.GenericComparer{}
    55  	case ftypes.Hex:
    56  		ecosystem = vulnerability.Erlang
    57  		comparer = compare.GenericComparer{}
    58  	case ftypes.Conan:
    59  		ecosystem = vulnerability.Conan
    60  		// Only semver can be used for version ranges
    61  		// https://docs.conan.io/en/latest/versioning/version_ranges.html
    62  		comparer = compare.GenericComparer{}
    63  	case ftypes.Swift:
    64  		// Swift uses semver
    65  		// https://www.swift.org/package-manager/#importing-dependencies
    66  		ecosystem = vulnerability.Swift
    67  		comparer = compare.GenericComparer{}
    68  	case ftypes.Cocoapods:
    69  		// CocoaPods uses RubyGems version specifiers
    70  		// https://guides.cocoapods.org/making/making-a-cocoapod.html#cocoapods-versioning-specifics
    71  		ecosystem = vulnerability.Cocoapods
    72  		comparer = rubygems.Comparer{}
    73  	case ftypes.CondaPkg:
    74  		log.Logger.Warn("Conda package is supported for SBOM, not for vulnerability scanning")
    75  		return Driver{}, false
    76  	case ftypes.Bitnami:
    77  		ecosystem = vulnerability.Bitnami
    78  		comparer = compare.GenericComparer{}
    79  	case ftypes.K8sUpstream:
    80  		ecosystem = vulnerability.Kubernetes
    81  		comparer = compare.GenericComparer{}
    82  	default:
    83  		log.Logger.Warnf("The %q library type is not supported for vulnerability scanning", libType)
    84  		return Driver{}, false
    85  	}
    86  	return Driver{
    87  		ecosystem: ecosystem,
    88  		comparer:  comparer,
    89  		dbc:       db.Config{},
    90  	}, true
    91  }
    92  
    93  // Driver represents security advisories for each programming language
    94  type Driver struct {
    95  	ecosystem dbTypes.Ecosystem
    96  	comparer  compare.Comparer
    97  	dbc       db.Config
    98  }
    99  
   100  // Type returns the driver ecosystem
   101  func (d *Driver) Type() string {
   102  	return string(d.ecosystem)
   103  }
   104  
   105  // DetectVulnerabilities scans buckets with the prefix according to the ecosystem.
   106  // If "ecosystem" is pip, it looks for buckets with "pip::" and gets security advisories from those buckets.
   107  // It allows us to add a new data source with the ecosystem prefix (e.g. pip::new-data-source)
   108  // and detect vulnerabilities without specifying a specific bucket name.
   109  func (d *Driver) DetectVulnerabilities(pkgID, pkgName, pkgVer string) ([]types.DetectedVulnerability, error) {
   110  	// e.g. "pip::", "npm::"
   111  	prefix := fmt.Sprintf("%s::", d.ecosystem)
   112  	advisories, err := d.dbc.GetAdvisories(prefix, vulnerability.NormalizePkgName(d.ecosystem, pkgName))
   113  	if err != nil {
   114  		return nil, xerrors.Errorf("failed to get %s advisories: %w", d.ecosystem, err)
   115  	}
   116  
   117  	var vulns []types.DetectedVulnerability
   118  	for _, adv := range advisories {
   119  		if !d.comparer.IsVulnerable(pkgVer, adv) {
   120  			continue
   121  		}
   122  
   123  		vuln := types.DetectedVulnerability{
   124  			VulnerabilityID:  adv.VulnerabilityID,
   125  			PkgID:            pkgID,
   126  			PkgName:          pkgName,
   127  			InstalledVersion: pkgVer,
   128  			FixedVersion:     createFixedVersions(adv),
   129  			DataSource:       adv.DataSource,
   130  		}
   131  		vulns = append(vulns, vuln)
   132  	}
   133  
   134  	return vulns, nil
   135  }
   136  
   137  func createFixedVersions(advisory dbTypes.Advisory) string {
   138  	if len(advisory.PatchedVersions) != 0 {
   139  		return strings.Join(advisory.PatchedVersions, ", ")
   140  	}
   141  
   142  	var fixedVersions []string
   143  	for _, version := range advisory.VulnerableVersions {
   144  		for _, s := range strings.Split(version, ",") {
   145  			s = strings.TrimSpace(s)
   146  			if !strings.HasPrefix(s, "<=") && strings.HasPrefix(s, "<") {
   147  				s = strings.TrimPrefix(s, "<")
   148  				fixedVersions = append(fixedVersions, strings.TrimSpace(s))
   149  			}
   150  		}
   151  	}
   152  	return strings.Join(fixedVersions, ", ")
   153  }