@@ -221,18 +221,24 @@ def get_affected_by_vulnerabilities(self, package):
221221 """Return a dictionary with advisory as keys and their details, including fixed_by_packages."""
222222 advisories = self .context ["advisory_map" ].get (package .id , [])
223223 impact_map = self .context ["impact_map" ].get (package .id , {})
224+ introduced_patch_map = self .context .get ("introduced_patch_map" , {})
225+ fixed_patch_map = self .context .get ("fixed_patch_map" , {})
224226
225227 if advisories :
226228 result = []
227229
228230 for adv in advisories :
229231 fixed = impact_map .get (adv ["avid" ])
232+ introduced_patches = introduced_patch_map .get ((package .id , adv ["avid" ]), [])
233+ fixed_patches = fixed_patch_map .get ((package .id , adv ["avid" ]), [])
230234 adv .pop ("avid" , None )
231235
232236 result .append (
233237 {
234238 ** adv ,
235239 "fixed_by_packages" : fixed ,
240+ "introduced_by_patch" : introduced_patches ,
241+ "fixed_by_patch" : fixed_patches ,
236242 }
237243 )
238244
@@ -266,7 +272,8 @@ def get_affected_by_vulnerabilities(self, package):
266272 impact = impact_by_avid .get (advisory .avid )
267273 if not impact :
268274 continue
269-
275+ introduced_patches = introduced_patch_map .get ((package .id , advisory .avid ), [])
276+ fixed_patches = fixed_patch_map .get ((package .id , advisory .avid ), [])
270277 result .append (
271278 {
272279 "advisory_id" : advisory .advisory_id .split ("/" )[- 1 ],
@@ -276,9 +283,10 @@ def get_affected_by_vulnerabilities(self, package):
276283 "exploitability" : advisory .exploitability ,
277284 "risk_score" : advisory .risk_score ,
278285 "fixed_by_packages" : [pkg .purl for pkg in impact .fixed_by_packages .all ()],
286+ "introduced_by_patch" : introduced_patches ,
287+ "fixed_by_patch" : fixed_patches ,
279288 }
280289 )
281-
282290 return result
283291
284292 if not advisories :
@@ -353,6 +361,8 @@ def return_advisories_data(self, package, advisories_qs, advisories):
353361 )
354362
355363 impact_by_avid = {impact .advisory .avid : impact for impact in impacts }
364+ introduced_patch_map = self .context .get ("introduced_patch_map" , {})
365+ fixed_patch_map = self .context .get ("fixed_patch_map" , {})
356366
357367 result = []
358368 for advisory in advisories :
@@ -361,6 +371,8 @@ def return_advisories_data(self, package, advisories_qs, advisories):
361371 if not impact :
362372 continue
363373
374+ introduced_patches = introduced_patch_map .get ((package .id , advisory .advisory .avid ), [])
375+ fixed_patches = fixed_patch_map .get ((package .id , advisory .advisory .avid ), [])
364376 result .append (
365377 {
366378 "advisory_id" : advisory .identifier ,
@@ -372,6 +384,8 @@ def return_advisories_data(self, package, advisories_qs, advisories):
372384 "fixed_by_packages" : list (
373385 set ([pkg .purl for pkg in impact .fixed_by_packages .all ()])
374386 ),
387+ "introduced_by_patch" : introduced_patches ,
388+ "fixed_by_patch" : fixed_patches ,
375389 }
376390 )
377391
@@ -456,6 +470,7 @@ def create(self, request, *args, **kwargs):
456470 affected_advisory_map = get_affected_advisories_bulk (page )
457471 fixing_advisory_map = get_fixing_advisories_bulk (page )
458472 impact_map = get_impacts_bulk (page )
473+ introduced_patch_map , fixed_patch_map = get_patches_bulk (page )
459474 serializer = self .get_serializer (
460475 page ,
461476 many = True ,
@@ -464,6 +479,8 @@ def create(self, request, *args, **kwargs):
464479 "advisory_map" : affected_advisory_map ,
465480 "impact_map" : impact_map ,
466481 "fixing_advisory_map" : fixing_advisory_map ,
482+ "introduced_patch_map" : introduced_patch_map ,
483+ "fixed_patch_map" : fixed_patch_map ,
467484 },
468485 )
469486 return self .get_paginated_response (serializer .data )
@@ -673,6 +690,48 @@ def get_impacts_bulk(packages):
673690 return impact_map
674691
675692
693+ def get_patches_bulk (packages ):
694+ """
695+ Returns a tuple of two dicts:
696+ introduced_map: (package_id, advisory_avid) -> list of introduced package_commit_patches dicts
697+ fixed_map: (package_id, advisory_avid) -> list of fixed package_commit_patches dicts
698+ Each package_commit_patches dict contains 'commit_hash' and 'vcs_url'
699+ """
700+ package_ids = [p .id for p in packages ]
701+ if not package_ids :
702+ return {}, {}
703+
704+ impacted_packages_qs = (
705+ ImpactedPackageAffecting .objects .filter (package_id__in = package_ids )
706+ .select_related ("impacted_package__advisory" )
707+ .prefetch_related (
708+ "impacted_package__introduced_by_package_commit_patches" ,
709+ "impacted_package__fixed_by_package_commit_patches" ,
710+ )
711+ )
712+
713+ introduced_patches = defaultdict (list )
714+ fixed_patches = defaultdict (list )
715+
716+ for impacted_pkg_qs in impacted_packages_qs :
717+ pkg_id = impacted_pkg_qs .package_id
718+ impact = impacted_pkg_qs .impacted_package
719+ avid = impact .advisory .avid
720+ key = (pkg_id , avid )
721+
722+ for patch in impact .introduced_by_package_commit_patches .all ():
723+ patch_data = {"commit_hash" : patch .commit_hash , "vcs_url" : patch .vcs_url }
724+ if patch_data not in introduced_patches [key ]:
725+ introduced_patches [key ].append (patch_data )
726+
727+ for patch in impact .fixed_by_package_commit_patches .all ():
728+ patch_data = {"commit_hash" : patch .commit_hash , "vcs_url" : patch .vcs_url }
729+ if patch_data not in fixed_patches [key ]:
730+ fixed_patches [key ].append (patch_data )
731+
732+ return introduced_patches , fixed_patches
733+
734+
676735def get_fixing_advisories_bulk (packages ):
677736 package_ids = [p .id for p in packages ]
678737
0 commit comments