diff --git a/pkg/database/mem-check.go b/pkg/database/mem-check.go index ddb8bdda..9cad4742 100644 --- a/pkg/database/mem-check.go +++ b/pkg/database/mem-check.go @@ -21,7 +21,7 @@ func (db *memDB) addVulnerability(osv OSV, pkgNames []string) { } for _, affected := range osv.Affected { - hash := string(affected.Package.Ecosystem) + "-" + affected.Package.NormalizedName() + hash := string(affected.Package.NormalizedEcosystem()) + "-" + affected.Package.NormalizedName() vulns := db.vulnerabilities[hash] if vulns == nil { diff --git a/pkg/database/osv.go b/pkg/database/osv.go index fbc0917b..520eccaf 100644 --- a/pkg/database/osv.go +++ b/pkg/database/osv.go @@ -31,6 +31,13 @@ type Package struct { Ecosystem Ecosystem `json:"ecosystem"` } +// NormalizedEcosystem returns the ecosystem name without the suffix +func (p Package) NormalizedEcosystem() Ecosystem { + eco, _, _ := strings.Cut(string(p.Ecosystem), ":") + + return Ecosystem(eco) +} + // NormalizedName ensures that the package name is normalized based on ecosystem // in accordance to the OSV specification. // @@ -40,7 +47,7 @@ type Package struct { // // In the future, it's hoped that this can be improved. func (p Package) NormalizedName() string { - if p.Ecosystem != lockfile.PipEcosystem { + if p.NormalizedEcosystem() != lockfile.PipEcosystem { return p.Name } @@ -261,9 +268,22 @@ func (osv *OSV) Link() string { return "" } +func areEcosystemsEqual(a, b internal.Ecosystem) bool { + aEcosystem, aSuffix, _ := strings.Cut(string(a), ":") + bEcosystem, bSuffix, _ := strings.Cut(string(b), ":") + + // only care about the minor version if both ecosystems have one + // otherwise we just assume that they're the same and move on + if aSuffix != "" && bSuffix != "" { + return aEcosystem == bEcosystem && aSuffix == bSuffix + } + + return aEcosystem == bEcosystem +} + func (osv *OSV) IsAffected(pkg internal.PackageDetails) bool { for _, affected := range osv.Affected { - if affected.Package.Ecosystem == pkg.Ecosystem && + if areEcosystemsEqual(affected.Package.Ecosystem, pkg.Ecosystem) && affected.Package.NormalizedName() == pkg.Name { if len(affected.Ranges) == 0 && len(affected.Versions) == 0 { _, _ = fmt.Fprintf( diff --git a/pkg/database/osv_test.go b/pkg/database/osv_test.go index b8b08522..5926f4d2 100644 --- a/pkg/database/osv_test.go +++ b/pkg/database/osv_test.go @@ -205,6 +205,76 @@ func TestOSV_IsAffected_AffectsWithEcosystem_DifferentEcosystem(t *testing.T) { } } +func TestOSV_IsAffected_AffectsWithEcosystem_DifferentEcosystemSuffixes(t *testing.T) { + t.Parallel() + + var osv database.OSV + var isAffected bool + + // suffix is considered when present on both advisory and package + osv = buildOSVWithAffected( + database.Affected{ + Package: database.Package{Ecosystem: "Packagist:https://packages.drupal.org/7", Name: "my-package"}, + Ranges: []database.AffectsRange{ + buildEcosystemAffectsRange(database.RangeEvent{Introduced: "0"}), + }, + }, + ) + + isAffected = osv.IsAffected(internal.PackageDetails{ + Name: "my-package", + Version: "1.0.0", + Ecosystem: "Packagist:https://packages.drupal.org/8", + CompareAs: "Packagist", + }) + + if isAffected { + t.Errorf("Package should not be affected") + } + + // suffix is only considered if present on both advisory and package + osv = buildOSVWithAffected( + database.Affected{ + Package: database.Package{Ecosystem: "Packagist", Name: "my-package"}, + Ranges: []database.AffectsRange{ + buildEcosystemAffectsRange(database.RangeEvent{Introduced: "0"}), + }, + }, + ) + + isAffected = osv.IsAffected(internal.PackageDetails{ + Name: "my-package", + Version: "1.0.0", + Ecosystem: "Packagist:https://packages.drupal.org/8", + CompareAs: "Packagist", + }) + + if !isAffected { + t.Errorf("Package should be affected") + } + + // suffix is only considered if present on both advisory and package + osv = buildOSVWithAffected( + database.Affected{ + Package: database.Package{Ecosystem: "Packagist:https://packages.drupal.org/8", Name: "my-package"}, + Ranges: []database.AffectsRange{ + buildEcosystemAffectsRange(database.RangeEvent{Introduced: "0"}), + }, + }, + ) + + isAffected = osv.IsAffected(internal.PackageDetails{ + Name: "my-package", + Version: "1.0.0", + Ecosystem: "Packagist", + CompareAs: "Packagist", + }) + + if !isAffected { + t.Errorf("Package should be affected") + } +} + func TestOSV_IsAffected_AffectsWithEcosystem_SingleAffected(t *testing.T) { t.Parallel()