From b390a352626365385d67d355988c41b872844256 Mon Sep 17 00:00:00 2001 From: anti Date: Sun, 17 May 2026 20:10:51 -0400 Subject: [PATCH] feat(ttp): add Ipv6LinkLocalLeakEvidence TypedDict + EVIDENCE_SCHEMA entry Pins the evidence shape for IPv6 link-local leakage findings. All fields optional (total=False) so partial observation (passive sniffer vs active solicitation) fills whatever the vector provides. Lifter lands in a subsequent commit. --- decnet/web/db/models/ttp.py | 11 +++++++++++ tests/ttp/test_evidence_shape.py | 12 ++++++++++++ 2 files changed, 23 insertions(+) diff --git a/decnet/web/db/models/ttp.py b/decnet/web/db/models/ttp.py index f23c979e..76faf098 100644 --- a/decnet/web/db/models/ttp.py +++ b/decnet/web/db/models/ttp.py @@ -125,6 +125,16 @@ class HttpFingerprintEvidence(TypedDict): raw: Optional[dict] # raw settings dict for h2_settings / h3_settings +class Ipv6LinkLocalLeakEvidence(TypedDict, total=False): + addr: str # the fe80:: address observed + mac_oui: str # first 3 octets if EUI-64-derived ("aa:bb:cc"), else "" + iid_kind: str # "eui64" | "stable_privacy" | "temporary" | "unknown" + vector: str # "passive_ndp" | "passive_mdns" | "passive_tcp" | "active_ns" | "active_echo" + on_iface: str # local iface that observed it + attacker_v4: str # v4 address we have for this attacker (correlation key) + observed_at: str # ISO8601 UTC + + # Maps source_kind → its evidence TypedDict. Used by TolerantTagger to # validate that lifters do not emit undeclared keys (programmer error → # TypeError, not the swallowed absence-of-data case). @@ -134,6 +144,7 @@ EVIDENCE_SCHEMA: dict[str, type] = { "email": EmailEvidence, "canary_fingerprint": CanaryFingerprintEvidence, "http_fingerprint": HttpFingerprintEvidence, + "ipv6_leak": Ipv6LinkLocalLeakEvidence, } diff --git a/tests/ttp/test_evidence_shape.py b/tests/ttp/test_evidence_shape.py index a61a08a8..e8e46f6e 100644 --- a/tests/ttp/test_evidence_shape.py +++ b/tests/ttp/test_evidence_shape.py @@ -30,6 +30,7 @@ from decnet.web.db.models.ttp import ( EmailEvidence, HttpFingerprintEvidence, IntelEvidence, + Ipv6LinkLocalLeakEvidence, TTPTag, compute_tag_uuid, ) @@ -96,6 +97,17 @@ def test_http_fingerprint_evidence_keys() -> None: assert keys == {"kind", "hash", "protocol", "client_ip", "seen_at", "raw"} +def test_ipv6_link_local_leak_evidence_keys() -> None: + keys = ( + Ipv6LinkLocalLeakEvidence.__required_keys__ + | Ipv6LinkLocalLeakEvidence.__optional_keys__ + ) + assert keys == { + "addr", "mac_oui", "iid_kind", "vector", + "on_iface", "attacker_v4", "observed_at", + } + + # ── Per-lifter parametrized positive case ───────────────────────────