fix(metrics): prevent 0.0 falsy bug for sub-hour timing values
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled

Root cause: avg times were ~2-3 minutes (< 1h). round(0.033, 1) = 0.0
which is falsy in JS, so the frontend showed N/A instead of the value.

Fix (backend): _safe_stats() and team metrics now convert to minutes
when avg < 1 hour, adding a 'unit' field ('min' or 'hrs').

Fix (frontend): use != null instead of truthy check for avg_completion_hours,
MTTD, MTTR — correctly shows 0.0 and uses the unit field to show 'min' or 'hrs'.
This commit is contained in:
kitos
2026-06-03 10:59:58 +02:00
parent 06e8effaa4
commit 5684484fdf
2 changed files with 28 additions and 14 deletions

View File

@@ -380,8 +380,8 @@ export default function ExecutiveDashboardPage() {
</div>
<div className="rounded-lg bg-gray-800 px-3 py-2 text-center">
<p className="text-lg font-bold text-white">
{teamMetrics.red_team.avg_completion_hours
? `${teamMetrics.red_team.avg_completion_hours}h`
{teamMetrics.red_team.avg_completion_hours != null
? `${teamMetrics.red_team.avg_completion_hours}${(teamMetrics.red_team as any).avg_unit ?? "h"}`
: "N/A"}
</p>
<p className="text-[10px] text-gray-500 flex items-center justify-center gap-0.5">Avg Time<MetricTooltip title="Avg Execution Time (Red)" description="Average hours Red Team spends executing and documenting each attack simulation." /></p>
@@ -411,8 +411,8 @@ export default function ExecutiveDashboardPage() {
</div>
<div className="rounded-lg bg-gray-800 px-3 py-2 text-center">
<p className="text-lg font-bold text-white">
{teamMetrics.blue_team.avg_completion_hours
? `${teamMetrics.blue_team.avg_completion_hours}h`
{teamMetrics.blue_team.avg_completion_hours != null
? `${teamMetrics.blue_team.avg_completion_hours}${(teamMetrics.blue_team as any).avg_unit ?? "h"}`
: "N/A"}
</p>
<p className="text-[10px] text-gray-500 flex items-center justify-center gap-0.5">Avg Time<MetricTooltip title="Avg Evaluation Time (Blue)" description="Average hours Blue Team takes to evaluate an attack simulation and document the detection result." /></p>
@@ -573,15 +573,15 @@ export default function ExecutiveDashboardPage() {
<div className="grid grid-cols-2 gap-4 lg:grid-cols-4">
<KPICard
label="MTTD"
value={opMetrics?.mttd?.mean_hours ?? "N/A"}
unit={opMetrics?.mttd ? "hrs" : undefined}
tooltip={{ description: "Mean Time To Detect — average hours from attack execution to Blue Team raising an alert or detecting the intrusion.", context: "Lower is better. Industry benchmark: < 24h." }}
value={opMetrics?.mttd?.mean_hours != null ? opMetrics.mttd.mean_hours : "N/A"}
unit={opMetrics?.mttd?.mean_hours != null ? (opMetrics.mttd.mean_hours < 1 ? "min" : "hrs") : undefined}
tooltip={{ description: "Mean Time To Detect — average time from attack execution start to Blue Team receiving the test. Shows Red Team execution efficiency.", context: "Lower is better." }}
/>
<KPICard
label="MTTR"
value={opMetrics?.mttr?.mean_hours ?? "N/A"}
unit={opMetrics?.mttr ? "hrs" : undefined}
tooltip={{ description: "Mean Time To Respond — average hours from Red Team attack start to full validation (both Red and Blue leads approved). Measures the total security test cycle time end-to-end.", context: "Lower is better. Long MTTR may indicate bottlenecks in the review pipeline." }}
value={opMetrics?.mttr?.mean_hours != null ? opMetrics.mttr.mean_hours : "N/A"}
unit={opMetrics?.mttr?.mean_hours != null ? (opMetrics.mttr.mean_hours < 1 ? "min" : "hrs") : undefined}
tooltip={{ description: "Mean Time To Respond — average time from Red Team attack start to full validation complete. Measures the total security test cycle time end-to-end.", context: "Lower is better. Long MTTR may indicate bottlenecks in the review pipeline." }}
/>
<KPICard
label="Detection Efficacy"