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.
This commit is contained in:
2026-05-17 20:10:51 -04:00
parent 3e6587e073
commit b390a35262
2 changed files with 23 additions and 0 deletions

View File

@@ -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,
}

View File

@@ -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 ───────────────────────────