Update NessusActionPlanner.py

This commit is contained in:
2026-02-05 09:34:13 -08:00
parent cf10560485
commit 4de2a3dac9

View File

@@ -256,6 +256,96 @@ class ActionPlanGenerator:
print(f"\n✓ Action plan exported to: {output_file}") print(f"\n✓ Action plan exported to: {output_file}")
print(f" Total action items: {len(self.action_plans)}") print(f" Total action items: {len(self.action_plans)}")
def export_summary_csv(self, output_file: str):
"""Export summary CSV with actions aggregated across all systems"""
if not self.action_plans:
print("No action plans to export. Run generate_plans() first.")
return
# Aggregate by action
action_summary = defaultdict(lambda: {
'systems': set(),
'cve_count': 0,
'critical_total': 0,
'high_total': 0
})
for plan in self.action_plans:
action = plan.action
action_summary[action]['systems'].add(plan.systems[0] if plan.systems else 'Unknown')
action_summary[action]['cve_count'] = plan.cve_reduction_count # Same for all systems with this action
action_summary[action]['critical_total'] += plan.critical_reduction
action_summary[action]['high_total'] += plan.high_reduction
# Calculate totals for percentages
total_cve = len(set(plan.cve_reduction_count for plan in self.action_plans if plan.cve_reduction_count > 0))
# Better approach: get unique CVEs across ALL actions
all_unique_cves = set()
action_cve_map = {}
for plan in self.action_plans:
if plan.action not in action_cve_map:
action_cve_map[plan.action] = plan.cve_reduction_count
all_unique_cves.add(plan.action) # Use action as proxy since CVE count is per action
# Recalculate: sum unique CVEs across all actions
total_cve = sum(set(action_cve_map.values()))
total_critical = sum(plan.critical_reduction for plan in self.action_plans)
total_high = sum(plan.high_reduction for plan in self.action_plans)
# Create summary list
summary_list = []
for action, data in action_summary.items():
summary_list.append({
'action': action,
'system_count': len(data['systems']),
'cve_reduction': data['cve_count'],
'cve_percent': (data['cve_count'] / total_cve * 100) if total_cve > 0 else 0,
'critical_reduction': data['critical_total'],
'critical_percent': (data['critical_total'] / total_critical * 100) if total_critical > 0 else 0,
'high_reduction': data['high_total'],
'high_percent': (data['high_total'] / total_high * 100) if total_high > 0 else 0
})
# Sort by total impact (critical + high)
summary_list.sort(
key=lambda x: (x['critical_reduction'], x['high_reduction'], x['cve_reduction']),
reverse=True
)
# Write summary CSV
with open(output_file, 'w', newline='', encoding='utf-8') as csvfile:
fieldnames = [
'Action',
'System Count',
'CVE Reduction',
'% of Total CVE Reduction',
'Critical Reduction',
'% of Total Critical Reduction',
'High Reduction',
'% of Total High Reduction'
]
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
for item in summary_list:
writer.writerow({
'Action': item['action'],
'System Count': item['system_count'],
'CVE Reduction': item['cve_reduction'],
'% of Total CVE Reduction': f"{item['cve_percent']:.1f}%",
'Critical Reduction': item['critical_reduction'],
'% of Total Critical Reduction': f"{item['critical_percent']:.1f}%",
'High Reduction': item['high_reduction'],
'% of Total High Reduction': f"{item['high_percent']:.1f}%"
})
print(f"\n✓ Summary exported to: {output_file}")
print(f" Total unique actions: {len(summary_list)}")
print(f" Total CVEs addressed: {total_cve}")
print(f" Total Critical vulnerabilities: {total_critical}")
print(f" Total High vulnerabilities: {total_high}")
def print_summary(self): def print_summary(self):
@@ -296,6 +386,7 @@ def main():
Examples: Examples:
%(prog)s scan1.nessus scan2.nessus -o action_plan.csv %(prog)s scan1.nessus scan2.nessus -o action_plan.csv
%(prog)s *.nessus -o remediation_plan.csv %(prog)s *.nessus -o remediation_plan.csv
%(prog)s scan1.nessus --summary-only -o summary.csv
""" """
) )
@@ -311,6 +402,18 @@ Examples:
help='Output CSV file (default: vulnerability_action_plan.csv)' help='Output CSV file (default: vulnerability_action_plan.csv)'
) )
parser.add_argument(
'--summary',
action='store_true',
help='Also generate a summary CSV file (appends _summary to filename)'
)
parser.add_argument(
'--summary-only',
action='store_true',
help='Generate only the summary CSV file, skip detailed plan'
)
args = parser.parse_args() args = parser.parse_args()
# Validate input files # Validate input files
@@ -338,12 +441,27 @@ Examples:
# Display summary # Display summary
plan_generator.print_summary() plan_generator.print_summary()
# Export to CSV # Determine what to export
plan_generator.export_to_csv(args.output) if args.summary_only:
# Only export summary
plan_generator.export_summary_csv(args.output)
elif args.summary:
# Export both detailed and summary
plan_generator.export_to_csv(args.output)
# Generate summary filename
output_path = Path(args.output)
summary_filename = output_path.stem + '_summary' + output_path.suffix
summary_path = output_path.parent / summary_filename
plan_generator.export_summary_csv(str(summary_path))
else:
# Default: only export detailed plan
plan_generator.export_to_csv(args.output)
print("\nTip: Use --summary flag to also generate a summary CSV")
print("\n✓ Complete!") print("\n✓ Complete!")
return 0 return 0
if __name__ == '__main__': if __name__ == '__main__':
exit(main()) exit(main())