Aggregate stats across all runs.
()
| 280 | # ── Stats / Trend queries ───────────────────────────────────────────────────── |
| 281 | |
| 282 | def get_stats() -> dict: |
| 283 | """Aggregate stats across all runs.""" |
| 284 | with get_db() as conn: |
| 285 | totals = conn.execute(""" |
| 286 | SELECT |
| 287 | COUNT(*) AS total_runs, |
| 288 | COALESCE(SUM(total_hosts), 0) AS total_hosts, |
| 289 | COALESCE(SUM(confirmed_exploitable), 0) AS total_confirmed, |
| 290 | COALESCE(SUM(remediations_generated), 0) AS total_remediations, |
| 291 | COALESCE(MAX(risk_score), 0) AS max_risk_score |
| 292 | FROM runs |
| 293 | """).fetchone() |
| 294 | |
| 295 | severity_counts = conn.execute(""" |
| 296 | SELECT |
| 297 | SUM(CASE WHEN cvss_score >= 9.0 THEN 1 ELSE 0 END) AS critical, |
| 298 | SUM(CASE WHEN cvss_score >= 7.0 AND cvss_score < 9.0 THEN 1 ELSE 0 END) AS high, |
| 299 | SUM(CASE WHEN cvss_score >= 4.0 AND cvss_score < 7.0 THEN 1 ELSE 0 END) AS medium, |
| 300 | SUM(CASE WHEN cvss_score > 0 AND cvss_score < 4.0 THEN 1 ELSE 0 END) AS low |
| 301 | FROM findings |
| 302 | WHERE exploit_status = 'CONFIRMED' |
| 303 | """).fetchone() |
| 304 | |
| 305 | return { |
| 306 | "total_runs": totals["total_runs"] or 0, |
| 307 | "total_hosts": totals["total_hosts"] or 0, |
| 308 | "total_confirmed": totals["total_confirmed"] or 0, |
| 309 | "total_remediations": totals["total_remediations"] or 0, |
| 310 | "max_risk_score": totals["max_risk_score"] or 0, |
| 311 | "critical": severity_counts["critical"] or 0, |
| 312 | "high": severity_counts["high"] or 0, |
| 313 | "medium": severity_counts["medium"] or 0, |
| 314 | "low": severity_counts["low"] or 0, |
| 315 | } |
| 316 | |
| 317 | |
| 318 | def get_risk_trend(days: int = 30) -> list[dict]: |