3030from dateutil import parser as dateparser
3131from django .db .models .query import QuerySet
3232from packageurl import PackageURL
33+ from univers .version_range import RANGE_CLASS_BY_SCHEMES
3334from univers .version_range import build_range_from_github_advisory_constraint
3435
3536from vulnerabilities import severity_systems
4849from vulnerabilities .package_managers import VersionAPI
4950from vulnerabilities .package_managers import get_api_package_name
5051from vulnerabilities .utils import AffectedPackage as LegacyAffectedPackage
52+ from vulnerabilities .utils import dedupe
5153from vulnerabilities .utils import get_affected_packages_by_patched_package
5254from vulnerabilities .utils import get_item
5355from vulnerabilities .utils import nearest_patched_package
153155 severity
154156 publishedAt
155157 }
158+ firstPatchedVersion{
159+ identifier
160+ }
156161 package {
157162 name
158163 }
@@ -236,60 +241,64 @@ def process_response(resp: dict, package_type: str) -> Iterable[AdvisoryData]:
236241 return
237242
238243 for vulnerability in vulnerabilities :
244+ aliases = []
239245 affected_packages = []
240- aliases = set ()
241246 github_advisory = get_item (vulnerability , "node" )
242247 if not github_advisory :
243248 logger .error (f"No node found in { vulnerability !r} " )
244249 continue
245250
246- name = get_item (github_advisory , "package" , "name" )
247- if not name :
248- logger .error (f"No name found in { github_advisory !r} " )
249- continue
250-
251- purl = get_purl (pkg_type = package_type , github_name = name )
252- if not purl :
253- continue
254-
255- vulnerable_range = get_item (github_advisory , "vulnerableVersionRange" )
256- if not vulnerable_range :
257- logger .error (f"No affected range found in { github_advisory !r} " )
258- continue
259-
260- affected_range = None
261- try :
262- affected_range = build_range_from_github_advisory_constraint (
263- package_type , vulnerable_range
264- )
265- except InvalidVersionRange :
266- logger .error (f"Could not parse affected range { vulnerable_range !r} " )
267- continue
268-
269- if affected_range != NotImplementedError :
270- affected_packages .append (
271- AffectedPackage (
272- package = purl ,
273- affected_version_range = affected_range ,
274- )
275- )
276-
277251 advisory = get_item (github_advisory , "advisory" )
278252 if not advisory :
279253 logger .error (f"No advisory found in { github_advisory !r} " )
280254 continue
281255
256+ summary = get_item (advisory , "summary" ) or ""
257+
282258 references = get_item (advisory , "references" ) or []
283259 if references :
284260 urls = (ref ["url" ] for ref in references )
285261 references = [Reference .from_url (u ) for u in urls ]
286262
287- summary = get_item (advisory , "summary" )
263+ date_published = get_item (advisory , "publishedAt" )
264+ if date_published :
265+ date_published = dateparser .parse (date_published )
266+
267+ name = get_item (github_advisory , "package" , "name" )
268+ if name :
269+ purl = get_purl (pkg_type = package_type , github_name = name )
270+ if purl :
271+ affected_range = get_item (github_advisory , "vulnerableVersionRange" )
272+ fixed_version = get_item (github_advisory , "firstPatchedVersion" , "identifier" )
273+ if affected_range :
274+ try :
275+ affected_range = build_range_from_github_advisory_constraint (
276+ package_type , affected_range
277+ )
278+ except InvalidVersionRange as e :
279+ logger .error (f"Could not parse affected range { affected_range !r} { e !r} " )
280+ affected_range = None
281+ if fixed_version :
282+ try :
283+ fixed_version = RANGE_CLASS_BY_SCHEMES [package_type ].version_class (
284+ fixed_version
285+ )
286+ except Exception as e :
287+ logger .error (f"Invalid fixed version { fixed_version !r} { e !r} " )
288+ fixed_version = None
289+ if affected_range or fixed_version :
290+ affected_packages .append (
291+ AffectedPackage (
292+ package = purl ,
293+ affected_version_range = affected_range ,
294+ fixed_version = fixed_version ,
295+ )
296+ )
288297 identifiers = get_item (advisory , "identifiers" ) or []
289298 for identifier in identifiers :
290299 value = identifier ["value" ]
291300 identifier_type = identifier ["type" ]
292- aliases .add (value )
301+ aliases .append (value )
293302 # attach the GHSA with severity score
294303 if identifier_type == "GHSA" :
295304 # Each Node has only one GHSA, hence exit after attaching
@@ -310,12 +319,8 @@ def process_response(resp: dict, package_type: str) -> Iterable[AdvisoryData]:
310319 else :
311320 logger .error (f"Unknown identifier type { identifier_type !r} and value { value !r} " )
312321
313- date_published = get_item (advisory , "publishedAt" )
314- if date_published :
315- date_published = dateparser .parse (date_published )
316-
317322 yield AdvisoryData (
318- aliases = sorted (list (aliases )),
323+ aliases = sorted (dedupe (aliases )),
319324 summary = summary ,
320325 references = references ,
321326 affected_packages = affected_packages ,
0 commit comments