github.com/devseccon/trivy@v0.47.1-0.20231123133102-bd902a0bd996/pkg/detector/ospkg/alpine/alpine.go (about) 1 package alpine 2 3 import ( 4 "strings" 5 "time" 6 7 version "github.com/knqyf263/go-apk-version" 8 "golang.org/x/xerrors" 9 "k8s.io/utils/clock" 10 11 dbTypes "github.com/aquasecurity/trivy-db/pkg/types" 12 "github.com/aquasecurity/trivy-db/pkg/vulnsrc/alpine" 13 osver "github.com/devseccon/trivy/pkg/detector/ospkg/version" 14 ftypes "github.com/devseccon/trivy/pkg/fanal/types" 15 "github.com/devseccon/trivy/pkg/log" 16 "github.com/devseccon/trivy/pkg/scanner/utils" 17 "github.com/devseccon/trivy/pkg/types" 18 ) 19 20 var ( 21 eolDates = map[string]time.Time{ 22 "2.0": time.Date(2012, 4, 1, 23, 59, 59, 0, time.UTC), 23 "2.1": time.Date(2012, 11, 1, 23, 59, 59, 0, time.UTC), 24 "2.2": time.Date(2013, 5, 1, 23, 59, 59, 0, time.UTC), 25 "2.3": time.Date(2013, 11, 1, 23, 59, 59, 0, time.UTC), 26 "2.4": time.Date(2014, 5, 1, 23, 59, 59, 0, time.UTC), 27 "2.5": time.Date(2014, 11, 1, 23, 59, 59, 0, time.UTC), 28 "2.6": time.Date(2015, 5, 1, 23, 59, 59, 0, time.UTC), 29 "2.7": time.Date(2015, 11, 1, 23, 59, 59, 0, time.UTC), 30 "3.0": time.Date(2016, 5, 1, 23, 59, 59, 0, time.UTC), 31 "3.1": time.Date(2016, 11, 1, 23, 59, 59, 0, time.UTC), 32 "3.2": time.Date(2017, 5, 1, 23, 59, 59, 0, time.UTC), 33 "3.3": time.Date(2017, 11, 1, 23, 59, 59, 0, time.UTC), 34 "3.4": time.Date(2018, 5, 1, 23, 59, 59, 0, time.UTC), 35 "3.5": time.Date(2018, 11, 1, 23, 59, 59, 0, time.UTC), 36 "3.6": time.Date(2019, 5, 1, 23, 59, 59, 0, time.UTC), 37 "3.7": time.Date(2019, 11, 1, 23, 59, 59, 0, time.UTC), 38 "3.8": time.Date(2020, 5, 1, 23, 59, 59, 0, time.UTC), 39 "3.9": time.Date(2020, 11, 1, 23, 59, 59, 0, time.UTC), 40 "3.10": time.Date(2021, 5, 1, 23, 59, 59, 0, time.UTC), 41 "3.11": time.Date(2021, 11, 1, 23, 59, 59, 0, time.UTC), 42 "3.12": time.Date(2022, 5, 1, 23, 59, 59, 0, time.UTC), 43 "3.13": time.Date(2022, 11, 1, 23, 59, 59, 0, time.UTC), 44 "3.14": time.Date(2023, 5, 1, 23, 59, 59, 0, time.UTC), 45 "3.15": time.Date(2023, 11, 1, 23, 59, 59, 0, time.UTC), 46 "3.16": time.Date(2024, 5, 23, 23, 59, 59, 0, time.UTC), 47 "3.17": time.Date(2024, 11, 22, 23, 59, 59, 0, time.UTC), 48 "3.18": time.Date(2025, 5, 9, 23, 59, 59, 0, time.UTC), 49 "edge": time.Date(9999, 1, 1, 0, 0, 0, 0, time.UTC), 50 } 51 ) 52 53 type options struct { 54 clock clock.Clock 55 } 56 57 type option func(*options) 58 59 func WithClock(c clock.Clock) option { 60 return func(opts *options) { 61 opts.clock = c 62 } 63 } 64 65 // Scanner implements the Alpine scanner 66 type Scanner struct { 67 vs alpine.VulnSrc 68 *options 69 } 70 71 // NewScanner is the factory method for Scanner 72 func NewScanner(opts ...option) *Scanner { 73 o := &options{ 74 clock: clock.RealClock{}, 75 } 76 77 for _, opt := range opts { 78 opt(o) 79 } 80 return &Scanner{ 81 vs: alpine.NewVulnSrc(), 82 options: o, 83 } 84 } 85 86 // Detect vulnerabilities in package using Alpine scanner 87 func (s *Scanner) Detect(osVer string, repo *ftypes.Repository, pkgs []ftypes.Package) ([]types.DetectedVulnerability, error) { 88 log.Logger.Info("Detecting Alpine vulnerabilities...") 89 osVer = osver.Minor(osVer) 90 repoRelease := s.repoRelease(repo) 91 92 log.Logger.Debugf("alpine: os version: %s", osVer) 93 log.Logger.Debugf("alpine: package repository: %s", repoRelease) 94 log.Logger.Debugf("alpine: the number of packages: %d", len(pkgs)) 95 96 stream := osVer 97 if repoRelease != "" && osVer != repoRelease { 98 // Prefer the repository release. Use OS version only when the repository is not detected. 99 stream = repoRelease 100 if repoRelease != "edge" { // TODO: we should detect the current edge version. 101 log.Logger.Warnf("Mixing Alpine versions is unsupported, OS: '%s', repository: '%s'", osVer, repoRelease) 102 } 103 } 104 105 var vulns []types.DetectedVulnerability 106 for _, pkg := range pkgs { 107 srcName := pkg.SrcName 108 if srcName == "" { 109 srcName = pkg.Name 110 } 111 advisories, err := s.vs.Get(stream, srcName) 112 if err != nil { 113 return nil, xerrors.Errorf("failed to get alpine advisories: %w", err) 114 } 115 116 sourceVersion, err := version.NewVersion(utils.FormatSrcVersion(pkg)) 117 if err != nil { 118 log.Logger.Debugf("failed to parse Alpine Linux installed package version: %s", err) 119 continue 120 } 121 122 for _, adv := range advisories { 123 if !s.isVulnerable(sourceVersion, adv) { 124 continue 125 } 126 vulns = append(vulns, types.DetectedVulnerability{ 127 VulnerabilityID: adv.VulnerabilityID, 128 PkgID: pkg.ID, 129 PkgName: pkg.Name, 130 InstalledVersion: utils.FormatVersion(pkg), 131 FixedVersion: adv.FixedVersion, 132 Layer: pkg.Layer, 133 PkgRef: pkg.Ref, 134 Custom: adv.Custom, 135 DataSource: adv.DataSource, 136 }) 137 } 138 } 139 return vulns, nil 140 } 141 142 func (s *Scanner) isVulnerable(installedVersion version.Version, adv dbTypes.Advisory) bool { 143 // This logic is for unfixed vulnerabilities, but Trivy DB doesn't have advisories for unfixed vulnerabilities for now 144 // because Alpine just provides potentially vulnerable packages. It will cause a lot of false positives. 145 // This is for DevSecCon commercial products. 146 if adv.AffectedVersion != "" { 147 // AffectedVersion means which version introduced this vulnerability. 148 affectedVersion, err := version.NewVersion(adv.AffectedVersion) 149 if err != nil { 150 log.Logger.Debugf("failed to parse Alpine Linux affected package version: %s", err) 151 return false 152 } 153 if affectedVersion.GreaterThan(installedVersion) { 154 return false 155 } 156 } 157 158 // This logic is also for unfixed vulnerabilities. 159 if adv.FixedVersion == "" { 160 // It means the unfixed vulnerability 161 return true 162 } 163 164 // Compare versions for fixed vulnerabilities 165 fixedVersion, err := version.NewVersion(adv.FixedVersion) 166 if err != nil { 167 log.Logger.Debugf("failed to parse Alpine Linux fixed version: %s", err) 168 return false 169 } 170 171 // It means the fixed vulnerability 172 return installedVersion.LessThan(fixedVersion) 173 } 174 175 // IsSupportedVersion checks if the version is supported. 176 func (s *Scanner) IsSupportedVersion(osFamily ftypes.OSType, osVer string) bool { 177 return osver.Supported(s.clock, eolDates, osFamily, osver.Minor(osVer)) 178 } 179 180 func (s *Scanner) repoRelease(repo *ftypes.Repository) string { 181 if repo == nil { 182 return "" 183 } 184 release := repo.Release 185 if strings.Count(release, ".") > 1 { 186 release = release[:strings.LastIndex(release, ".")] 187 } 188 return release 189 }