@@ -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,50 @@ 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+ ordered : list [Vulnerability ] = []
131+
132+ def index_vulnerability (vuln : Vulnerability ) -> None :
133+ aliases = list (dict .fromkeys (alias for alias in (vuln .advisory_aliases or []) if alias != vuln .id ))
134+ vuln .advisory_aliases = aliases
135+ for candidate_id in [vuln .id , * aliases ]:
136+ merged [candidate_id ] = vuln
137+
138+ for vuln in vulnerabilities :
139+ candidate_ids = [vuln .id , * (vuln .advisory_aliases or [])]
140+ for candidate_id in candidate_ids :
141+ existing = merged .get (candidate_id )
142+ if existing is None :
143+ continue
144+ aliases = list (existing .advisory_aliases or [])
145+ for alias in [vuln .id , * (vuln .advisory_aliases or [])]:
146+ if alias != existing .id and alias not in aliases :
147+ aliases .append (alias )
148+ existing .advisory_aliases = aliases
149+ index_vulnerability (existing )
150+ break
151+ else :
152+ index_vulnerability (vuln )
153+ ordered .append (vuln )
154+ return ordered
155+
108156
109157def resolve_dependencies (
110158 repo_path : Path , repo_branch : str
@@ -197,23 +245,28 @@ def query_ghad(
197245 github_vulnerabilities_query , variable_values = variables_package
198246 )
199247 dep_version = dep .version_parser (repo_path )
200- matching_vulns = [
201- v
202- for v in result ["securityVulnerabilities" ]["nodes" ]
203- if v ["advisory" ]["withdrawnAt" ] is None
204- and dep_version in SpecifierSet (v ["vulnerableVersionRange" ])
205- and v ["advisory" ]["ghsaId" ] not in ignore_list
206- ]
248+ matching_vulns = []
249+ for vuln in result ["securityVulnerabilities" ]["nodes" ]:
250+ if vuln ["advisory" ]["withdrawnAt" ] is not None :
251+ continue
252+ if dep_version not in SpecifierSet (vuln ["vulnerableVersionRange" ]):
253+ continue
254+ preferred_id = preferred_advisory_id (vuln ["advisory" ])
255+ aliases = advisory_aliases (vuln ["advisory" ], preferred_id )
256+ if any (candidate_id in ignore_list for candidate_id in [preferred_id , * aliases ]):
257+ continue
258+ matching_vulns .append ((vuln , preferred_id , aliases ))
207259 if matching_vulns :
208260 found_vulnerabilities .extend (
209261 [
210262 Vulnerability (
211- id = vuln [ "advisory" ][ "ghsaId" ] ,
263+ id = preferred_id ,
212264 url = vuln ["advisory" ]["permalink" ],
213265 dependency = name ,
214266 version = dep_version ,
267+ advisory_aliases = aliases ,
215268 )
216- for vuln in matching_vulns
269+ for vuln , preferred_id , aliases in matching_vulns
217270 ]
218271 )
219272
@@ -398,7 +451,7 @@ def main() -> int:
398451
399452 from npm_audit import NPMAuditChecker
400453 print ("Running npm package vulnerability audit..." , file = sys .stderr )
401- npm_checker = NPMAuditChecker (repo_path , npm_timeout , gh_token = gh_token )
454+ npm_checker = NPMAuditChecker (repo_path , npm_timeout , gh_token = gh_token , nvd_key = nvd_key )
402455 npm_vulnerabilities = npm_checker .check_npm_vulnerabilities (Vulnerability )
403456 if npm_checker .failed_packages :
404457 scan_complete = False
@@ -417,11 +470,14 @@ def main() -> int:
417470 print (f"Warning: npm vulnerability checking failed: { e } " , file = sys .stderr )
418471 print (f"Traceback: { traceback .format_exc ()} " , file = sys .stderr )
419472
473+ merged_vulnerabilities = merge_vulnerabilities (
474+ ghad_vulnerabilities + nvd_vulnerabilities + npm_vulnerabilities
475+ )
420476 all_vulnerabilities = {
421- "vulnerabilities" : ghad_vulnerabilities + nvd_vulnerabilities + npm_vulnerabilities ,
477+ "vulnerabilities" : merged_vulnerabilities ,
422478 "scan_complete" : scan_complete ,
423479 }
424- no_vulnerabilities_found = not ghad_vulnerabilities and not nvd_vulnerabilities and not npm_vulnerabilities
480+ no_vulnerabilities_found = not merged_vulnerabilities
425481
426482 if scan_file is not None :
427483 with open (scan_file , "w" ) as f :
0 commit comments