github.com/khulnasoft-lab/tunnel-db@v0.0.0-20231117205118-74e1113bd007/pkg/vulnsrc/vulnerability/vulnerability.go (about) 1 package vulnerability 2 3 import ( 4 "sort" 5 "strings" 6 7 "github.com/khulnasoft-lab/tunnel-db/pkg/db" 8 "github.com/khulnasoft-lab/tunnel-db/pkg/log" 9 "github.com/khulnasoft-lab/tunnel-db/pkg/types" 10 ) 11 12 const ( 13 rejectVulnerability = "** REJECT **" 14 ) 15 16 var ( 17 sources = []types.SourceID{NVD, RedHat, Debian, Ubuntu, Alpine, Amazon, OracleOVAL, SuseCVRF, Photon, 18 ArchLinux, Alma, Rocky, CBLMariner, RubySec, PhpSecurityAdvisories, NodejsSecurityWg, GHSA, GLAD, OSV, K8sVulnDB, 19 } 20 ) 21 22 type Vulnerability struct { 23 dbc db.Operation 24 } 25 26 func New(dbc db.Operation) Vulnerability { 27 return Vulnerability{dbc: dbc} 28 } 29 30 func (v Vulnerability) GetDetails(vulnID string) map[types.SourceID]types.VulnerabilityDetail { 31 details, err := v.dbc.GetVulnerabilityDetail(vulnID) 32 if err != nil { 33 log.Logger.Warnf("Failed to get vulnerability detail: %s", err) 34 return nil 35 } else if len(details) == 0 { 36 return nil 37 } 38 return details 39 } 40 41 func (Vulnerability) IsRejected(details map[types.SourceID]types.VulnerabilityDetail) bool { 42 return getRejectedStatus(details) 43 } 44 45 func (Vulnerability) Normalize(details map[types.SourceID]types.VulnerabilityDetail) types.Vulnerability { 46 return types.Vulnerability{ 47 Title: getTitle(details), 48 Description: getDescription(details), 49 Severity: getSeverity(details).String(), // TODO: We have to keep this key until we deprecate 50 CweIDs: getCweIDs(details), 51 VendorSeverity: getVendorSeverity(details), 52 CVSS: getCVSS(details), 53 References: getReferences(details), 54 PublishedDate: details[NVD].PublishedDate, 55 LastModifiedDate: details[NVD].LastModifiedDate, 56 } 57 } 58 59 func getCVSS(details map[types.SourceID]types.VulnerabilityDetail) types.VendorCVSS { 60 vc := make(types.VendorCVSS) 61 for vendor, detail := range details { 62 if (detail.CvssVector == "" || detail.CvssScore == 0) && (detail.CvssVectorV3 == "" || detail.CvssScoreV3 == 0) { 63 continue 64 } 65 vc[vendor] = types.CVSS{ 66 V2Vector: detail.CvssVector, 67 V3Vector: detail.CvssVectorV3, 68 V2Score: detail.CvssScore, 69 V3Score: detail.CvssScoreV3, 70 } 71 } 72 return vc 73 } 74 75 func getVendorSeverity(details map[types.SourceID]types.VulnerabilityDetail) types.VendorSeverity { 76 vs := make(types.VendorSeverity) 77 for vendor, detail := range details { 78 switch { 79 case detail.SeverityV3 != types.SeverityUnknown: 80 vs[vendor] = detail.SeverityV3 81 case detail.Severity != types.SeverityUnknown: 82 vs[vendor] = detail.Severity 83 case detail.CvssScoreV3 > 0: 84 vs[vendor] = scoreToSeverity(detail.CvssScoreV3) 85 case detail.CvssScore > 0: 86 vs[vendor] = scoreToSeverity(detail.CvssScore) 87 } 88 } 89 return vs 90 } 91 92 func getSeverity(details map[types.SourceID]types.VulnerabilityDetail) types.Severity { 93 for _, source := range sources { 94 switch d, ok := details[source]; { 95 case !ok: 96 continue 97 case d.CvssScoreV3 > 0: 98 return scoreToSeverity(d.CvssScoreV3) 99 case d.CvssScore > 0: 100 return scoreToSeverity(d.CvssScore) 101 case d.SeverityV3 != 0: 102 return d.SeverityV3 103 case d.Severity != 0: 104 return d.Severity 105 } 106 } 107 return types.SeverityUnknown 108 } 109 110 func getTitle(details map[types.SourceID]types.VulnerabilityDetail) string { 111 for _, source := range sources { 112 d, ok := details[source] 113 if !ok { 114 continue 115 } 116 if d.Title != "" { 117 return d.Title 118 } 119 } 120 return "" 121 } 122 123 func getDescription(details map[types.SourceID]types.VulnerabilityDetail) string { 124 for _, source := range sources { 125 d, ok := details[source] 126 if !ok { 127 continue 128 } 129 if d.Description != "" { 130 return d.Description 131 } 132 } 133 return "" 134 } 135 136 func getCweIDs(details map[types.SourceID]types.VulnerabilityDetail) []string { 137 for _, source := range sources { 138 d, ok := details[source] 139 if !ok { 140 continue 141 } 142 if len(d.CweIDs) != 0 { 143 return d.CweIDs 144 } 145 } 146 return nil 147 } 148 149 func getReferences(details map[types.SourceID]types.VulnerabilityDetail) []string { 150 references := map[string]struct{}{} 151 for _, source := range sources { 152 // Amazon contains unrelated references 153 if source == Amazon { 154 continue 155 } 156 d, ok := details[source] 157 if !ok { 158 continue 159 } 160 for _, ref := range d.References { 161 // e.g. "\nhttps://curl.haxx.se/docs/CVE-2019-5481.html\n " 162 ref = strings.TrimSpace(ref) 163 for _, r := range strings.Split(ref, "\n") { 164 references[r] = struct{}{} 165 } 166 } 167 } 168 var refs []string 169 for ref := range references { 170 refs = append(refs, ref) 171 } 172 sort.Slice(refs, func(i, j int) bool { 173 return refs[i] < refs[j] 174 }) 175 return refs 176 } 177 178 func getRejectedStatus(details map[types.SourceID]types.VulnerabilityDetail) bool { 179 for _, source := range sources { 180 d, ok := details[source] 181 if !ok { 182 continue 183 } 184 if strings.Contains(d.Description, rejectVulnerability) { 185 return true 186 } 187 } 188 return false 189 } 190 191 func scoreToSeverity(score float64) types.Severity { 192 switch { 193 case score >= 9.0: 194 return types.SeverityCritical 195 case score >= 7.0: 196 return types.SeverityHigh 197 case score >= 4.0: 198 return types.SeverityMedium 199 case score > 0.0: 200 return types.SeverityLow 201 default: 202 return types.SeverityUnknown 203 } 204 } 205 206 func NormalizePkgName(ecosystem types.Ecosystem, pkgName string) string { 207 if ecosystem == Pip { 208 // from https://www.python.org/dev/peps/pep-0426/#name 209 // All comparisons of distribution names MUST be case insensitive, 210 // and MUST consider hyphens and underscores to be equivalent. 211 pkgName = strings.ToLower(pkgName) 212 pkgName = strings.ReplaceAll(pkgName, "_", "-") 213 } else if ecosystem == Swift { 214 // Swift uses `https://github.com/<author>/<package>.git format 215 // But part Swift advisories doesn't `https://` prefix or `.git` suffix 216 // e.g. https://github.com/github/advisory-database/blob/76f65b0d0fdac39c8b0e834ab03562b5f80d5b27/advisories/github-reviewed/2023/06/GHSA-qvxg-wjxc-r4gg/GHSA-qvxg-wjxc-r4gg.json#L21 217 // https://github.com/github/advisory-database/blob/76f65b0d0fdac39c8b0e834ab03562b5f80d5b27/advisories/github-reviewed/2023/07/GHSA-jq43-q8mx-r7mq/GHSA-jq43-q8mx-r7mq.json#L21 218 // Remove them to fit the same format 219 pkgName = strings.TrimPrefix(pkgName, "https://") 220 pkgName = strings.TrimSuffix(pkgName, ".git") 221 } else if ecosystem != NuGet && ecosystem != Go && ecosystem != Cocoapods { 222 // Nuget and Cocoapods are case-sensitive, Go has case-sensitive packages 223 pkgName = strings.ToLower(pkgName) 224 } 225 return pkgName 226 }