Files
T

367 lines
14 KiB
Python

#!/usr/bin/env python3
"""
Anti-Phishing Training Program Analytics
Tracks training completion, simulation results, and program effectiveness
over time. Generates reports comparing departments, identifying repeat
offenders, and measuring ROI.
Usage:
python process.py dashboard --data program_data.json
python process.py trend --data program_data.json --months 12
python process.py repeat-offenders --data program_data.json
python process.py department-report --data program_data.json
"""
import argparse
import json
import sys
from datetime import datetime, timezone
from collections import defaultdict
from dataclasses import dataclass, field, asdict
@dataclass
class UserRecord:
"""Training and simulation record for a single user."""
email: str = ""
name: str = ""
department: str = ""
role: str = ""
simulations_sent: int = 0
simulations_clicked: int = 0
simulations_submitted: int = 0
simulations_reported: int = 0
trainings_assigned: int = 0
trainings_completed: int = 0
last_simulation_date: str = ""
last_training_date: str = ""
risk_level: str = "low"
@dataclass
class DepartmentMetrics:
"""Aggregated metrics for a department."""
name: str = ""
total_users: int = 0
avg_click_rate: float = 0.0
avg_submit_rate: float = 0.0
avg_report_rate: float = 0.0
training_completion: float = 0.0
repeat_offenders: int = 0
trend: str = "stable"
@dataclass
class ProgramDashboard:
"""Overall program dashboard metrics."""
total_users: int = 0
total_simulations_sent: int = 0
overall_click_rate: float = 0.0
overall_submit_rate: float = 0.0
overall_report_rate: float = 0.0
training_completion_rate: float = 0.0
repeat_offender_count: int = 0
repeat_offender_rate: float = 0.0
maturity_level: int = 1
departments: list = field(default_factory=list)
top_risks: list = field(default_factory=list)
monthly_trends: list = field(default_factory=list)
def calculate_risk_level(user: UserRecord) -> str:
"""Calculate risk level for a user based on simulation history."""
if user.simulations_sent == 0:
return "unknown"
click_rate = user.simulations_clicked / user.simulations_sent
submit_rate = user.simulations_submitted / user.simulations_sent
if submit_rate > 0.3 or user.simulations_submitted >= 3:
return "critical"
elif click_rate > 0.4 or user.simulations_clicked >= 3:
return "high"
elif click_rate > 0.2:
return "medium"
elif user.simulations_reported > 0:
return "low"
else:
return "low"
def assess_maturity(dashboard: ProgramDashboard) -> int:
"""Assess SANS Security Awareness Maturity level (1-5)."""
if dashboard.total_simulations_sent == 0:
return 1 # Non-existent
if dashboard.training_completion_rate < 50:
return 2 # Compliance-focused
if dashboard.overall_click_rate > 15:
return 2
if dashboard.overall_click_rate > 5:
return 3 # Promoting Awareness
if dashboard.overall_report_rate > 50 and dashboard.overall_click_rate < 5:
return 5 # Metrics Framework
return 4 # Long-term Sustainment
def process_program_data(data: dict) -> ProgramDashboard:
"""Process raw program data into dashboard metrics."""
dashboard = ProgramDashboard()
users_data = data.get("users", [])
simulations = data.get("simulations", [])
trainings = data.get("trainings", [])
# Build user records
user_records = {}
for u in users_data:
record = UserRecord(
email=u.get("email", ""),
name=u.get("name", ""),
department=u.get("department", "Unknown"),
role=u.get("role", ""),
)
user_records[record.email] = record
# Process simulation results
for sim in simulations:
for result in sim.get("results", []):
email = result.get("email", "")
if email in user_records:
user = user_records[email]
user.simulations_sent += 1
if result.get("clicked"):
user.simulations_clicked += 1
if result.get("submitted"):
user.simulations_submitted += 1
if result.get("reported"):
user.simulations_reported += 1
user.last_simulation_date = sim.get("date", "")
# Process training completions
for training in trainings:
for completion in training.get("completions", []):
email = completion.get("email", "")
if email in user_records:
user = user_records[email]
user.trainings_assigned += 1
if completion.get("completed"):
user.trainings_completed += 1
user.last_training_date = training.get("date", "")
# Calculate risk levels
for user in user_records.values():
user.risk_level = calculate_risk_level(user)
# Aggregate overall metrics
all_users = list(user_records.values())
dashboard.total_users = len(all_users)
total_sent = sum(u.simulations_sent for u in all_users)
total_clicked = sum(u.simulations_clicked for u in all_users)
total_submitted = sum(u.simulations_submitted for u in all_users)
total_reported = sum(u.simulations_reported for u in all_users)
total_assigned = sum(u.trainings_assigned for u in all_users)
total_completed = sum(u.trainings_completed for u in all_users)
dashboard.total_simulations_sent = total_sent
dashboard.overall_click_rate = round(total_clicked / max(total_sent, 1) * 100, 1)
dashboard.overall_submit_rate = round(total_submitted / max(total_sent, 1) * 100, 1)
dashboard.overall_report_rate = round(total_reported / max(total_sent, 1) * 100, 1)
dashboard.training_completion_rate = round(total_completed / max(total_assigned, 1) * 100, 1)
# Repeat offenders (clicked 2+ times)
repeat_offenders = [u for u in all_users if u.simulations_clicked >= 2]
dashboard.repeat_offender_count = len(repeat_offenders)
dashboard.repeat_offender_rate = round(
len(repeat_offenders) / max(len(all_users), 1) * 100, 1
)
# Department breakdown
dept_users = defaultdict(list)
for user in all_users:
dept_users[user.department].append(user)
for dept_name, users in sorted(dept_users.items()):
dept = DepartmentMetrics(name=dept_name, total_users=len(users))
d_sent = sum(u.simulations_sent for u in users)
d_clicked = sum(u.simulations_clicked for u in users)
d_submitted = sum(u.simulations_submitted for u in users)
d_reported = sum(u.simulations_reported for u in users)
d_assigned = sum(u.trainings_assigned for u in users)
d_completed = sum(u.trainings_completed for u in users)
dept.avg_click_rate = round(d_clicked / max(d_sent, 1) * 100, 1)
dept.avg_submit_rate = round(d_submitted / max(d_sent, 1) * 100, 1)
dept.avg_report_rate = round(d_reported / max(d_sent, 1) * 100, 1)
dept.training_completion = round(d_completed / max(d_assigned, 1) * 100, 1)
dept.repeat_offenders = sum(1 for u in users if u.simulations_clicked >= 2)
dashboard.departments.append(dept)
# Top risk users
risk_users = sorted(all_users, key=lambda u: u.simulations_submitted, reverse=True)
dashboard.top_risks = [
{"email": u.email, "name": u.name, "department": u.department,
"click_count": u.simulations_clicked, "submit_count": u.simulations_submitted,
"risk_level": u.risk_level}
for u in risk_users[:20] if u.simulations_clicked > 0
]
# Monthly trends from simulation data
monthly = defaultdict(lambda: {"sent": 0, "clicked": 0, "submitted": 0, "reported": 0})
for sim in simulations:
month = sim.get("date", "")[:7] # YYYY-MM
for result in sim.get("results", []):
monthly[month]["sent"] += 1
if result.get("clicked"):
monthly[month]["clicked"] += 1
if result.get("submitted"):
monthly[month]["submitted"] += 1
if result.get("reported"):
monthly[month]["reported"] += 1
for month in sorted(monthly.keys()):
m = monthly[month]
dashboard.monthly_trends.append({
"month": month,
"sent": m["sent"],
"click_rate": round(m["clicked"] / max(m["sent"], 1) * 100, 1),
"submit_rate": round(m["submitted"] / max(m["sent"], 1) * 100, 1),
"report_rate": round(m["reported"] / max(m["sent"], 1) * 100, 1),
})
dashboard.maturity_level = assess_maturity(dashboard)
return dashboard
def format_dashboard(dashboard: ProgramDashboard) -> str:
"""Format dashboard as text report."""
lines = []
lines.append("=" * 65)
lines.append(" ANTI-PHISHING TRAINING PROGRAM DASHBOARD")
lines.append("=" * 65)
lines.append(f" Generated: {datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M UTC')}")
lines.append(f" Maturity Level: {dashboard.maturity_level}/5 (SANS Model)")
lines.append("")
lines.append("[PROGRAM OVERVIEW]")
lines.append(f" Total Users: {dashboard.total_users}")
lines.append(f" Total Simulations Sent: {dashboard.total_simulations_sent}")
lines.append(f" Overall Click Rate: {dashboard.overall_click_rate}%")
lines.append(f" Overall Submit Rate: {dashboard.overall_submit_rate}%")
lines.append(f" Overall Report Rate: {dashboard.overall_report_rate}%")
lines.append(f" Training Completion: {dashboard.training_completion_rate}%")
lines.append(f" Repeat Offenders: {dashboard.repeat_offender_count} "
f"({dashboard.repeat_offender_rate}%)")
lines.append("")
lines.append("[DEPARTMENT BREAKDOWN]")
lines.append(f" {'Department':<20} {'Users':>6} {'Click%':>7} {'Submit%':>8} "
f"{'Report%':>8} {'Training%':>10} {'Repeat':>7}")
lines.append(" " + "-" * 66)
for dept in sorted(dashboard.departments, key=lambda d: d.avg_click_rate, reverse=True):
lines.append(f" {dept.name:<20} {dept.total_users:>6} {dept.avg_click_rate:>6.1f}% "
f"{dept.avg_submit_rate:>7.1f}% {dept.avg_report_rate:>7.1f}% "
f"{dept.training_completion:>9.1f}% {dept.repeat_offenders:>7}")
lines.append("")
if dashboard.top_risks:
lines.append("[TOP RISK USERS]")
for i, user in enumerate(dashboard.top_risks[:10], 1):
lines.append(f" {i}. {user['name']} ({user['department']}) - "
f"Clicked: {user['click_count']}, Submitted: {user['submit_count']} "
f"[{user['risk_level'].upper()}]")
lines.append("")
if dashboard.monthly_trends:
lines.append("[MONTHLY TRENDS]")
lines.append(f" {'Month':<10} {'Sent':>6} {'Click%':>7} {'Submit%':>8} {'Report%':>8}")
lines.append(" " + "-" * 39)
for trend in dashboard.monthly_trends[-12:]:
lines.append(f" {trend['month']:<10} {trend['sent']:>6} "
f"{trend['click_rate']:>6.1f}% {trend['submit_rate']:>7.1f}% "
f"{trend['report_rate']:>7.1f}%")
lines.append("")
lines.append("=" * 65)
return "\n".join(lines)
def main():
parser = argparse.ArgumentParser(description="Anti-Phishing Training Program Analytics")
subparsers = parser.add_subparsers(dest="command")
dash_parser = subparsers.add_parser("dashboard", help="Generate program dashboard")
dash_parser.add_argument("--data", required=True, help="Program data JSON file")
dash_parser.add_argument("--output", "-o", help="Output file")
dept_parser = subparsers.add_parser("department-report", help="Department breakdown")
dept_parser.add_argument("--data", required=True)
repeat_parser = subparsers.add_parser("repeat-offenders", help="List repeat offenders")
repeat_parser.add_argument("--data", required=True)
repeat_parser.add_argument("--threshold", type=int, default=2, help="Minimum click count")
trend_parser = subparsers.add_parser("trend", help="Show monthly trends")
trend_parser.add_argument("--data", required=True)
trend_parser.add_argument("--months", type=int, default=12)
parser.add_argument("--json", action="store_true", help="Output as JSON")
args = parser.parse_args()
if not args.command:
parser.print_help()
sys.exit(1)
with open(args.data, "r") as f:
data = json.load(f)
dashboard = process_program_data(data)
if args.command == "dashboard":
if args.json:
output = json.dumps(asdict(dashboard), indent=2, default=str)
else:
output = format_dashboard(dashboard)
if args.output:
with open(args.output, "w") as f:
f.write(output)
print(f"Dashboard written to {args.output}")
else:
print(output)
elif args.command == "department-report":
for dept in sorted(dashboard.departments, key=lambda d: d.avg_click_rate, reverse=True):
if args.json:
print(json.dumps(asdict(dept), indent=2))
else:
print(f"{dept.name}: {dept.total_users} users, "
f"click={dept.avg_click_rate}%, report={dept.avg_report_rate}%, "
f"training={dept.training_completion}%")
elif args.command == "repeat-offenders":
for user in dashboard.top_risks:
if user["click_count"] >= args.threshold:
print(f" {user['name']} ({user['department']}): "
f"clicked {user['click_count']}x, submitted {user['submit_count']}x "
f"[{user['risk_level']}]")
elif args.command == "trend":
for trend in dashboard.monthly_trends[-args.months:]:
print(f" {trend['month']}: click={trend['click_rate']}%, "
f"submit={trend['submit_rate']}%, report={trend['report_rate']}%")
if __name__ == "__main__":
main()