feat(phase-28): add scoring system, operational metrics and executive dashboard (T-224 to T-226)

This commit is contained in:
2026-02-09 17:24:44 +01:00
parent a911ddeb52
commit 12f33307fd
11 changed files with 1930 additions and 0 deletions

View File

@@ -0,0 +1,101 @@
import client from "./client";
// ── Types ────────────────────────────────────────────────────────────
export interface MTTDMetric {
mean_hours: number;
median_hours: number;
min_hours: number;
max_hours: number;
sample_size: number;
}
export interface MTTRMetric {
mean_hours: number;
median_hours: number;
min_hours: number;
max_hours: number;
sample_size: number;
}
export interface DetectionEfficacy {
percentage: number;
detected: number;
partially: number;
not_detected: number;
total: number;
}
export interface AlertFidelity {
percentage: number;
triggered: number;
not_triggered: number;
total_evaluated: number;
}
export interface CoverageVelocity {
techniques_per_week: number;
trend: "improving" | "declining" | "stable";
}
export interface ValidationThroughput {
tests_per_week: number;
trend: "improving" | "declining" | "stable";
}
export interface RejectionRate {
percentage: number;
by_red_lead: number;
by_blue_lead: number;
}
export interface OperationalMetrics {
mttd: MTTDMetric | null;
mttr: MTTRMetric | null;
detection_efficacy: DetectionEfficacy;
alert_fidelity: AlertFidelity;
coverage_velocity: CoverageVelocity;
validation_throughput: ValidationThroughput;
rejection_rate: RejectionRate;
}
export interface OperationalTrendPoint {
date: string;
detection_efficacy: number;
validated_tests: number;
detected_tests: number;
}
export interface TeamMetrics {
red_team: {
tests_completed: number;
avg_completion_hours: number | null;
rejection_rate: number;
};
blue_team: {
tests_completed: number;
avg_completion_hours: number | null;
rejection_rate: number;
};
}
// ── API Functions ────────────────────────────────────────────────────
export async function getOperationalMetrics(): Promise<OperationalMetrics> {
const { data } = await client.get<OperationalMetrics>("/metrics/operational");
return data;
}
export async function getOperationalTrend(
period: string = "90d",
): Promise<OperationalTrendPoint[]> {
const { data } = await client.get<OperationalTrendPoint[]>("/metrics/operational/trend", {
params: { period },
});
return data;
}
export async function getMetricsByTeam(): Promise<TeamMetrics> {
const { data } = await client.get<TeamMetrics>("/metrics/operational/by-team");
return data;
}

100
frontend/src/api/scores.ts Normal file
View File

@@ -0,0 +1,100 @@
import client from "./client";
// ── Types ────────────────────────────────────────────────────────────
export interface ScoreBreakdownItem {
score: number;
max: number;
detail: string;
}
export interface TechniqueScore {
mitre_id: string;
name: string;
tactic: string | null;
status_global: string | null;
total_score: number;
breakdown: Record<string, ScoreBreakdownItem>;
}
export interface TacticScore {
tactic: string;
average_score: number;
techniques_count: number;
techniques_scored: number;
}
export interface ActorCoverageScore {
actor_id: string;
actor_name: string;
total_score: number;
techniques_count: number;
techniques_covered: number;
techniques_detail: Array<{
mitre_id: string;
name: string;
score: number;
breakdown: Record<string, ScoreBreakdownItem>;
}>;
}
export interface OrganizationScore {
overall_score: number;
total_coverage: number;
critical_coverage: number;
detection_maturity: number;
response_readiness: number;
techniques_evaluated: number;
techniques_total: number;
}
export interface ScoreHistoryPoint {
date: string;
score: number;
validated_tests: number;
}
export interface ScoringConfig {
weights: {
tests: number;
detection_rules: number;
d3fend: number;
freshness: number;
platform_diversity: number;
};
total: number;
}
// ── API Functions ────────────────────────────────────────────────────
export async function getTechniqueScore(mitreId: string): Promise<TechniqueScore> {
const { data } = await client.get<TechniqueScore>(`/scores/technique/${mitreId}`);
return data;
}
export async function getTacticScore(tactic: string): Promise<TacticScore> {
const { data } = await client.get<TacticScore>(`/scores/tactic/${tactic}`);
return data;
}
export async function getActorCoverageScore(actorId: string): Promise<ActorCoverageScore> {
const { data } = await client.get<ActorCoverageScore>(`/scores/threat-actor/${actorId}`);
return data;
}
export async function getOrganizationScore(): Promise<OrganizationScore> {
const { data } = await client.get<OrganizationScore>("/scores/organization");
return data;
}
export async function getScoreHistory(period: string = "90d"): Promise<ScoreHistoryPoint[]> {
const { data } = await client.get<ScoreHistoryPoint[]>("/scores/history", {
params: { period },
});
return data;
}
export async function getScoringConfig(): Promise<ScoringConfig> {
const { data } = await client.get<ScoringConfig>("/scores/config");
return data;
}