Skip to content

Commit 31d64a0

Browse files
authored
fix: account for ecosystem suffix when determining affected advisories (#359)
1 parent cdebe40 commit 31d64a0

File tree

3 files changed

+93
-3
lines changed

3 files changed

+93
-3
lines changed

pkg/database/mem-check.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ func (db *memDB) addVulnerability(osv OSV, pkgNames []string) {
2121
}
2222

2323
for _, affected := range osv.Affected {
24-
hash := string(affected.Package.Ecosystem) + "-" + affected.Package.NormalizedName()
24+
hash := string(affected.Package.NormalizedEcosystem()) + "-" + affected.Package.NormalizedName()
2525
vulns := db.vulnerabilities[hash]
2626

2727
if vulns == nil {

pkg/database/osv.go

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,13 @@ type Package struct {
3131
Ecosystem Ecosystem `json:"ecosystem"`
3232
}
3333

34+
// NormalizedEcosystem returns the ecosystem name without the suffix
35+
func (p Package) NormalizedEcosystem() Ecosystem {
36+
eco, _, _ := strings.Cut(string(p.Ecosystem), ":")
37+
38+
return Ecosystem(eco)
39+
}
40+
3441
// NormalizedName ensures that the package name is normalized based on ecosystem
3542
// in accordance to the OSV specification.
3643
//
@@ -40,7 +47,7 @@ type Package struct {
4047
//
4148
// In the future, it's hoped that this can be improved.
4249
func (p Package) NormalizedName() string {
43-
if p.Ecosystem != lockfile.PipEcosystem {
50+
if p.NormalizedEcosystem() != lockfile.PipEcosystem {
4451
return p.Name
4552
}
4653

@@ -261,9 +268,22 @@ func (osv *OSV) Link() string {
261268
return ""
262269
}
263270

271+
func areEcosystemsEqual(a, b internal.Ecosystem) bool {
272+
aEcosystem, aSuffix, _ := strings.Cut(string(a), ":")
273+
bEcosystem, bSuffix, _ := strings.Cut(string(b), ":")
274+
275+
// only care about the minor version if both ecosystems have one
276+
// otherwise we just assume that they're the same and move on
277+
if aSuffix != "" && bSuffix != "" {
278+
return aEcosystem == bEcosystem && aSuffix == bSuffix
279+
}
280+
281+
return aEcosystem == bEcosystem
282+
}
283+
264284
func (osv *OSV) IsAffected(pkg internal.PackageDetails) bool {
265285
for _, affected := range osv.Affected {
266-
if affected.Package.Ecosystem == pkg.Ecosystem &&
286+
if areEcosystemsEqual(affected.Package.Ecosystem, pkg.Ecosystem) &&
267287
affected.Package.NormalizedName() == pkg.Name {
268288
if len(affected.Ranges) == 0 && len(affected.Versions) == 0 {
269289
_, _ = fmt.Fprintf(

pkg/database/osv_test.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,76 @@ func TestOSV_IsAffected_AffectsWithEcosystem_DifferentEcosystem(t *testing.T) {
205205
}
206206
}
207207

208+
func TestOSV_IsAffected_AffectsWithEcosystem_DifferentEcosystemSuffixes(t *testing.T) {
209+
t.Parallel()
210+
211+
var osv database.OSV
212+
var isAffected bool
213+
214+
// suffix is considered when present on both advisory and package
215+
osv = buildOSVWithAffected(
216+
database.Affected{
217+
Package: database.Package{Ecosystem: "Packagist:https://packages.drupal.org/7", Name: "my-package"},
218+
Ranges: []database.AffectsRange{
219+
buildEcosystemAffectsRange(database.RangeEvent{Introduced: "0"}),
220+
},
221+
},
222+
)
223+
224+
isAffected = osv.IsAffected(internal.PackageDetails{
225+
Name: "my-package",
226+
Version: "1.0.0",
227+
Ecosystem: "Packagist:https://packages.drupal.org/8",
228+
CompareAs: "Packagist",
229+
})
230+
231+
if isAffected {
232+
t.Errorf("Package should not be affected")
233+
}
234+
235+
// suffix is only considered if present on both advisory and package
236+
osv = buildOSVWithAffected(
237+
database.Affected{
238+
Package: database.Package{Ecosystem: "Packagist", Name: "my-package"},
239+
Ranges: []database.AffectsRange{
240+
buildEcosystemAffectsRange(database.RangeEvent{Introduced: "0"}),
241+
},
242+
},
243+
)
244+
245+
isAffected = osv.IsAffected(internal.PackageDetails{
246+
Name: "my-package",
247+
Version: "1.0.0",
248+
Ecosystem: "Packagist:https://packages.drupal.org/8",
249+
CompareAs: "Packagist",
250+
})
251+
252+
if !isAffected {
253+
t.Errorf("Package should be affected")
254+
}
255+
256+
// suffix is only considered if present on both advisory and package
257+
osv = buildOSVWithAffected(
258+
database.Affected{
259+
Package: database.Package{Ecosystem: "Packagist:https://packages.drupal.org/8", Name: "my-package"},
260+
Ranges: []database.AffectsRange{
261+
buildEcosystemAffectsRange(database.RangeEvent{Introduced: "0"}),
262+
},
263+
},
264+
)
265+
266+
isAffected = osv.IsAffected(internal.PackageDetails{
267+
Name: "my-package",
268+
Version: "1.0.0",
269+
Ecosystem: "Packagist",
270+
CompareAs: "Packagist",
271+
})
272+
273+
if !isAffected {
274+
t.Errorf("Package should be affected")
275+
}
276+
}
277+
208278
func TestOSV_IsAffected_AffectsWithEcosystem_SingleAffected(t *testing.T) {
209279
t.Parallel()
210280

0 commit comments

Comments
 (0)