feat(risk): Phase 12 — Risk Intelligence [FASE-12]
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
- TechniqueRiskProfile model: per-technique risk scoring (0-100) - 4-factor weighted scoring: detection_gap(35%) + threat_actors(30%) + osint(20%) + test_failures(15%) - Risk levels: critical(≥75) / high(≥50) / medium(≥25) / low(≥10) / info - Detailed scoring_breakdown (JSONB) + actionable recommendations per technique - Router /api/v1/risk: compute-all, compute-one, list, matrix, summary, recommendations, top - Alembic migration b038risk (raw SQL, idempotent) - QA script: 60+ tests across all endpoints Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
71
backend/app/schemas/risk_schema.py
Normal file
71
backend/app/schemas/risk_schema.py
Normal file
@@ -0,0 +1,71 @@
|
||||
"""Phase 12: Risk Intelligence schemas."""
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, List, Optional
|
||||
from uuid import UUID
|
||||
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
VALID_RISK_LEVELS = ["critical", "high", "medium", "low", "info"]
|
||||
|
||||
|
||||
class TechniqueRiskProfileOut(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: UUID
|
||||
technique_id: UUID
|
||||
risk_score: float
|
||||
likelihood: float
|
||||
impact: float
|
||||
risk_level: str
|
||||
detection_gap: float
|
||||
threat_actor_count: int
|
||||
osint_signal_count: int
|
||||
test_fail_count: int
|
||||
test_total_count: int
|
||||
test_failure_rate: float
|
||||
confidence_level: float
|
||||
scoring_breakdown: Optional[Dict[str, Any]]
|
||||
recommendations: Optional[List[str]]
|
||||
computed_at: datetime
|
||||
is_stale: bool
|
||||
|
||||
|
||||
class RiskMatrixEntry(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
technique_id: UUID
|
||||
technique_name: Optional[str] = None
|
||||
technique_tid: Optional[str] = None # e.g. "T1059"
|
||||
risk_score: float
|
||||
likelihood: float
|
||||
impact: float
|
||||
risk_level: str
|
||||
detection_gap: float
|
||||
computed_at: datetime
|
||||
|
||||
|
||||
class RiskSummary(BaseModel):
|
||||
total_techniques: int
|
||||
scored_techniques: int
|
||||
stale_count: int
|
||||
by_level: Dict[str, int] # {"critical": 3, "high": 12, ...}
|
||||
avg_risk_score: float
|
||||
top_risks: List[RiskMatrixEntry]
|
||||
|
||||
|
||||
class RecommendationItem(BaseModel):
|
||||
technique_id: UUID
|
||||
technique_name: Optional[str] = None
|
||||
technique_tid: Optional[str] = None
|
||||
risk_level: str
|
||||
risk_score: float
|
||||
recommendations: List[str]
|
||||
priority: int # 1 = highest
|
||||
|
||||
|
||||
class ComputeResult(BaseModel):
|
||||
computed: int
|
||||
skipped: int
|
||||
errors: int
|
||||
duration_seconds: float
|
||||
Reference in New Issue
Block a user