github.com/quay/claircore@v1.5.28/rhel/rhcc/rhcc.go (about) 1 // Package rhcc implements an ecosystem for the Red Hat Container Catalog. 2 // 3 // This ecosystem treats an entire container as a package and matches advisories 4 // against it. 5 package rhcc 6 7 import ( 8 "encoding/xml" 9 "strings" 10 "time" 11 12 "github.com/quay/claircore" 13 "github.com/quay/claircore/pkg/rhctag" 14 "github.com/quay/claircore/toolkit/types/cpe" 15 ) 16 17 var goldRepo = claircore.Repository{ 18 Name: "Red Hat Container Catalog", 19 URI: `https://catalog.redhat.com/software/containers/explore`, 20 } 21 22 type cveMap struct { 23 XMLName xml.Name `xml:"cvemap"` 24 RedHatVulnerabilities []redHatVulnerability `xml:"Vulnerability"` 25 } 26 27 type redHatVulnerability struct { 28 XMLName xml.Name `xml:"Vulnerability"` 29 Name string `xml:"name,attr"` 30 ThreatSeverity string `xml:"ThreatSeverity"` 31 AffectedReleases []affectedRelease `xml:"AffectedRelease"` 32 Details []details `xml:"Details"` 33 } 34 35 type affectedRelease struct { 36 XMLName xml.Name `xml:"AffectedRelease"` 37 Cpe string `xml:"cpe,attr"` 38 ReleaseDate customTime `xml:"ReleaseDate"` 39 Package string `xml:"Package"` 40 Impact string `xml:"impact,attr"` 41 Advisory advisory `xml:"Advisory"` 42 } 43 44 type advisory struct { 45 XMLName xml.Name `xml:"Advisory"` 46 Text string `xml:",cdata"` 47 URL string `xml:"url,attr"` 48 } 49 50 type details struct { 51 XMLName xml.Name `xml:"Details"` 52 Text string `xml:",cdata"` 53 Source string `xml:"source,attr"` 54 } 55 56 type customTime struct { 57 time time.Time 58 } 59 60 type consolidatedRelease struct { 61 Issued time.Time 62 FixedInVersions *rhctag.Versions 63 Severity string 64 AdvisoryLink string 65 AdvisoryName string 66 Cpe cpe.WFN 67 } 68 69 func (c *customTime) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { 70 const shortForm = "2006-01-02" 71 var v string 72 d.DecodeElement(&v, &start) 73 date := strings.Split(v, "T") 74 parse, err := time.Parse(shortForm, date[0]) 75 if err != nil { 76 return err 77 } 78 *c = customTime{parse} 79 return nil 80 } 81 82 func parseContainerPackage(p string) (bool, string, string) { 83 parts := strings.Split(p, ":") 84 if len(parts) != 2 { 85 return false, "", "" 86 } 87 if !strings.ContainsAny(parts[0], "/") { 88 return false, "", "" 89 } 90 return true, parts[0], parts[1] 91 } 92 93 // Prefer Red Hat descriptions over Mitre ones 94 func getDescription(ds []details) string { 95 rhDetailsIdx := -1 96 mitreDetailsIdx := -1 97 result := "" 98 for idx, d := range ds { 99 if d.Source == "Red Hat" { 100 rhDetailsIdx = idx 101 break 102 } else if d.Source == "Mitre" { 103 mitreDetailsIdx = idx 104 } 105 } 106 if rhDetailsIdx != -1 { 107 result = ds[rhDetailsIdx].Text 108 return strings.TrimSpace(result) 109 } 110 if mitreDetailsIdx != -1 { 111 result = ds[mitreDetailsIdx].Text 112 return strings.TrimSpace(result) 113 } 114 return strings.TrimSpace(result) 115 }