feat(ttp/stix): add deduped process SCOs for attacker commands
This commit is contained in:
@@ -1492,6 +1492,10 @@ class BaseRepository(ABC):
|
||||
"""Raw ``ttp_tag`` rows for one attacker (for STIX export + similar)."""
|
||||
raise NotImplementedError
|
||||
|
||||
async def list_attacker_commands_deduped(self, uuid: str) -> list[str]:
|
||||
"""Deduplicated ``command_text`` strings for one attacker, order-preserved."""
|
||||
raise NotImplementedError
|
||||
|
||||
async def list_ttp_decky_phases(
|
||||
self, identity_uuid: str,
|
||||
) -> list[dict[str, Any]]:
|
||||
|
||||
@@ -41,6 +41,24 @@ class AttackerActivityMixin(_MixinBase):
|
||||
page = commands[offset: offset + limit]
|
||||
return {"total": total, "data": page}
|
||||
|
||||
async def list_attacker_commands_deduped(self, uuid: str) -> list[str]:
|
||||
async with self._session() as session:
|
||||
result = await session.execute(
|
||||
select(col(Attacker.commands)).where(Attacker.uuid == uuid)
|
||||
)
|
||||
raw = result.scalar_one_or_none()
|
||||
if raw is None:
|
||||
return []
|
||||
commands: list = json.loads(raw) if isinstance(raw, str) else raw
|
||||
seen: set[str] = set()
|
||||
out: list[str] = []
|
||||
for entry in commands:
|
||||
text = str(entry.get("command_text", "")).strip()
|
||||
if text and text not in seen:
|
||||
seen.add(text)
|
||||
out.append(text)
|
||||
return out
|
||||
|
||||
async def get_attacker_service_activity(
|
||||
self, attacker_uuid: str
|
||||
) -> list[tuple[str, str]]:
|
||||
|
||||
@@ -68,6 +68,7 @@ async def api_export_attacker_stix(
|
||||
repo.list_ttp_tags_by_attacker(uuid),
|
||||
repo.get_attacker_artifacts(uuid),
|
||||
repo.list_smtp_targets(uuid),
|
||||
repo.list_attacker_commands_deduped(uuid),
|
||||
)
|
||||
behavior = cast(dict[str, Any] | None, results[0])
|
||||
identity = cast(dict[str, Any] | None, results[1])
|
||||
@@ -76,6 +77,7 @@ async def api_export_attacker_stix(
|
||||
raw_tags = cast(list[dict[str, Any]], results[4])
|
||||
artifacts = cast(list[dict[str, Any]], results[5])
|
||||
smtp_targets = cast(list[dict[str, Any]], results[6])
|
||||
commands = cast(list[str], results[7])
|
||||
|
||||
bundle = build_attacker_bundle(
|
||||
attacker=attacker,
|
||||
@@ -89,6 +91,7 @@ async def api_export_attacker_stix(
|
||||
raw_tags=raw_tags,
|
||||
artifacts=artifacts,
|
||||
smtp_targets=smtp_targets,
|
||||
commands=commands,
|
||||
)
|
||||
return Response(
|
||||
content=bundle.serialize(pretty=True, indent=2),
|
||||
|
||||
Reference in New Issue
Block a user