@@ -2563,6 +2563,17 @@ class Meta:
25632563 def save (self , * args , ** kwargs ):
25642564 self .full_clean ()
25652565 return super ().save (* args , ** kwargs )
2566+
2567+ @property
2568+ def get_status_label (self ):
2569+ label_by_status = {choice [0 ]: choice [1 ] for choice in VulnerabilityStatusType .choices }
2570+ return label_by_status .get (self .status ) or VulnerabilityStatusType .PUBLISHED .label
2571+
2572+ def get_absolute_url (self ):
2573+ """
2574+ Return this Vulnerability details absolute URL.
2575+ """
2576+ return reverse ("advisory_details" , args = [self .id ])
25662577
25672578 def to_advisory_data (self ) -> "AdvisoryDataV2" :
25682579 from vulnerabilities .importer import AdvisoryDataV2
@@ -2588,6 +2599,65 @@ def get_aliases(self):
25882599 Return a queryset of all Aliases for this vulnerability.
25892600 """
25902601 return self .aliases .all ()
2602+
2603+ def aggregate_fixed_and_affected_packages (self ):
2604+ from vulnerabilities .utils import get_purl_version_class
2605+
2606+ sorted_fixed_by_packages = self .fixed_by_packages .filter (is_ghost = False ).order_by (
2607+ "type" , "namespace" , "name" , "qualifiers" , "subpath"
2608+ )
2609+
2610+ if sorted_fixed_by_packages :
2611+ sorted_fixed_by_packages .first ().calculate_version_rank
2612+
2613+ sorted_affected_packages = self .affecting_packages .all ()
2614+
2615+ if sorted_affected_packages :
2616+ sorted_affected_packages .first ().calculate_version_rank
2617+
2618+ grouped_fixed_by_packages = {
2619+ key : list (group )
2620+ for key , group in groupby (
2621+ sorted_fixed_by_packages ,
2622+ key = attrgetter ("type" , "namespace" , "name" , "qualifiers" , "subpath" ),
2623+ )
2624+ }
2625+
2626+ all_affected_fixed_by_matches = []
2627+
2628+ for sorted_affected_package in sorted_affected_packages :
2629+ affected_fixed_by_matches = {
2630+ "affected_package" : sorted_affected_package ,
2631+ "matched_fixed_by_packages" : [],
2632+ }
2633+
2634+ # Build the key to find matching group
2635+ key = (
2636+ sorted_affected_package .type ,
2637+ sorted_affected_package .namespace ,
2638+ sorted_affected_package .name ,
2639+ sorted_affected_package .qualifiers ,
2640+ sorted_affected_package .subpath ,
2641+ )
2642+
2643+ # Get matching group from pre-grouped fixed_by_packages
2644+ matching_fixed_packages = grouped_fixed_by_packages .get (key , [])
2645+
2646+ # Get version classes for comparison
2647+ affected_version_class = get_purl_version_class (sorted_affected_package )
2648+ affected_version = affected_version_class (sorted_affected_package .version )
2649+
2650+ # Compare versions and filter valid matches
2651+ matched_fixed_by_packages = [
2652+ fixed_by_package .purl
2653+ for fixed_by_package in matching_fixed_packages
2654+ if get_purl_version_class (fixed_by_package )(fixed_by_package .version )
2655+ > affected_version
2656+ ]
2657+
2658+ affected_fixed_by_matches ["matched_fixed_by_packages" ] = matched_fixed_by_packages
2659+ all_affected_fixed_by_matches .append (affected_fixed_by_matches )
2660+ return sorted_fixed_by_packages , sorted_affected_packages , all_affected_fixed_by_matches
25912661
25922662 alias = get_aliases
25932663
0 commit comments