feat(ttp): add mitre_url_for + groups_using_technique helpers
Two reusable bundle-derived lookups that the next two commits build on: - mitre_url_for(tid) returns the canonical attack.mitre.org URL by reading external_references on the cached attack-pattern. Backed by the existing lru-cached _attack_pattern_by_id so per-call cost is constant. Handles top-level techniques and sub-techniques (T1059.004 -> .../techniques/T1059/004). - GroupRef + groups_using_technique(tid) surface the intrusion-set reverse index from the loaded bundle: given a technique, return the MITRE-tracked groups documented as using it. Sorted by group_id for deterministic responses; lru-cached. Sub-technique semantics match ATT&CK Navigator (do NOT auto-union with parent). - decnet/ttp/data/intel_loader._mitre_url_for collapses to a thin re-export of attack_stix.mitre_url_for; the loader keeps mitre_url on TechniqueEmission for the eventual STIX export. - tests/ttp/test_attack_url.py covers both helpers: top-level + sub URLs, unknown -> None / (), GroupRef immutability + hashability, deterministic ordering, sub-technique distinct from parent.
This commit is contained in:
@@ -145,13 +145,13 @@ class ProviderMapping:
|
||||
|
||||
|
||||
def _mitre_url_for(technique_id: str) -> str | None:
|
||||
obj = attack_stix._attack_pattern_by_id(technique_id)
|
||||
if obj is None:
|
||||
return None
|
||||
for ref in obj.get("external_references", []):
|
||||
if ref.get("source_name") == "mitre-attack":
|
||||
return ref.get("url")
|
||||
return None
|
||||
"""Compatibility shim — collapsed to a re-export of :func:`attack_stix.mitre_url_for`.
|
||||
|
||||
Public callers should import :func:`decnet.ttp.attack_stix.mitre_url_for`
|
||||
directly. Kept here so the in-tree loader stays self-contained
|
||||
when someone reads it cold.
|
||||
"""
|
||||
return attack_stix.mitre_url_for(technique_id)
|
||||
|
||||
|
||||
def _data_path(provider: str) -> Path:
|
||||
|
||||
Reference in New Issue
Block a user