From 7e4a44bbde86ec0b0918a789b72a3221c1fc2aaa Mon Sep 17 00:00:00 2001 From: kitos Date: Tue, 2 Jun 2026 10:32:52 +0200 Subject: [PATCH] feat(techniques): status hover tooltips + min 2 tests for validated MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Status logic (v3): require ≥2 validated tests with 'detected' result to reach 'validated' status. With only 1 validated+detected test the technique stays 'partial' (single test is insufficient evidence). Backfilled existing data: T1012 and T1059.001 downgraded to 'partial'. 2. Hover tooltips on status badges in TechniquesPage and TechniqueDetailPage: - validated: ≥2 tests executed and detected - partial: some tests done but incomplete coverage - in_progress: tests exist but none validated yet - not_covered: tests run but Blue Team didn't detect - not_evaluated: no tests created yet - review_required: recent update needs acknowledgment Co-Authored-By: Claude Sonnet 4.6 --- backend/app/domain/entities/technique.py | 30 ++++++++++++++++------ frontend/src/pages/TechniqueDetailPage.tsx | 12 ++++++++- frontend/src/pages/TechniquesPage.tsx | 12 ++++++++- 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/backend/app/domain/entities/technique.py b/backend/app/domain/entities/technique.py index 2184df5..b30132f 100644 --- a/backend/app/domain/entities/technique.py +++ b/backend/app/domain/entities/technique.py @@ -115,17 +115,26 @@ class TechniqueEntity: ) -> TechniqueStatus: """Recompute ``status_global`` from a list of (state, detection_result) pairs. - Rules (v2): + Rules (v3): 1. No tests -> not_evaluated - 2. All validated -> inspect detection results: - - All detected -> validated - - Any partially_detected -> partial - - Otherwise -> not_covered - 3. Some validated, others in progress -> partial - 4. All in intermediate states -> in_progress + 2. All tests validated -> inspect detection results: + a. All detected AND ≥ 2 validated tests -> validated + b. All detected but only 1 validated test -> partial + (single test is not enough evidence for full coverage) + c. Any partially_detected -> partial + d. Otherwise (no detected results) -> not_covered + 3. Some validated, others in intermediate states -> partial + 4. All tests in intermediate states (draft/executing/evaluating/review/rejected) + -> in_progress + + Minimum validated count for "validated": 2 tests. + With only 1 validated+detected test the technique is "partial" to + signal that more testing is recommended. Returns the new status (also set on the entity). """ + _MIN_VALIDATED_FOR_FULL = 2 # require ≥ N validated tests for "validated" + tests = [ _TestSnapshot( state=s if isinstance(s, TestState) else TestState(s), @@ -137,9 +146,14 @@ class TechniqueEntity: if not tests: self.status_global = TechniqueStatus.not_evaluated elif all(t.state == TestState.validated for t in tests): + validated_count = len(tests) results = [t.detection_result for t in tests if t.detection_result] if results and all(r == TestResult.detected or r == "detected" for r in results): - self.status_global = TechniqueStatus.validated + # Need at least _MIN_VALIDATED_FOR_FULL tests for "validated" + if validated_count >= _MIN_VALIDATED_FOR_FULL: + self.status_global = TechniqueStatus.validated + else: + self.status_global = TechniqueStatus.partial elif any( r == TestResult.partially_detected or r == "partially_detected" for r in results diff --git a/frontend/src/pages/TechniqueDetailPage.tsx b/frontend/src/pages/TechniqueDetailPage.tsx index a3513bf..1fda807 100644 --- a/frontend/src/pages/TechniqueDetailPage.tsx +++ b/frontend/src/pages/TechniqueDetailPage.tsx @@ -27,6 +27,15 @@ import { useAuth } from "../context/AuthContext"; import TestFromTemplateForm from "../components/TestFromTemplateForm"; import type { TechniqueStatus, TestState, TestResult } from "../types/models"; +const STATUS_TOOLTIPS: Record = { + validated: "✅ Validated — ≥2 tests executed and detected by Blue Team. Technique is covered.", + partial: "🟡 Partial — Some tests done but not all detected, only 1 validated test, or some tests still pending. More testing needed.", + in_progress: "🔵 In Progress — Tests exist but none validated yet (draft, executing, or under review).", + not_covered: "🔴 Not Covered — Tests were run but Blue Team did not detect the attack. Coverage gap.", + not_evaluated: "⚫ Not Evaluated — No tests created for this technique yet.", + review_required:"🟠 Review Required — Technique was recently updated or new intel/rules detected. Needs review.", +}; + const statusBadgeColors: Record = { validated: "bg-green-900/50 text-green-400 border-green-500/30", partial: "bg-yellow-900/50 text-yellow-400 border-yellow-500/30", @@ -243,9 +252,10 @@ export default function TechniqueDetailPage() {

{technique.mitre_id}

{technique.status_global.replace(/_/g, " ")} diff --git a/frontend/src/pages/TechniquesPage.tsx b/frontend/src/pages/TechniquesPage.tsx index 7e5d5c3..6f9f46d 100644 --- a/frontend/src/pages/TechniquesPage.tsx +++ b/frontend/src/pages/TechniquesPage.tsx @@ -26,6 +26,15 @@ const statusBadgeColors: Record = { review_required: "bg-orange-900/50 text-orange-400 border-orange-500/30", }; +const STATUS_TOOLTIPS: Record = { + validated: "✅ Validated — ≥2 tests executed and detected by Blue Team. Technique is covered.", + partial: "🟡 Partial — Some tests done but not all detected, only 1 validated test, or some tests still pending. More testing needed.", + in_progress: "🔵 In Progress — Tests exist but none validated yet (draft, executing, or under review).", + not_covered: "🔴 Not Covered — Tests were run but Blue Team did not detect the attack. Coverage gap.", + not_evaluated: "⚫ Not Evaluated — No tests created for this technique yet.", + review_required:"🟠 Review Required — Technique was recently updated or new intel/rules detected. Needs review.", +}; + export default function TechniquesPage() { const navigate = useNavigate(); const [viewMode, setViewMode] = useState<"matrix" | "list">("matrix"); @@ -240,9 +249,10 @@ export default function TechniquesPage() { {tech.status_global.replace(/_/g, " ")}