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 }