Skip to content

Commit eaeeab6

Browse files
authored
fix: include release component in upstream qualifier (#740)
* fix: include release component in upstream qulifier for rhel10 CN-482 The upstream qualifier in the PURL was only including the version from the source RPM, omitting the release component. This caused false positive vulnerabilities for RHEL 10 packages where the installed version was equal to or greater than the fix version. Example: - Before: upstream=glibc@2.39 (missing release) - After: upstream=glibc@2.39-46.el10_0 (complete version) This caused version comparison failures because in RPM versioning, a version without a release is considered older than the same version with a release: 2.39 < 2.39-43.el10_0 → TRUE (incorrectly triggers vulnerability) * test: verify upstream qualifier includes release * test: update snapshots to include release in upstream qualifier
1 parent ba78a13 commit eaeeab6

13 files changed

Lines changed: 3308 additions & 3248 deletions

File tree

lib/analyzer/package-managers/rpm.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,10 @@ function purl(
4949
const sourcePackage = parseSourceRPM(pkg.sourceRPM);
5050
if (sourcePackage) {
5151
let upstream = sourcePackage.name;
52-
if (sourcePackage.version) {
52+
if (sourcePackage.version && sourcePackage.release) {
53+
// Include full version with release for accurate vulnerability matching
54+
upstream += `@${sourcePackage.version}-${sourcePackage.release}`;
55+
} else if (sourcePackage.version) {
5356
upstream += `@${sourcePackage.version}`;
5457
}
5558
qualifiers.upstream = upstream;

test/lib/analyzer/package-managers/rpm.spec.ts

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ describe("RPM Package Version and Epoch Handling", () => {
188188
expect(purl).toContain("distro=rhel-8.2");
189189
});
190190

191-
it("should include epoch=0 with sourceRPM upstream qualifier", () => {
191+
it("should include epoch=0 with sourceRPM upstream qualifier including release", () => {
192192
const result = mapRpmSqlitePackages(
193193
"test-image",
194194
[
@@ -206,11 +206,68 @@ describe("RPM Package Version and Epoch Handling", () => {
206206

207207
const purl = result.Analysis[0].Purl;
208208
expect(purl).toContain("epoch=0");
209-
expect(purl).toContain("upstream=libxml2%402.9.7"); // @ is URL-encoded as %40
209+
// upstream should include full version with release for accurate vulnerability matching
210+
expect(purl).toContain("upstream=libxml2%402.9.7-14.el8"); // @ is URL-encoded as %40
210211
});
211212
});
212213
});
213214

215+
describe("upstream qualifier includes full version with release", () => {
216+
it("should generate upstream with version-release to prevent false positives", () => {
217+
// This test ensures we don't have the false positive issue where:
218+
// - Package: pam-libs@1.6.1-8.el10
219+
// - Vulnerability fixed in: 0:1.6.1-8.el10
220+
// Without the release in upstream, version comparison fails incorrectly
221+
const result = mapRpmSqlitePackages(
222+
"test-image",
223+
[
224+
{
225+
name: "pam-libs",
226+
version: "1.6.1",
227+
release: "8.el10",
228+
sourceRPM: "pam-1.6.1-8.el10.src.rpm",
229+
size: 1000,
230+
},
231+
],
232+
[],
233+
{ name: "rhel", version: "10.1" },
234+
);
235+
236+
const purl = result.Analysis[0].Purl;
237+
// upstream MUST include the release (8.el10) for accurate vulnerability matching
238+
expect(purl).toContain("upstream=pam%401.6.1-8.el10");
239+
// Verify the full PURL format
240+
expect(purl).toBe(
241+
"pkg:rpm/rhel/pam-libs@1.6.1-8.el10?distro=rhel-10.1&upstream=pam%401.6.1-8.el10",
242+
);
243+
});
244+
245+
it("should generate upstream with version-release for glibc packages", () => {
246+
// Another false positive scenario:
247+
// - Package: glibc@2.39-58.el10_1.2
248+
// - Vulnerability fixed in: 0:2.39-43.el10_0
249+
// Without release, "2.39" is compared against "2.39-43" and matches incorrectly
250+
const result = mapRpmSqlitePackages(
251+
"test-image",
252+
[
253+
{
254+
name: "glibc",
255+
version: "2.39",
256+
release: "58.el10_1.2",
257+
sourceRPM: "glibc-2.39-58.el10_1.2.src.rpm",
258+
size: 5000,
259+
},
260+
],
261+
[],
262+
{ name: "rhel", version: "10.1" },
263+
);
264+
265+
const purl = result.Analysis[0].Purl;
266+
// upstream MUST include the release for proper version comparison
267+
expect(purl).toContain("upstream=glibc%402.39-58.el10_1.2");
268+
});
269+
});
270+
214271
describe("parseSourceRPM", () => {
215272
it("should correctly parse all valid source RPM strings from source_rpms.csv", () => {
216273
const csvFilePath = path.join(

test/system/application-scans/__snapshots__/node.spec.ts.snap

Lines changed: 206 additions & 206 deletions
Large diffs are not rendered by default.

test/system/bugs/__snapshots__/rpm-transitive-dependencies.spec.ts.snap

Lines changed: 161 additions & 161 deletions
Large diffs are not rendered by default.

test/system/flags/__snapshots__/app-vulns.spec.ts.snap

Lines changed: 206 additions & 206 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)