github.com/devseccon/trivy@v0.47.1-0.20231123133102-bd902a0bd996/pkg/detector/ospkg/ubuntu/ubuntu.go (about) 1 package ubuntu 2 3 import ( 4 "strings" 5 "time" 6 7 version "github.com/knqyf263/go-deb-version" 8 "golang.org/x/xerrors" 9 "k8s.io/utils/clock" 10 11 "github.com/aquasecurity/trivy-db/pkg/vulnsrc/ubuntu" 12 osver "github.com/devseccon/trivy/pkg/detector/ospkg/version" 13 ftypes "github.com/devseccon/trivy/pkg/fanal/types" 14 "github.com/devseccon/trivy/pkg/log" 15 "github.com/devseccon/trivy/pkg/scanner/utils" 16 "github.com/devseccon/trivy/pkg/types" 17 ) 18 19 var ( 20 eolDates = map[string]time.Time{ 21 "4.10": time.Date(2006, 4, 30, 23, 59, 59, 0, time.UTC), 22 "5.04": time.Date(2006, 10, 31, 23, 59, 59, 0, time.UTC), 23 "5.10": time.Date(2007, 4, 13, 23, 59, 59, 0, time.UTC), 24 "6.06": time.Date(2011, 6, 1, 23, 59, 59, 0, time.UTC), 25 "6.10": time.Date(2008, 4, 25, 23, 59, 59, 0, time.UTC), 26 "7.04": time.Date(2008, 10, 19, 23, 59, 59, 0, time.UTC), 27 "7.10": time.Date(2009, 4, 18, 23, 59, 59, 0, time.UTC), 28 "8.04": time.Date(2013, 5, 9, 23, 59, 59, 0, time.UTC), 29 "8.10": time.Date(2010, 4, 30, 23, 59, 59, 0, time.UTC), 30 "9.04": time.Date(2010, 10, 23, 23, 59, 59, 0, time.UTC), 31 "9.10": time.Date(2011, 4, 29, 23, 59, 59, 0, time.UTC), 32 "10.04": time.Date(2015, 4, 29, 23, 59, 59, 0, time.UTC), 33 "10.10": time.Date(2012, 4, 10, 23, 59, 59, 0, time.UTC), 34 "11.04": time.Date(2012, 10, 28, 23, 59, 59, 0, time.UTC), 35 "11.10": time.Date(2013, 5, 9, 23, 59, 59, 0, time.UTC), 36 "12.04": time.Date(2019, 4, 26, 23, 59, 59, 0, time.UTC), 37 "12.04-ESM": time.Date(2019, 4, 28, 23, 59, 59, 0, time.UTC), 38 "12.10": time.Date(2014, 5, 16, 23, 59, 59, 0, time.UTC), 39 "13.04": time.Date(2014, 1, 27, 23, 59, 59, 0, time.UTC), 40 "13.10": time.Date(2014, 7, 17, 23, 59, 59, 0, time.UTC), 41 "14.04": time.Date(2022, 4, 25, 23, 59, 59, 0, time.UTC), 42 "14.04-ESM": time.Date(2024, 4, 25, 23, 59, 59, 0, time.UTC), 43 "14.10": time.Date(2015, 7, 23, 23, 59, 59, 0, time.UTC), 44 "15.04": time.Date(2016, 1, 23, 23, 59, 59, 0, time.UTC), 45 "15.10": time.Date(2016, 7, 22, 23, 59, 59, 0, time.UTC), 46 "16.04": time.Date(2021, 4, 21, 23, 59, 59, 0, time.UTC), 47 "16.04-ESM": time.Date(2026, 4, 29, 23, 59, 59, 0, time.UTC), 48 "16.10": time.Date(2017, 7, 20, 23, 59, 59, 0, time.UTC), 49 "17.04": time.Date(2018, 1, 13, 23, 59, 59, 0, time.UTC), 50 "17.10": time.Date(2018, 7, 19, 23, 59, 59, 0, time.UTC), 51 "18.04": time.Date(2023, 5, 31, 23, 59, 59, 0, time.UTC), 52 "18.04-ESM": time.Date(2028, 3, 31, 23, 59, 59, 0, time.UTC), 53 "18.10": time.Date(2019, 7, 18, 23, 59, 59, 0, time.UTC), 54 "19.04": time.Date(2020, 1, 18, 23, 59, 59, 0, time.UTC), 55 "19.10": time.Date(2020, 7, 17, 23, 59, 59, 0, time.UTC), 56 "20.04": time.Date(2025, 4, 23, 23, 59, 59, 0, time.UTC), 57 "20.10": time.Date(2021, 7, 22, 23, 59, 59, 0, time.UTC), 58 "21.04": time.Date(2022, 1, 20, 23, 59, 59, 0, time.UTC), 59 "21.10": time.Date(2022, 7, 14, 23, 59, 59, 0, time.UTC), 60 "22.04": time.Date(2027, 4, 23, 23, 59, 59, 0, time.UTC), 61 "22.10": time.Date(2023, 7, 20, 23, 59, 59, 0, time.UTC), 62 "23.04": time.Date(2024, 1, 20, 23, 59, 59, 0, time.UTC), 63 } 64 ) 65 66 type options struct { 67 clock clock.Clock 68 } 69 70 type option func(*options) 71 72 func WithClock(c clock.Clock) option { 73 return func(opts *options) { 74 opts.clock = c 75 } 76 } 77 78 // Scanner implements the Ubuntu scanner 79 type Scanner struct { 80 vs ubuntu.VulnSrc 81 *options 82 } 83 84 // NewScanner is the factory method for Scanner 85 func NewScanner(opts ...option) *Scanner { 86 o := &options{ 87 clock: clock.RealClock{}, 88 } 89 90 for _, opt := range opts { 91 opt(o) 92 } 93 return &Scanner{ 94 vs: ubuntu.NewVulnSrc(), 95 options: o, 96 } 97 } 98 99 // Detect scans and returns the vulnerabilities 100 func (s *Scanner) Detect(osVer string, _ *ftypes.Repository, pkgs []ftypes.Package) ([]types.DetectedVulnerability, error) { 101 log.Logger.Info("Detecting Ubuntu vulnerabilities...") 102 log.Logger.Debugf("ubuntu: os version: %s", osVer) 103 log.Logger.Debugf("ubuntu: the number of packages: %d", len(pkgs)) 104 105 var vulns []types.DetectedVulnerability 106 for _, pkg := range pkgs { 107 osVer = s.versionFromEolDates(osVer) 108 advisories, err := s.vs.Get(osVer, pkg.SrcName) 109 if err != nil { 110 return nil, xerrors.Errorf("failed to get Ubuntu advisories: %w", err) 111 } 112 113 sourceVersion, err := version.NewVersion(utils.FormatSrcVersion(pkg)) 114 if err != nil { 115 log.Logger.Debugf("failed to parse Ubuntu installed package version: %w", err) 116 continue 117 } 118 119 for _, adv := range advisories { 120 vuln := types.DetectedVulnerability{ 121 VulnerabilityID: adv.VulnerabilityID, 122 PkgID: pkg.ID, 123 PkgName: pkg.Name, 124 InstalledVersion: utils.FormatVersion(pkg), 125 FixedVersion: adv.FixedVersion, 126 PkgRef: pkg.Ref, 127 Layer: pkg.Layer, 128 Custom: adv.Custom, 129 DataSource: adv.DataSource, 130 } 131 132 if adv.FixedVersion == "" { 133 vulns = append(vulns, vuln) 134 continue 135 } 136 137 fixedVersion, err := version.NewVersion(adv.FixedVersion) 138 if err != nil { 139 log.Logger.Debugf("failed to parse Ubuntu package version: %w", err) 140 continue 141 } 142 143 if sourceVersion.LessThan(fixedVersion) { 144 vulns = append(vulns, vuln) 145 } 146 } 147 } 148 return vulns, nil 149 } 150 151 // IsSupportedVersion checks is OSFamily can be scanned using Ubuntu scanner 152 func (s *Scanner) IsSupportedVersion(osFamily ftypes.OSType, osVer string) bool { 153 return osver.Supported(s.clock, eolDates, osFamily, osVer) 154 } 155 156 // versionFromEolDates checks if actual (not ESM) version is not outdated 157 func (s *Scanner) versionFromEolDates(osVer string) string { 158 if _, ok := eolDates[osVer]; ok { 159 return osVer 160 } 161 162 // if base version (not ESM) is still actual 163 // we need to use this version 164 // e.g. Ubuntu doesn't have vulnerabilities for `18.04-ESM`, because `18.04` is not outdated 165 // then we need to get vulnerabilities for `18.04` 166 // if `18.04` is outdated - we need to use `18.04-ESM` (we will return error until we add `18.04-ESM` to eolDates) 167 ver := strings.TrimRight(osVer, "-ESM") 168 if eol, ok := eolDates[ver]; ok && s.clock.Now().Before(eol) { 169 return ver 170 } 171 return osVer 172 }