diff --git a/grype/version/rpm_version.go b/grype/version/rpm_version.go index af77467fac6..f75676c9dab 100644 --- a/grype/version/rpm_version.go +++ b/grype/version/rpm_version.go @@ -101,7 +101,27 @@ func (v rpmVersion) compare(v2 rpmVersion) int { return ret } - return compareRpmVersions(v.release, v2.release) + return compareRpmReleases(v.release, v2.release) +} + +// compareRpmReleases normalizes two release strigns before +// calling compareRpmVersions to compare them. +// It normalize the release strings by removing the build number +// from the end: +// "1.module_el8.3.0+757+d382997d" -> "1.module_el8.3.0" +// This is important because the build numbers are not identical between CentOS +// 8 and RHEL 8, even for the same package version, leading to FPs or FNs. +func compareRpmReleases(a, b string) int { + if a == b { + return 0 + } + + a = strings.Replace(a, "module+el", "module_el", 1) + b = strings.Replace(b, "module+el", "module_el", 1) + + trimmedA, _, _ := strings.Cut(a, "+") + trimmedB, _, _ := strings.Cut(b, "+") + return compareRpmVersions(trimmedA, trimmedB) } func epochIsPresent(epoch *int) bool { diff --git a/grype/version/rpm_version_test.go b/grype/version/rpm_version_test.go index 4997b021ce8..cc73acb4253 100644 --- a/grype/version/rpm_version_test.go +++ b/grype/version/rpm_version_test.go @@ -25,12 +25,22 @@ func TestVersionRpm(t *testing.T) { {"1:2", "1", 1}, {"0:4.19.1-1.el7_5", "2:4.19.1-1.el7_5", -1}, {"4:1.2.3-3-el7_5", "1.2.3-el7_5~snapshot1", 1}, - //Non-standard comparisons that ignore epochs due to only one being available + // Non-standard comparisons that ignore epochs due to only one being available {"1:0", "1", -1}, {"2:4.19.01-1.el7_5", "4.19.1-1.el7_5", 0}, {"4.19.01-1.el7_5", "2:4.19.1-1.el7_5", 0}, {"4.19.0-1.el7_5", "12:4.19.0-1.el7", 1}, {"3:4.19.0-1.el7_5", "4.21.0-1.el7", -1}, + // centos and rhel build numbers differ on same version of same package + // ensure these are equal. + {"3:10.3.28-1.module_el8.3.0+757+d382997d", "3:10.3.28-1.module+el8.3.0+10472+7adc332a", 0}, + // some amazonlinux examples + {"2.13.0-2.amzn2023.0.2", "2.13.0-2.amzn2023.0.1", 1}, + {"1.20.14-18.amzn2023.0.1", "1.20.14-18.amzn2023.0.2", -1}, + // examples from oracle linux 8 for python38-tkinter ELSA-2021-9130 + {"3.8.17-2.module+el8.9.0+90017+9913aa0c", "0:3.8.3-3.0.1.module+el8.3.0+el8+9681+09f2c1ca", 1}, + // note that build number 9680 and 9681 are different, but that's not part of the version comparison + {"0:3.8.3-3.0.1.module+el8.3.0+el8+9680+09f2c1ca", "0:3.8.3-3.0.1.module+el8.3.0+el8+9681+09f2c1ca", 0}, } for _, test := range tests { diff --git a/test/quality/vulnerability-match-labels b/test/quality/vulnerability-match-labels index 8915ebaa906..cbe008df99a 160000 --- a/test/quality/vulnerability-match-labels +++ b/test/quality/vulnerability-match-labels @@ -1 +1 @@ -Subproject commit 8915ebaa90638c15512afa1299a5cc4015d6a3b5 +Subproject commit cbe008df99ae4531767eddf1f36dce17f2434c4b