@@ -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+
214271describe ( "parseSourceRPM" , ( ) => {
215272 it ( "should correctly parse all valid source RPM strings from source_rpms.csv" , ( ) => {
216273 const csvFilePath = path . join (
0 commit comments