diff --git a/grype/matcher/apk/matcher.go b/grype/matcher/apk/matcher.go index 2fb8bc98b8a4..2db14cfaddf2 100644 --- a/grype/matcher/apk/matcher.go +++ b/grype/matcher/apk/matcher.go @@ -2,6 +2,7 @@ package apk import ( "fmt" + "strings" "github.com/anchore/grype/grype/distro" "github.com/anchore/grype/grype/match" @@ -45,7 +46,8 @@ func (m *Matcher) Match(store vulnerability.Provider, d *distro.Distro, p pkg.Pa func (m *Matcher) cpeMatchesWithoutSecDBFixes(store vulnerability.Provider, d *distro.Distro, p pkg.Package) ([]match.Match, error) { // find CPE-indexed vulnerability matches specific to the given package name and version - cpeMatches, err := search.ByPackageCPE(store, p, m.Type()) + cpePackage := cpeComparablePackage(p) + cpeMatches, err := search.ByPackageCPE(store, cpePackage, m.Type()) if err != nil { return nil, err } @@ -101,6 +103,36 @@ cveLoop: return finalCpeMatches, nil } +func cpeComparablePackage(p pkg.Package) pkg.Package { + // clean the alpine package version so that it compares correctly with the CPE version comparison logic + // alpine versions are suffixed with -r{buildindex}; however, if left intact CPE comparison logic will + // incorrectly treat these as a pre-release. In actuality, we just want to treat 1.2.3-r21 as equivalent to + // 1.2.3 for purposes of CPE-based matching since the alpine fix should filter out any cases where a later + // build fixes something that was vulnerable in 1.2.3 + components := strings.Split(p.Version, "-r") + cpeComparableVersion := p.Version + + if len(components) == 2 { + cpeComparableVersion = components[0] + } + + if cpeComparableVersion == p.Version { + return p + } + + t := p + t.Version = cpeComparableVersion + t.CPEs = nil + t.CPEs = append(t.CPEs, p.CPEs...) + + for index, cpe := range t.CPEs { + cpe.Version = cpeComparableVersion + t.CPEs[index] = cpe + } + + return t +} + func deduplicateMatches(secDBMatches, cpeMatches []match.Match) (matches []match.Match) { // add additional unique matches from CPE source that is unique from the SecDB matches secDBMatchesByID := matchesByID(secDBMatches)