Update NessusActionPlanner.py
This commit is contained in:
@@ -256,6 +256,96 @@ class ActionPlanGenerator:
|
||||
|
||||
print(f"\n✓ Action plan exported to: {output_file}")
|
||||
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):
|
||||
@@ -296,6 +386,7 @@ def main():
|
||||
Examples:
|
||||
%(prog)s scan1.nessus scan2.nessus -o action_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)'
|
||||
)
|
||||
|
||||
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()
|
||||
|
||||
# Validate input files
|
||||
@@ -338,12 +441,27 @@ Examples:
|
||||
# Display summary
|
||||
plan_generator.print_summary()
|
||||
|
||||
# Export to CSV
|
||||
plan_generator.export_to_csv(args.output)
|
||||
# Determine what to export
|
||||
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!")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
exit(main())
|
||||
exit(main())
|
||||
Reference in New Issue
Block a user