@@ -388,30 +388,31 @@ def merge_vulnerability(vuln) -> None:
388388 advisory_aliases = self .advisory_aliases (vuln ["advisory" ], preferred_id ),
389389 )
390390 )
391+ try :
392+ global_advisories = self .fetch_global_advisories (packages_for_global_advisories )
393+ except Exception as exc :
394+ self .failed_packages .append (
395+ f"{ package_dir } : global advisory query failed: { exc } "
396+ )
397+ logger .warning (f"Skipping global advisory query for { package_dir } : { exc } " )
398+ global_advisories = []
399+
391400 for package in packages_for_global_advisories :
392- try :
393- for vuln in self .query_global_advisory_vulnerabilities (package ):
394- merge_vulnerability (
395- vulnerability_class (
396- id = vuln ["id" ],
397- url = vuln ["url" ],
398- dependency = package ["name" ],
399- version = package ["version" ],
400- source = "npm" ,
401- severity = vuln .get ("severity" ),
402- via = [vuln ["summary" ]] if vuln .get ("summary" ) else [],
403- fix_available = vuln .get ("fix_available" ),
404- main_dep_name = main_dep_name ,
405- main_dep_path = main_dep_path ,
406- advisory_aliases = vuln .get ("aliases" , []),
407- )
401+ for vuln in self .match_global_advisories (package , global_advisories ):
402+ merge_vulnerability (
403+ vulnerability_class (
404+ id = vuln ["id" ],
405+ url = vuln ["url" ],
406+ dependency = package ["name" ],
407+ version = package ["version" ],
408+ source = "npm" ,
409+ severity = vuln .get ("severity" ),
410+ via = [vuln ["summary" ]] if vuln .get ("summary" ) else [],
411+ fix_available = vuln .get ("fix_available" ),
412+ main_dep_name = main_dep_name ,
413+ main_dep_path = main_dep_path ,
414+ advisory_aliases = vuln .get ("aliases" , []),
408415 )
409- except Exception as exc :
410- self .failed_packages .append (
411- f"{ package_dir } : global advisory query failed for { package ['name' ]} @{ package ['version' ]} : { exc } "
412- )
413- logger .warning (
414- f"Skipping global advisory query for { package ['name' ]} @{ package ['version' ]} : { exc } "
415416 )
416417
417418 for package in packages :
@@ -456,8 +457,7 @@ def merge_vulnerability(vuln) -> None:
456457 logger .info (f"Parsed { len (vulnerabilities )} merged advisory matches from { package_dir } " )
457458 return vulnerabilities
458459
459- def query_global_advisory_vulnerabilities (self , package : Dict [str , str ]) -> List [Dict [str , object ]]:
460- advisories = self .fetch_global_advisories (package ["name" ], package ["version" ])
460+ def match_global_advisories (self , package : Dict [str , str ], advisories : List [Dict ]) -> List [Dict [str , object ]]:
461461 results : List [Dict [str , object ]] = []
462462 seen_ids : set [str ] = set ()
463463 for advisory in advisories :
@@ -488,16 +488,18 @@ def query_global_advisory_vulnerabilities(self, package: Dict[str, str]) -> List
488488 })
489489 return results
490490
491- def fetch_global_advisories (self , package_name : str , version : str ) -> List [Dict ]:
491+ def fetch_global_advisories (self , packages : List [Dict [str , str ]]) -> List [Dict ]:
492+ if not packages :
493+ return []
492494 cache = getattr (self , "_global_advisory_cache" , None )
493495 if cache is None :
494496 cache = self ._global_advisory_cache = {}
495- key = ( package_name , version )
496- if key in cache :
497- return cache [key ]
497+ requested = tuple ( sorted ({ f" { package [ 'name' ] } @ { package [ ' version' ] } " for package in packages }) )
498+ if requested in cache :
499+ return cache [requested ]
498500 query = urllib .parse .urlencode ([
499501 ("ecosystem" , "npm" ),
500- ("affects[] " , f" { package_name } @ { version } " ),
502+ ("affects" , "," . join ( requested ) ),
501503 ("per_page" , "100" ),
502504 ])
503505 url = f"https://api.github.com/advisories?{ query } "
@@ -506,15 +508,14 @@ def fetch_global_advisories(self, package_name: str, version: str) -> List[Dict]
506508 "X-GitHub-Api-Version" : GITHUB_API_VERSION ,
507509 "User-Agent" : "nsolid-dependency-vuln-assessments" ,
508510 }
509- if self .gh_token is not None :
510- headers ["Authorization" ] = f"Bearer { self .gh_token } "
511511 request = urllib .request .Request (url , headers = headers )
512512 with urllib .request .urlopen (request , timeout = min (self .timeout , 30 )) as response :
513513 payload = json .load (response )
514514 if not isinstance (payload , list ):
515+ logger .warning (f"Global advisory query returned non-list payload for { len (requested )} packages: { payload } " )
515516 return []
516- cache [key ] = [item for item in payload if isinstance (item , dict )]
517- return cache [key ]
517+ cache [requested ] = [item for item in payload if isinstance (item , dict )]
518+ return cache [requested ]
518519
519520 def preferred_global_advisory_cve (self , advisory : Dict ) -> Optional [str ]:
520521 for identifier in advisory .get ("identifiers" ) or []:
0 commit comments