@@ -96,6 +96,10 @@ def default(self, obj):
9696 vulnerableVersionRange
9797 advisory {
9898 ghsaId
99+ identifiers {
100+ type
101+ value
102+ }
99103 permalink
100104 withdrawnAt
101105 }
@@ -105,6 +109,38 @@ def default(self, obj):
105109"""
106110)
107111
112+ def preferred_advisory_id (advisory : dict ) -> str :
113+ for identifier in advisory .get ("identifiers" ) or []:
114+ if identifier .get ("type" ) == "CVE" and identifier .get ("value" ):
115+ return identifier ["value" ]
116+ return advisory ["ghsaId" ]
117+
118+
119+ def advisory_aliases (advisory : dict , preferred_id : str ) -> list [str ]:
120+ aliases : list [str ] = []
121+ for identifier in advisory .get ("identifiers" ) or []:
122+ value = identifier .get ("value" )
123+ if value and value != preferred_id and value not in aliases :
124+ aliases .append (value )
125+ return aliases
126+
127+
128+ def merge_vulnerabilities (vulnerabilities : list [Vulnerability ]) -> list [Vulnerability ]:
129+ merged : dict [str , Vulnerability ] = {}
130+ for vuln in vulnerabilities :
131+ candidate_ids = [vuln .id , * (vuln .advisory_aliases or [])]
132+ for candidate_id in candidate_ids :
133+ existing = merged .get (candidate_id )
134+ if existing is None :
135+ continue
136+ for alias in vuln .advisory_aliases or []:
137+ if alias != existing .id and alias not in existing .advisory_aliases :
138+ existing .advisory_aliases .append (alias )
139+ break
140+ else :
141+ merged [vuln .id ] = vuln
142+ return list (merged .values ())
143+
108144
109145def resolve_dependencies (
110146 repo_path : Path , repo_branch : str
@@ -202,16 +238,19 @@ def query_ghad(
202238 for v in result ["securityVulnerabilities" ]["nodes" ]
203239 if v ["advisory" ]["withdrawnAt" ] is None
204240 and dep_version in SpecifierSet (v ["vulnerableVersionRange" ])
205- and v ["advisory" ][ "ghsaId" ] not in ignore_list
241+ and preferred_advisory_id ( v ["advisory" ]) not in ignore_list
206242 ]
207243 if matching_vulns :
208244 found_vulnerabilities .extend (
209245 [
210246 Vulnerability (
211- id = vuln ["advisory" ][ "ghsaId" ] ,
247+ id = preferred_advisory_id ( vuln ["advisory" ]) ,
212248 url = vuln ["advisory" ]["permalink" ],
213249 dependency = name ,
214250 version = dep_version ,
251+ advisory_aliases = advisory_aliases (
252+ vuln ["advisory" ], preferred_advisory_id (vuln ["advisory" ])
253+ ),
215254 )
216255 for vuln in matching_vulns
217256 ]
@@ -417,11 +456,14 @@ def main() -> int:
417456 print (f"Warning: npm vulnerability checking failed: { e } " , file = sys .stderr )
418457 print (f"Traceback: { traceback .format_exc ()} " , file = sys .stderr )
419458
459+ merged_vulnerabilities = merge_vulnerabilities (
460+ ghad_vulnerabilities + nvd_vulnerabilities + npm_vulnerabilities
461+ )
420462 all_vulnerabilities = {
421- "vulnerabilities" : ghad_vulnerabilities + nvd_vulnerabilities + npm_vulnerabilities ,
463+ "vulnerabilities" : merged_vulnerabilities ,
422464 "scan_complete" : scan_complete ,
423465 }
424- no_vulnerabilities_found = not ghad_vulnerabilities and not nvd_vulnerabilities and not npm_vulnerabilities
466+ no_vulnerabilities_found = not merged_vulnerabilities
425467
426468 if scan_file is not None :
427469 with open (scan_file , "w" ) as f :
0 commit comments