feat(domain): add domain layer foundation -- enums, value objects, TechniqueEntity, repository ports
This commit is contained in:
51
backend/app/domain/value_objects/mitre_id.py
Normal file
51
backend/app/domain/value_objects/mitre_id.py
Normal file
@@ -0,0 +1,51 @@
|
||||
"""MitreId — validated MITRE ATT&CK technique identifier.
|
||||
|
||||
Immutable value object that ensures the identifier follows the ATT&CK
|
||||
format: ``T`` followed by 4 digits, optionally a dot and 3 more digits
|
||||
for sub-techniques (e.g. ``T1059``, ``T1059.001``).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from dataclasses import dataclass
|
||||
|
||||
_MITRE_ID_RE = re.compile(r"^T\d{4}(\.\d{3})?$")
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class MitreId:
|
||||
"""Validated MITRE ATT&CK technique identifier."""
|
||||
|
||||
value: str
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
if not _MITRE_ID_RE.match(self.value):
|
||||
raise ValueError(
|
||||
f"Invalid MITRE ATT&CK ID '{self.value}'. "
|
||||
"Expected format: T1234 or T1234.001"
|
||||
)
|
||||
|
||||
@property
|
||||
def is_subtechnique(self) -> bool:
|
||||
return "." in self.value
|
||||
|
||||
@property
|
||||
def parent_id(self) -> str | None:
|
||||
"""Return the parent technique ID (e.g. T1059 for T1059.001)."""
|
||||
if not self.is_subtechnique:
|
||||
return None
|
||||
return self.value.split(".")[0]
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.value
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, MitreId):
|
||||
return self.value == other.value
|
||||
if isinstance(other, str):
|
||||
return self.value == other
|
||||
return NotImplemented
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.value)
|
||||
Reference in New Issue
Block a user