"""Technique query service — framework-agnostic queries for technique details.""" from __future__ import annotations from sqlalchemy.orm import Session, joinedload from app.domain.errors import EntityNotFoundError from app.models.technique import Technique from app.models.detection_rule import DetectionRule from app.services.d3fend_import_service import get_defenses_for_technique # Severity sort order for detection rules (most critical first) _SEVERITY_ORDER = {"critical": 0, "high": 1, "medium": 2, "low": 3, "informational": 4, None: 5} def get_technique_detail(db: Session, mitre_id: str) -> dict: """Fetch full technique details including tests, detection rules, and D3FEND defenses.""" technique = ( db.query(Technique) .options(joinedload(Technique.tests)) .filter(Technique.mitre_id == mitre_id) .first() ) if technique is None: raise EntityNotFoundError("Technique", mitre_id) defenses = get_defenses_for_technique(db, technique.id) detection_rules = ( db.query(DetectionRule) .filter( DetectionRule.mitre_technique_id == mitre_id, DetectionRule.is_active == True, # noqa: E712 ) .all() ) # Sort by severity (critical first), then alphabetically by title detection_rules.sort( key=lambda r: (_SEVERITY_ORDER.get(r.severity, 5), (r.title or "").lower()) ) return { "id": str(technique.id), "mitre_id": technique.mitre_id, "name": technique.name, "description": technique.description, "tactic": technique.tactic, "platforms": technique.platforms or [], "mitre_version": technique.mitre_version, "mitre_last_modified": technique.mitre_last_modified, "is_subtechnique": technique.is_subtechnique, "parent_mitre_id": technique.parent_mitre_id, "status_global": technique.status_global.value if technique.status_global else "not_evaluated", "review_required": technique.review_required, "last_review_date": technique.last_review_date, "tests": [ { "id": str(t.id), "name": t.name, "state": t.state.value if t.state else None, "result": t.result.value if t.result else None, "platform": t.platform, "created_at": t.created_at.isoformat() if t.created_at else None, } for t in technique.tests ], "detection_rules": [ { "id": str(r.id), "title": r.title, "description": r.description, "source": r.source, "source_id": r.source_id, "source_url": r.source_url, "rule_format": r.rule_format, "severity": r.severity, "platforms": r.platforms or [], "false_positive_rate": r.false_positive_rate, } for r in detection_rules ], "d3fend_defenses": defenses, }