Update NessusActionPlanner.py
This commit is contained in:
@@ -257,6 +257,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):
|
||||||
"""Print a summary of the action plans"""
|
"""Print a summary of the action plans"""
|
||||||
@@ -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,8 +441,23 @@ 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
|
||||||
|
|||||||
Reference in New Issue
Block a user