1package redhat 2 3import ( 4 "strings" 5 "time" 6 7 "github.com/aquasecurity/trivy-db/pkg/vulnsrc/redhat" 8 9 version "github.com/knqyf263/go-rpm-version" 10 "golang.org/x/xerrors" 11 12 "github.com/aquasecurity/fanal/analyzer/os" 13 ftypes "github.com/aquasecurity/fanal/types" 14 dbTypes "github.com/aquasecurity/trivy-db/pkg/types" 15 "github.com/aquasecurity/trivy/pkg/log" 16 "github.com/aquasecurity/trivy/pkg/scanner/utils" 17 "github.com/aquasecurity/trivy/pkg/types" 18) 19 20var ( 21 redhatEOLDates = map[string]time.Time{ 22 "4": time.Date(2017, 5, 31, 23, 59, 59, 0, time.UTC), 23 "5": time.Date(2020, 11, 30, 23, 59, 59, 0, time.UTC), 24 "6": time.Date(2024, 6, 30, 23, 59, 59, 0, time.UTC), 25 // N/A 26 "7": time.Date(3000, 1, 1, 23, 59, 59, 0, time.UTC), 27 "8": time.Date(3000, 1, 1, 23, 59, 59, 0, time.UTC), 28 } 29 centosEOLDates = map[string]time.Time{ 30 "3": time.Date(2010, 10, 31, 23, 59, 59, 0, time.UTC), 31 "4": time.Date(2012, 2, 29, 23, 59, 59, 0, time.UTC), 32 "5": time.Date(2017, 3, 31, 23, 59, 59, 0, time.UTC), 33 "6": time.Date(2020, 11, 30, 23, 59, 59, 0, time.UTC), 34 "7": time.Date(2024, 6, 30, 23, 59, 59, 0, time.UTC), 35 // N/A 36 "8": time.Date(3000, 6, 30, 23, 59, 59, 0, time.UTC), 37 } 38) 39 40type Scanner struct { 41 vs dbTypes.VulnSrc 42} 43 44func NewScanner() *Scanner { 45 return &Scanner{ 46 vs: redhat.NewVulnSrc(), 47 } 48} 49 50func (s *Scanner) Detect(osVer string, pkgs []ftypes.Package) ([]types.DetectedVulnerability, error) { 51 log.Logger.Info("Detecting RHEL/CentOS vulnerabilities...") 52 if strings.Count(osVer, ".") > 0 { 53 osVer = osVer[:strings.Index(osVer, ".")] 54 } 55 log.Logger.Debugf("redhat: os version: %s", osVer) 56 log.Logger.Debugf("redhat: the number of packages: %d", len(pkgs)) 57 58 var vulns []types.DetectedVulnerability 59 for _, pkg := range pkgs { 60 // For Red Hat Security Data API containing only source package names 61 advisories, err := s.vs.Get(osVer, pkg.SrcName) 62 if err != nil { 63 return nil, xerrors.Errorf("failed to get Red Hat advisories: %w", err) 64 } 65 66 installed := utils.FormatVersion(pkg) 67 installedVersion := version.NewVersion(installed) 68 69 for _, adv := range advisories { 70 if adv.FixedVersion != "" { 71 continue 72 } 73 vuln := types.DetectedVulnerability{ 74 VulnerabilityID: adv.VulnerabilityID, 75 PkgName: pkg.Name, 76 InstalledVersion: installed, 77 Layer: pkg.Layer, 78 } 79 vulns = append(vulns, vuln) 80 } 81 82 // For Red Hat OVAL containing only binary package names 83 advisories, err = s.vs.Get(osVer, pkg.Name) 84 if err != nil { 85 return nil, xerrors.Errorf("failed to get Red Hat advisories: %w", err) 86 } 87 88 for _, adv := range advisories { 89 fixedVersion := version.NewVersion(adv.FixedVersion) 90 if installedVersion.LessThan(fixedVersion) { 91 vuln := types.DetectedVulnerability{ 92 VulnerabilityID: adv.VulnerabilityID, 93 PkgName: pkg.Name, 94 InstalledVersion: installed, 95 FixedVersion: fixedVersion.String(), 96 Layer: pkg.Layer, 97 } 98 vulns = append(vulns, vuln) 99 } 100 } 101 } 102 return vulns, nil 103} 104 105func (s *Scanner) IsSupportedVersion(osFamily, osVer string) bool { 106 now := time.Now() 107 return s.isSupportedVersion(now, osFamily, osVer) 108} 109 110func (s *Scanner) isSupportedVersion(now time.Time, osFamily, osVer string) bool { 111 if strings.Count(osVer, ".") > 0 { 112 osVer = osVer[:strings.Index(osVer, ".")] 113 } 114 115 var eolDate time.Time 116 var ok bool 117 if osFamily == os.RedHat { 118 eolDate, ok = redhatEOLDates[osVer] 119 } else if osFamily == os.CentOS { 120 eolDate, ok = centosEOLDates[osVer] 121 } 122 if !ok { 123 log.Logger.Warnf("This OS version is not on the EOL list: %s %s", osFamily, osVer) 124 return false 125 } 126 return now.Before(eolDate) 127} 128