feat(ttp/ipv6_leak): wire Ipv6LeakLifter into composite tagger and worker
- Add "ipv6_leak" to KNOWN_SOURCE_KINDS in ttp/base.py - Register Ipv6LeakLifter(store) in factory.py get_tagger() - Subscribe worker to attacker.fingerprinted; route by Event.type so JARM/HASSH/ipv6_leak share the topic without source_kind collision - Add bump_attacker_ipv6_leak() to BaseRepository (abstract) + TTPMixin (implementation): increments ipv6_leak_count, sets last_ipv6_* denorm fields, appends-with-dedup to AttackerIdentity.ipv6_link_local_iids - Call bump_attacker_ipv6_leak from _process_event after insert_tags - Add DummyRepo stub + coverage call in tests/db/test_base_repo.py
This commit is contained in:
@@ -1549,6 +1549,23 @@ class BaseRepository(ABC):
|
||||
"""Fleet-wide distinct-technique rollup."""
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
async def bump_attacker_ipv6_leak(
|
||||
self,
|
||||
attacker_uuid: str,
|
||||
identity_uuid: Optional[str],
|
||||
evidence: dict[str, Any],
|
||||
) -> None:
|
||||
"""Increment ``Attacker.ipv6_leak_count``, set ``last_ipv6_*`` denorm
|
||||
fields, and append-with-dedup to ``AttackerIdentity.ipv6_link_local_iids``.
|
||||
|
||||
*evidence* is an ``Ipv6LinkLocalLeakEvidence``-shaped dict carrying
|
||||
``addr``, ``iid_kind``, ``mac_oui``, and ``observed_at``. Missing
|
||||
keys default to empty string. The method is idempotent for the
|
||||
count but deduplicates IID entries by ``addr``.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
async def list_ttp_tags_by_attacker(
|
||||
self, uuid: str, limit: int = 2000,
|
||||
|
||||
@@ -14,8 +14,8 @@ from __future__ import annotations
|
||||
|
||||
import json
|
||||
from collections.abc import AsyncIterator
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
from datetime import datetime, timezone
|
||||
from typing import Any, Optional
|
||||
|
||||
from sqlalchemy import func, select
|
||||
from sqlmodel import col
|
||||
@@ -453,6 +453,59 @@ class TTPMixin(_MixinBase):
|
||||
for row in res.scalars().all():
|
||||
yield row
|
||||
|
||||
async def bump_attacker_ipv6_leak(
|
||||
self,
|
||||
attacker_uuid: str,
|
||||
identity_uuid: Optional[str],
|
||||
evidence: dict[str, Any],
|
||||
) -> None:
|
||||
"""Increment ``Attacker.ipv6_leak_count`` + set last_ipv6_* denorm fields.
|
||||
|
||||
Also appends-with-dedup to ``AttackerIdentity.ipv6_link_local_iids``
|
||||
(JSON text column, keyed by ``addr``). Both updates run in a single
|
||||
session; missing rows are silently skipped.
|
||||
"""
|
||||
now = datetime.now(timezone.utc)
|
||||
addr = evidence.get("addr", "")
|
||||
async with self._session() as session:
|
||||
res = await session.execute(
|
||||
select(Attacker).where(Attacker.uuid == attacker_uuid)
|
||||
)
|
||||
attacker = res.scalar_one_or_none()
|
||||
if attacker is not None:
|
||||
attacker.ipv6_leak_count = (attacker.ipv6_leak_count or 0) + 1
|
||||
attacker.last_ipv6_leak_at = now
|
||||
attacker.last_ipv6_link_local = addr or None
|
||||
attacker.last_ipv6_iid_kind = evidence.get("iid_kind") or None
|
||||
attacker.last_ipv6_mac_oui = evidence.get("mac_oui") or None
|
||||
session.add(attacker)
|
||||
|
||||
if identity_uuid:
|
||||
id_res = await session.execute(
|
||||
select(AttackerIdentity).where(
|
||||
AttackerIdentity.uuid == identity_uuid
|
||||
)
|
||||
)
|
||||
identity = id_res.scalar_one_or_none()
|
||||
if identity is not None and addr:
|
||||
try:
|
||||
iids: list[dict[str, Any]] = json.loads(
|
||||
identity.ipv6_link_local_iids or "[]"
|
||||
)
|
||||
except (json.JSONDecodeError, TypeError):
|
||||
iids = []
|
||||
if not any(e.get("iid") == addr for e in iids):
|
||||
iids.append({
|
||||
"iid": addr,
|
||||
"oui": evidence.get("mac_oui", ""),
|
||||
"kind": evidence.get("iid_kind", "unknown"),
|
||||
"first_seen": now.isoformat(),
|
||||
})
|
||||
identity.ipv6_link_local_iids = json.dumps(iids)
|
||||
session.add(identity)
|
||||
|
||||
await session.commit()
|
||||
|
||||
async def list_distinct_techniques(self) -> list[TechniqueRollupRow]:
|
||||
"""Fleet-wide distinct-technique rollup with counts +
|
||||
most-recent-seen timestamps.
|
||||
|
||||
Reference in New Issue
Block a user