feat(ttp): STIX 2.1 bundle export for individual attackers

GET /api/v1/attackers/{uuid}/export/stix returns a self-contained STIX
2.1 bundle: ip observation, threat-actor, ATT&CK attack-patterns with
canonical MITRE IDs, uses relationships, per-tag sightings, file SCOs
for artifacts, domain-name SCOs for SMTP targets, and a provider intel
note. Attack-pattern SDOs carry the MITRE bundle IDs so consumers
deduplicating against the public ATT&CK bundle get exact matches.
This commit is contained in:
2026-05-09 07:21:22 -04:00
parent c4d6eb5bb3
commit fe0ed4a251
7 changed files with 653 additions and 0 deletions

View File

@@ -342,6 +342,25 @@ class TTPMixin(_MixinBase):
res = await session.execute(stmt)
return [r.model_dump(mode="json") for r in res.scalars().all()]
async def list_ttp_tags_by_attacker(
self, uuid: str, limit: int = 2000,
) -> list[dict]:
"""Raw ``ttp_tag`` rows for one attacker UUID. Newest-first.
Used by the STIX exporter (and similar full-row consumers) that
need per-tag granularity — distinct from the rollup returned by
:meth:`list_techniques_by_attacker`.
"""
async with self._session() as session:
stmt: Any = (
select(TTPTag)
.where(TTPTag.attacker_uuid == uuid)
.order_by(col(TTPTag.created_at).desc())
.limit(limit)
)
res = await session.execute(stmt)
return [r.model_dump(mode="json") for r in res.scalars().all()]
# ── Backfill iterators (E.4) ────────────────────────────────────
#
# Read-only iterators consumed by ``decnet ttp backfill`` to replay