github.com/google/osv-scalibr@v0.4.1/enricher/vulnmatch/osvdev/osvdev_test.go (about) 1 // Copyright 2025 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package osvdev_test 16 17 import ( 18 "context" 19 "fmt" 20 "testing" 21 "time" 22 23 "github.com/google/go-cmp/cmp" 24 "github.com/google/go-cmp/cmp/cmpopts" 25 "github.com/google/osv-scalibr/enricher" 26 "github.com/google/osv-scalibr/enricher/vulnmatch/osvdev" 27 "github.com/google/osv-scalibr/enricher/vulnmatch/osvdev/fakeclient" 28 "github.com/google/osv-scalibr/extractor" 29 "github.com/google/osv-scalibr/inventory" 30 "github.com/google/osv-scalibr/inventory/vex" 31 "github.com/google/osv-scalibr/purl" 32 osvpb "github.com/ossf/osv-schema/bindings/go/osvschema" 33 "google.golang.org/protobuf/testing/protocmp" 34 structpb "google.golang.org/protobuf/types/known/structpb" 35 "google.golang.org/protobuf/types/known/timestamppb" 36 ) 37 38 func TestEnrich(t *testing.T) { 39 cancelledContext, cancel := context.WithCancel(context.Background()) 40 cancel() 41 42 var ( 43 jsPkg = &extractor.Package{Name: "express", Version: "4.17.1", PURLType: purl.TypeNPM} 44 goPkg = &extractor.Package{Name: "github.com/gin-gonic/gin", Version: "1.8.1", PURLType: purl.TypeGolang} 45 fzfPkg = &extractor.Package{Name: "fzf", Version: "0.63.0", PURLType: purl.TypeBrew} 46 pyPkg = &extractor.Package{Name: "requests", Version: "1.63.0", PURLType: purl.TypePyPi} 47 unknownPkg = &extractor.Package{Name: "unknown", PURLType: purl.TypeGolang} 48 49 goPkgWithSignals = &extractor.Package{ 50 Name: "github.com/gin-gonic/gin", 51 Version: "1.8.1", 52 PURLType: purl.TypeGolang, 53 ExploitabilitySignals: []*vex.PackageExploitabilitySignal{ 54 { 55 Plugin: "annotator/example", VulnIdentifiers: []string{"GHSA-2c4m-59x9-fr2g"}, 56 }, 57 }} 58 ) 59 60 var ( 61 goVuln1 = osvpb.Vulnerability{ 62 SchemaVersion: "1.7.0", 63 Id: "GHSA-2c4m-59x9-fr2g", 64 Modified: timestamppb.New(time.Date(2023, 11, 8, 4, 12, 18, 674169000, time.UTC)), 65 Published: timestamppb.New(time.Date(2023, 5, 12, 20, 19, 25, 0, time.UTC)), 66 Aliases: []string{"CVE-2023-29401", "GO-2023-1737"}, 67 Summary: "Gin Web Framework does not properly sanitize filename parameter ...", 68 Details: "The filename parameter of the Context.FileAttachment function is ...", 69 Severity: []*osvpb.Severity{{Type: osvpb.Severity_CVSS_V3, Score: "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N"}}, 70 References: []*osvpb.Reference{ 71 {Type: osvpb.Reference_ADVISORY, Url: "https://nvd.nist.gov/vuln/detail/CVE-2023-29401"}, 72 {Type: osvpb.Reference_WEB, Url: "https://github.com/gin-gonic/gin/issues/3555"}, 73 {Type: osvpb.Reference_WEB, Url: "https://github.com/gin-gonic/gin/pull/3556"}, 74 {Type: osvpb.Reference_PACKAGE, Url: "https://github.com/gin-gonic/gin"}, 75 {Type: osvpb.Reference_WEB, Url: "https://github.com/gin-gonic/gin/releases/tag/v1.9.1"}, 76 {Type: osvpb.Reference_WEB, Url: "https://pkg.go.dev/vuln/GO-2023-1737"}, 77 }, 78 DatabaseSpecific: &structpb.Struct{ 79 Fields: map[string]*structpb.Value{ 80 "cwe_ids": {Kind: &structpb.Value_ListValue{ListValue: &structpb.ListValue{Values: []*structpb.Value{ 81 {Kind: &structpb.Value_StringValue{StringValue: "CWE-494"}}, 82 }}}}, 83 "github_reviewed": {Kind: &structpb.Value_BoolValue{BoolValue: true}}, 84 "github_reviewed_at": {Kind: &structpb.Value_StringValue{StringValue: "2023-05-12T20:19:25Z"}}, 85 "nvd_published_at": {Kind: &structpb.Value_StringValue{StringValue: "2023-06-08T21:15:16Z"}}, 86 "severity": {Kind: &structpb.Value_StringValue{StringValue: "MODERATE"}}, 87 }, 88 }, 89 Affected: []*osvpb.Affected{ 90 { 91 Package: &osvpb.Package{ 92 Ecosystem: goPkg.Ecosystem().String(), 93 Name: goPkg.Name, 94 Purl: "pkg:golang/github.com/gin-gonic/gin", 95 }, 96 Ranges: []*osvpb.Range{ 97 {Type: osvpb.Range_SEMVER, Events: []*osvpb.Event{{Introduced: "1.3.1-0.20190301021747-ccb9e902956d"}, {Fixed: "1.9.1"}}}, 98 }, 99 DatabaseSpecific: &structpb.Struct{ 100 Fields: map[string]*structpb.Value{ 101 "source": {Kind: &structpb.Value_StringValue{StringValue: "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2023/05/GHSA-2c4m-59x9-fr2g/GHSA-2c4m-59x9-fr2g.json"}}, 102 }, 103 }, 104 }, 105 }, 106 } 107 108 goVuln2 = osvpb.Vulnerability{ 109 SchemaVersion: "1.7.0", 110 Id: "GHSA-3vp4-m3rf-835h", 111 Aliases: []string{"CVE-2023-26125"}, 112 Summary: "Improper input validation in github.com/gin-gonic/gin", 113 Details: "Versions of the package github.com/gin-gonic/gin before version ...", 114 Severity: []*osvpb.Severity{{Type: osvpb.Severity_CVSS_V3, Score: "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L"}}, 115 Modified: timestamppb.New(time.Date(2023, 11, 8, 4, 11, 58, 943766000, time.UTC)), 116 Published: timestamppb.New(time.Date(2023, 5, 4, 6, 30, 12, 0, time.UTC)), 117 Affected: []*osvpb.Affected{ 118 { 119 Package: &osvpb.Package{ 120 Ecosystem: goPkg.Ecosystem().String(), 121 Name: goPkg.Name, 122 Purl: "pkg:golang/github.com/gin-gonic/gin", 123 }, 124 Ranges: []*osvpb.Range{ 125 {Type: osvpb.Range_SEMVER, Events: []*osvpb.Event{{Introduced: "0"}, {Fixed: "1.9.0"}}}, 126 }, 127 DatabaseSpecific: &structpb.Struct{ 128 Fields: map[string]*structpb.Value{ 129 "source": {Kind: &structpb.Value_StringValue{StringValue: "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2023/05/GHSA-3vp4-m3rf-835h/GHSA-3vp4-m3rf-835h.json"}}, 130 }, 131 }, 132 }, 133 }, 134 References: []*osvpb.Reference{ 135 {Type: osvpb.Reference_ADVISORY, Url: "https://nvd.nist.gov/vuln/detail/CVE-2023-26125"}, 136 {Type: osvpb.Reference_WEB, Url: "https://github.com/gin-gonic/gin/pull/3500"}, 137 {Type: osvpb.Reference_WEB, Url: "https://github.com/gin-gonic/gin/pull/3503"}, 138 {Type: osvpb.Reference_WEB, Url: "https://github.com/t0rchwo0d/gin/commit/fd9f98e70fb4107ee68c783482d231d35e60507b"}, 139 {Type: osvpb.Reference_PACKAGE, Url: "https://github.com/gin-gonic/gin"}, 140 {Type: osvpb.Reference_WEB, Url: "https://github.com/gin-gonic/gin/releases/tag/v1.9.0"}, 141 {Type: osvpb.Reference_WEB, Url: "https://security.snyk.io/vuln/SNYK-GOLANG-GITHUBCOMGINGONICGIN-3324285"}, 142 }, 143 DatabaseSpecific: &structpb.Struct{ 144 Fields: map[string]*structpb.Value{ 145 "cwe_ids": {Kind: &structpb.Value_ListValue{ListValue: &structpb.ListValue{Values: []*structpb.Value{ 146 {Kind: &structpb.Value_StringValue{StringValue: "CWE-20"}}, 147 {Kind: &structpb.Value_StringValue{StringValue: "CWE-77"}}, 148 }}}}, 149 "github_reviewed": {Kind: &structpb.Value_BoolValue{BoolValue: true}}, 150 "github_reviewed_at": {Kind: &structpb.Value_StringValue{StringValue: "2023-05-05T02:20:00Z"}}, 151 "nvd_published_at": {Kind: &structpb.Value_StringValue{StringValue: "2023-05-04T05:15:09Z"}}, 152 "severity": {Kind: &structpb.Value_StringValue{StringValue: "MODERATE"}}, 153 }, 154 }, 155 } 156 157 goVuln3 = osvpb.Vulnerability{ 158 SchemaVersion: "1.7.0", 159 Id: "GO-2023-1737", 160 Aliases: []string{"CVE-2023-29401", "GHSA-2c4m-59x9-fr2g"}, 161 Summary: "Improper handling of filenames in Content-Disposition HTTP heade...", 162 Details: "The filename parameter of the Context.FileAttachment function is ...", 163 DatabaseSpecific: &structpb.Struct{ 164 Fields: map[string]*structpb.Value{ 165 "review_status": {Kind: &structpb.Value_StringValue{StringValue: "REVIEWED"}}, 166 "url": {Kind: &structpb.Value_StringValue{StringValue: "https://pkg.go.dev/vuln/GO-2023-1737"}}, 167 }, 168 }, 169 Affected: []*osvpb.Affected{ 170 { 171 Package: &osvpb.Package{ 172 Ecosystem: goPkg.Ecosystem().String(), 173 Name: goPkg.Name, 174 Purl: "pkg:golang/github.com/gin-gonic/gin", 175 }, 176 Ranges: []*osvpb.Range{ 177 {Type: osvpb.Range_SEMVER, Events: []*osvpb.Event{{Introduced: "1.3.1-0.20190301021747-ccb9e902956d"}, {Fixed: "1.9.1"}}}, 178 }, 179 DatabaseSpecific: &structpb.Struct{ 180 Fields: map[string]*structpb.Value{ 181 "source": {Kind: &structpb.Value_StringValue{StringValue: "https://vuln.go.dev/ID/GO-2023-1737.json"}}, 182 }, 183 }, 184 EcosystemSpecific: &structpb.Struct{ 185 Fields: map[string]*structpb.Value{ 186 "imports": {Kind: &structpb.Value_ListValue{ListValue: &structpb.ListValue{ 187 Values: []*structpb.Value{ 188 {Kind: &structpb.Value_StructValue{StructValue: &structpb.Struct{ 189 Fields: map[string]*structpb.Value{ 190 "path": {Kind: &structpb.Value_StringValue{StringValue: "github.com/gin-gonic/gin"}}, 191 "symbols": {Kind: &structpb.Value_ListValue{ListValue: &structpb.ListValue{ 192 Values: []*structpb.Value{ 193 {Kind: &structpb.Value_StringValue{StringValue: "Context.FileAttachment"}}, 194 }, 195 }}}, 196 }, 197 }}}, 198 }, 199 }}}, 200 }, 201 }, 202 }, 203 }, 204 Modified: timestamppb.New(time.Date(2024, 5, 20, 16, 3, 47, 0, time.UTC)), 205 Published: timestamppb.New(time.Date(2023, 5, 11, 18, 59, 56, 0, time.UTC)), 206 Credits: []*osvpb.Credit{{Name: "motoyasu-saburi"}}, 207 References: []*osvpb.Reference{ 208 {Type: osvpb.Reference_REPORT, Url: "https://github.com/gin-gonic/gin/issues/3555"}, 209 {Type: osvpb.Reference_FIX, Url: "https://github.com/gin-gonic/gin/pull/3556"}, 210 {Type: osvpb.Reference_WEB, Url: "https://github.com/gin-gonic/gin/releases/tag/v1.9.1"}, 211 }, 212 } 213 214 jsVuln1 = osvpb.Vulnerability{ 215 SchemaVersion: "1.7.0", 216 Id: "GHSA-qw6h-vgh9-j6wx", 217 Modified: timestamppb.New(time.Date(2024, 11, 18, 16, 27, 11, 0, time.UTC)), 218 Published: timestamppb.New(time.Date(2024, 9, 10, 19, 41, 4, 0, time.UTC)), 219 Aliases: []string{"CVE-2024-43796"}, 220 Related: []string{"CGA-7rmh-796c-qmq8", "CGA-8w92-879x-f9wc", "CGA-jq8v-jx6x-3fpc"}, 221 Summary: "express vulnerable to XSS via response.redirect()", 222 Details: "In express <4.20.0, passing untrusted user input ...", 223 Severity: []*osvpb.Severity{ 224 {Type: osvpb.Severity_CVSS_V3, Score: "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:L/A:L"}, 225 {Type: osvpb.Severity_CVSS_V4, Score: "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:N/VI:N/VA:N/SC:L/SI:L/SA:L"}, 226 }, 227 Affected: []*osvpb.Affected{ 228 { 229 Package: &osvpb.Package{Ecosystem: "npm", Name: "express", Purl: "pkg:npm/express"}, 230 Ranges: []*osvpb.Range{ 231 {Type: osvpb.Range_SEMVER, Events: []*osvpb.Event{{Introduced: "0"}, {Fixed: "4.20.0"}}}, 232 }, 233 DatabaseSpecific: &structpb.Struct{ 234 Fields: map[string]*structpb.Value{ 235 "source": {Kind: &structpb.Value_StringValue{StringValue: "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2024/09/GHSA-qw6h-vgh9-j6wx/GHSA-qw6h-vgh9-j6wx.json"}}, 236 }, 237 }, 238 }, 239 { 240 Package: &osvpb.Package{Ecosystem: "npm", Name: "express", Purl: "pkg:npm/express"}, 241 Ranges: []*osvpb.Range{ 242 {Type: osvpb.Range_SEMVER, Events: []*osvpb.Event{{Introduced: "5.0.0-alpha.1"}, {Fixed: "5.0.0"}}}, 243 }, 244 DatabaseSpecific: &structpb.Struct{ 245 Fields: map[string]*structpb.Value{ 246 "source": {Kind: &structpb.Value_StringValue{StringValue: "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2024/09/GHSA-qw6h-vgh9-j6wx/GHSA-qw6h-vgh9-j6wx.json"}}, 247 }, 248 }, 249 }, 250 }, 251 References: []*osvpb.Reference{ 252 {Type: osvpb.Reference_WEB, Url: "https://github.com/expressjs/express/security/advisories/GHSA-qw6h-vgh9-j6wx"}, 253 {Type: osvpb.Reference_ADVISORY, Url: "https://nvd.nist.gov/vuln/detail/CVE-2024-43796"}, 254 {Type: osvpb.Reference_WEB, Url: "https://github.com/expressjs/express/commit/54271f69b511fea198471e6ff3400ab805d6b553"}, 255 {Type: osvpb.Reference_PACKAGE, Url: "https://github.com/expressjs/express"}, 256 }, 257 DatabaseSpecific: &structpb.Struct{ 258 Fields: map[string]*structpb.Value{ 259 "cwe_ids": {Kind: &structpb.Value_ListValue{ListValue: &structpb.ListValue{Values: []*structpb.Value{ 260 {Kind: &structpb.Value_StringValue{StringValue: "CWE-79"}}, 261 }}}}, 262 "github_reviewed": {Kind: &structpb.Value_BoolValue{BoolValue: true}}, 263 "github_reviewed_at": {Kind: &structpb.Value_StringValue{StringValue: "2024-09-10T19:41:04Z"}}, 264 "nvd_published_at": {Kind: &structpb.Value_StringValue{StringValue: "2024-09-10T15:15:17Z"}}, 265 "severity": {Kind: &structpb.Value_StringValue{StringValue: "LOW"}}, 266 }, 267 }, 268 } 269 270 jsVuln1Local = osvpb.Vulnerability{ 271 SchemaVersion: "1.7.0", 272 Id: "GHSA-qw6h-vgh9-j6wx", 273 Modified: timestamppb.New(time.Date(2024, 11, 18, 16, 27, 11, 0, time.UTC)), 274 Published: timestamppb.New(time.Date(2024, 9, 10, 19, 41, 4, 0, time.UTC)), 275 Aliases: []string{"CVE-2024-43796"}, 276 Related: []string{"CGA-7rmh-796c-qmq8", "CGA-8w92-879x-f9wc", "CGA-jq8v-jx6x-3fpc"}, 277 Summary: "express vulnerable to XSS via response.redirect()", 278 Details: "In express <4.20.0, passing untrusted user input ...", 279 Severity: []*osvpb.Severity{ 280 {Type: osvpb.Severity_CVSS_V3, Score: "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:L/A:L"}, 281 }, 282 Affected: []*osvpb.Affected{ 283 { 284 Package: &osvpb.Package{Ecosystem: "npm", Name: "express", Purl: "pkg:npm/express"}, 285 Ranges: []*osvpb.Range{ 286 {Type: osvpb.Range_SEMVER, Events: []*osvpb.Event{{Introduced: "0"}, {Fixed: "4.20.0"}}}, 287 }, 288 DatabaseSpecific: &structpb.Struct{ 289 Fields: map[string]*structpb.Value{ 290 "source": {Kind: &structpb.Value_StringValue{StringValue: "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2024/09/GHSA-qw6h-vgh9-j6wx/GHSA-qw6h-vgh9-j6wx.json"}}, 291 }, 292 }, 293 }, 294 { 295 Package: &osvpb.Package{Ecosystem: "npm", Name: "express", Purl: "pkg:npm/express"}, 296 Ranges: []*osvpb.Range{ 297 {Type: osvpb.Range_SEMVER, Events: []*osvpb.Event{{Introduced: "5.0.0-alpha.1"}, {Fixed: "5.0.0"}}}, 298 }, 299 DatabaseSpecific: &structpb.Struct{ 300 Fields: map[string]*structpb.Value{ 301 "source": {Kind: &structpb.Value_StringValue{StringValue: "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2024/09/GHSA-qw6h-vgh9-j6wx/GHSA-qw6h-vgh9-j6wx.json"}}, 302 }, 303 }, 304 }, 305 }, 306 } 307 308 jsVuln2 = osvpb.Vulnerability{ 309 SchemaVersion: "1.7.0", 310 Id: "GHSA-rv95-896h-c2vc", 311 Modified: timestamppb.New(time.Date(2025, 7, 21, 16, 57, 31, 0, time.UTC)), 312 Published: timestamppb.New(time.Date(2024, 3, 25, 19, 40, 26, 0, time.UTC)), 313 Withdrawn: timestamppb.New(time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC)), 314 Aliases: []string{"CVE-2024-29041"}, 315 Related: []string{"CGA-5389-98xc-vr78", "CGA-qg2p-wmx3-mx9q", "CGA-rjrm-49wc-v48x", "CGA-w26h-h47r-f6rx", "CVE-2024-29041"}, 316 Summary: "Express.js Open Redirect in malformed URLs", 317 Details: "Versions of Express.js prior to 4.19.2 ...", 318 Severity: []*osvpb.Severity{{Type: osvpb.Severity_CVSS_V3, Score: "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N"}}, 319 Affected: []*osvpb.Affected{ 320 { 321 Package: &osvpb.Package{Ecosystem: "npm", Name: "express", Purl: "pkg:npm/express"}, 322 Ranges: []*osvpb.Range{{Type: osvpb.Range_SEMVER, Events: []*osvpb.Event{{Introduced: "0"}, {Fixed: "4.19.2"}}}}, 323 DatabaseSpecific: &structpb.Struct{ 324 Fields: map[string]*structpb.Value{ 325 "source": {Kind: &structpb.Value_StringValue{StringValue: "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2024/03/GHSA-rv95-896h-c2vc/GHSA-rv95-896h-c2vc.json"}}, 326 }, 327 }, 328 }, 329 { 330 Package: &osvpb.Package{Ecosystem: "npm", Name: "express", Purl: "pkg:npm/express"}, 331 Ranges: []*osvpb.Range{{Type: osvpb.Range_SEMVER, Events: []*osvpb.Event{{Introduced: "5.0.0-alpha.1"}, {Fixed: "5.0.0-beta.3"}}}}, 332 DatabaseSpecific: &structpb.Struct{ 333 Fields: map[string]*structpb.Value{ 334 "source": {Kind: &structpb.Value_StringValue{StringValue: "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2024/03/GHSA-rv95-896h-c2vc/GHSA-rv95-896h-c2vc.json"}}, 335 }, 336 }}, 337 }, 338 References: []*osvpb.Reference{ 339 {Type: osvpb.Reference_WEB, Url: "https://github.com/expressjs/express/security/advisories/GHSA-rv95-896h-c2vc"}, 340 {Type: osvpb.Reference_ADVISORY, Url: "https://nvd.nist.gov/vuln/detail/CVE-2024-29041"}, 341 {Type: osvpb.Reference_WEB, Url: "https://github.com/koajs/koa/issues/1800"}, 342 {Type: osvpb.Reference_WEB, Url: "https://github.com/expressjs/express/pull/5539"}, 343 {Type: osvpb.Reference_WEB, Url: "https://github.com/expressjs/express/commit/0867302ddbde0e9463d0564fea5861feb708c2dd"}, 344 {Type: osvpb.Reference_WEB, Url: "https://github.com/expressjs/express/commit/0b746953c4bd8e377123527db11f9cd866e39f94"}, 345 {Type: osvpb.Reference_WEB, Url: "https://expressjs.com/en/4x/api.html#res.location"}, 346 {Type: osvpb.Reference_PACKAGE, Url: "https://github.com/expressjs/express"}, 347 }, 348 DatabaseSpecific: &structpb.Struct{ 349 Fields: map[string]*structpb.Value{ 350 "cwe_ids": {Kind: &structpb.Value_ListValue{ListValue: &structpb.ListValue{Values: []*structpb.Value{ 351 {Kind: &structpb.Value_StringValue{StringValue: "CWE-1286"}}, 352 {Kind: &structpb.Value_StringValue{StringValue: "CWE-601"}}, 353 }}}}, 354 "github_reviewed": {Kind: &structpb.Value_BoolValue{BoolValue: true}}, 355 "github_reviewed_at": {Kind: &structpb.Value_StringValue{StringValue: "2024-03-25T19:40:26Z"}}, 356 "nvd_published_at": {Kind: &structpb.Value_StringValue{StringValue: "2024-03-25T21:15:46Z"}}, 357 "severity": {Kind: &structpb.Value_StringValue{StringValue: "MODERATE"}}, 358 }, 359 }, 360 } 361 362 fzfVulnLocal = osvpb.Vulnerability{ 363 Id: "mockID", 364 Affected: inventory.PackageToAffected(fzfPkg, "3002.1", &osvpb.Severity{ 365 Type: osvpb.Severity_CVSS_V3, 366 Score: "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", 367 }), 368 } 369 370 pyPkgSameVulnAsFzf = osvpb.Vulnerability{ 371 Id: "mockID", 372 Affected: inventory.PackageToAffected(pyPkg, "3.002.1", &osvpb.Severity{ 373 Type: osvpb.Severity_CVSS_V3, 374 Score: "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", 375 }), 376 } 377 ) 378 379 client := fakeclient.New(map[string][]*osvpb.Vulnerability{ 380 fmt.Sprintf("%s:%s:", goPkg.Name, goPkg.Version): {&goVuln1, &goVuln2, &goVuln3}, 381 fmt.Sprintf("%s:%s:", jsPkg.Name, jsPkg.Version): {&jsVuln1, &jsVuln2}, 382 fmt.Sprintf("%s:%s:", pyPkg.Name, pyPkg.Version): {&pyPkgSameVulnAsFzf}, 383 }) 384 385 tests := []struct { 386 name string 387 packageVulns []*inventory.PackageVuln 388 packages []*extractor.Package 389 //nolint:containedctx 390 ctx context.Context 391 wantErr error 392 initialQueryTimeout time.Duration 393 wantPackageVulns []*inventory.PackageVuln 394 }{ 395 { 396 name: "ctx_cancelled", 397 ctx: cancelledContext, 398 packages: []*extractor.Package{jsPkg, goPkg}, 399 wantPackageVulns: []*inventory.PackageVuln{}, 400 wantErr: cmpopts.AnyError, 401 }, 402 { 403 name: "initial_query_timeout", 404 initialQueryTimeout: -1 * time.Second, 405 packages: []*extractor.Package{jsPkg, goPkg}, 406 wantPackageVulns: []*inventory.PackageVuln{}, 407 wantErr: osvdev.ErrInitialQueryTimeout, 408 }, 409 { 410 name: "simple_test", 411 packages: []*extractor.Package{goPkg}, 412 wantPackageVulns: []*inventory.PackageVuln{ 413 {Vulnerability: &goVuln1, Package: goPkg, Plugins: []string{osvdev.Name}}, 414 {Vulnerability: &goVuln2, Package: goPkg, Plugins: []string{osvdev.Name}}, 415 {Vulnerability: &goVuln3, Package: goPkg, Plugins: []string{osvdev.Name}}, 416 }, 417 }, 418 { 419 name: "not_covered_purl_type", 420 packages: []*extractor.Package{fzfPkg}, 421 wantPackageVulns: []*inventory.PackageVuln{}, 422 }, 423 { 424 name: "unknown_package", 425 packages: []*extractor.Package{unknownPkg}, 426 wantPackageVulns: []*inventory.PackageVuln{}, 427 }, 428 { 429 name: "interleaving_covered_not_covered", 430 packages: []*extractor.Package{goPkg, fzfPkg, jsPkg}, 431 wantPackageVulns: []*inventory.PackageVuln{ 432 {Vulnerability: &goVuln1, Package: goPkg, Plugins: []string{osvdev.Name}}, 433 {Vulnerability: &goVuln2, Package: goPkg, Plugins: []string{osvdev.Name}}, 434 {Vulnerability: &goVuln3, Package: goPkg, Plugins: []string{osvdev.Name}}, 435 {Vulnerability: &jsVuln1, Package: jsPkg, Plugins: []string{osvdev.Name}}, 436 {Vulnerability: &jsVuln2, Package: jsPkg, Plugins: []string{osvdev.Name}}, 437 }, 438 }, 439 { 440 name: "not_empty_local_inventory_vulns", 441 packageVulns: []*inventory.PackageVuln{ 442 {Vulnerability: &fzfVulnLocal, Package: fzfPkg, Plugins: []string{"mock/plugin"}}, 443 }, 444 packages: []*extractor.Package{fzfPkg, jsPkg}, 445 wantPackageVulns: []*inventory.PackageVuln{ 446 {Vulnerability: &fzfVulnLocal, Package: fzfPkg, Plugins: []string{"mock/plugin"}}, 447 {Vulnerability: &jsVuln1, Package: jsPkg, Plugins: []string{osvdev.Name}}, 448 {Vulnerability: &jsVuln2, Package: jsPkg, Plugins: []string{osvdev.Name}}, 449 }, 450 }, 451 { 452 name: "one_local_one_remote__same_pkg_same_cve", 453 packageVulns: []*inventory.PackageVuln{ 454 {Vulnerability: &jsVuln1Local, Package: jsPkg, Plugins: []string{"mock/plugin"}}, 455 }, 456 packages: []*extractor.Package{jsPkg}, 457 wantPackageVulns: []*inventory.PackageVuln{ 458 {Vulnerability: &jsVuln1, Package: jsPkg, Plugins: []string{osvdev.Name, "mock/plugin"}}, 459 {Vulnerability: &jsVuln2, Package: jsPkg, Plugins: []string{osvdev.Name}}, 460 }, 461 }, 462 { 463 name: "one_local_one_remote__different_pkg_same_cve", 464 packageVulns: []*inventory.PackageVuln{ 465 {Vulnerability: &fzfVulnLocal, Package: fzfPkg, Plugins: []string{"mock/plugin"}}, 466 }, 467 packages: []*extractor.Package{fzfPkg, pyPkg}, 468 wantPackageVulns: []*inventory.PackageVuln{ 469 {Vulnerability: &fzfVulnLocal, Package: fzfPkg, Plugins: []string{"mock/plugin"}}, 470 {Vulnerability: &pyPkgSameVulnAsFzf, Package: pyPkg, Plugins: []string{osvdev.Name}}, 471 }, 472 }, 473 { 474 name: "exploitability_signals", 475 packages: []*extractor.Package{goPkgWithSignals}, 476 wantPackageVulns: []*inventory.PackageVuln{ 477 { 478 Vulnerability: &goVuln1, 479 Package: goPkgWithSignals, 480 Plugins: []string{osvdev.Name}, 481 ExploitabilitySignals: []*vex.FindingExploitabilitySignal{{Plugin: "annotator/example", Justification: vex.Unspecified}}, 482 }, 483 {Vulnerability: &goVuln2, Package: goPkgWithSignals, Plugins: []string{osvdev.Name}}, 484 {Vulnerability: &goVuln3, Package: goPkgWithSignals, Plugins: []string{osvdev.Name}}, 485 }}, 486 } 487 488 for _, tt := range tests { 489 t.Run(tt.name, func(t *testing.T) { 490 if tt.ctx == nil { 491 tt.ctx = context.Background() 492 } 493 494 e := osvdev.NewWithClient(client, tt.initialQueryTimeout) 495 496 var input *enricher.ScanInput 497 498 if tt.packageVulns == nil { 499 tt.packageVulns = []*inventory.PackageVuln{} 500 } 501 502 inv := &inventory.Inventory{ 503 PackageVulns: tt.packageVulns, 504 Packages: tt.packages, 505 } 506 507 err := e.Enrich(tt.ctx, input, inv) 508 if !cmp.Equal(tt.wantErr, err, cmpopts.EquateErrors()) { 509 t.Fatalf("Enrich(%v) error: %v, want %v", tt.packages, err, tt.wantErr) 510 } 511 512 want := &inventory.Inventory{ 513 PackageVulns: tt.wantPackageVulns, 514 Packages: tt.packages, 515 } 516 517 sortPkgVulns := cmpopts.SortSlices(func(a, b *inventory.PackageVuln) bool { 518 if a.Vulnerability.Id != b.Vulnerability.Id { 519 return a.Vulnerability.Id < b.Vulnerability.Id 520 } 521 return a.Package.Name < b.Package.Name 522 }) 523 524 diff := cmp.Diff( 525 want, inv, 526 sortPkgVulns, 527 protocmp.Transform(), 528 cmpopts.SortSlices(func(a, b string) bool { return a < b }), 529 ) 530 531 if diff != "" { 532 t.Errorf("Enrich(%v): unexpected diff (-want +got): %v", tt.packages, diff) 533 } 534 }) 535 } 536 }