Runs the chained identity + campaign clustering pipeline against all seven fixtures via from_synthetic / from_synthetic_identity adapters and ratchets every YAML floor to 1.0 — the production clusterer (and the reference clusterers used in the per-fixture tests) all score perfectly across ARI / homogeneity / completeness / singleton_recall on each fixture. Three substrate fixes surfaced by the ratchet: - Tuning: shared_infra now Jaccards payload+C2 only; decky_set moved into cohort_weight to prevent fleet-scarcity false-merges (F1's shared_wordlist failure mode). Tier weight raised to 1.0 so shared payload+C2 alone crosses threshold (F5's intended pass). - Adapter: from_synthetic_identity now reads SyntheticSession started_at + duration_s for session_windows and per-decky timestamps (the production-row adapter still uses start_ts/end_ts when available). - Fixture data: paused_campaign.yaml's JA3 collided exactly with vpn_hopping.yaml's (same TLS extension list). The collision fused two unrelated campaigns under the chained identity layer in the noise_floor composite. Made paused's JA3 distinct. Also wires Campaign / CampaignsResponse into models/__init__.py's __all__ that was missed in the schema commit.
86 lines
3.4 KiB
YAML
86 lines
3.4 KiB
YAML
# Fixture 4 (paused_campaign) — see development/CAMPAIGN_CLUSTERING.md §2.
|
|
#
|
|
# One campaign that operates in two sprints with a multi-day silence
|
|
# between them:
|
|
#
|
|
# active days 1-2 (0-indexed [0, 1]) — Delivery, Exploitation
|
|
# silent days 3-5 (0-indexed [2, 3, 4]) — pause window
|
|
# active days 6-7 (0-indexed [5, 6]) — Discovery, Lateral Movement,
|
|
# Exfiltration
|
|
#
|
|
# Modeled as TWO DSL actors representing the same operator's two
|
|
# operational windows. Both share JA3, HASSH, payload, and C2
|
|
# callback — the stable signals a fingerprint-driven clusterer
|
|
# resolves on. Their ``active_days`` differ so each operator-half
|
|
# emits sessions in disjoint time ranges, which is what makes the
|
|
# adversarial time-window clusterer fragment the campaign.
|
|
#
|
|
# Two-actor modeling caveat: the factory mints a separate
|
|
# ``truth_identity_id`` per DSL actor by design (see IDENTITY_
|
|
# RESOLUTION.md — identities are recovered from signals, not
|
|
# declared in the DSL). This is a CAMPAIGN-LEVEL fixture only;
|
|
# identity-level scoring is fixture 2's job. The bound floors below
|
|
# apply at level=campaign.
|
|
#
|
|
# Pass condition: a fingerprint-driven clusterer must fold both
|
|
# operational windows into one cluster (shared JA3 + HASSH +
|
|
# payload). A clusterer that lets a multi-day quiet period split
|
|
# the campaign fails the completeness floor.
|
|
#
|
|
# Adversarial condition: ``time_window_clusterer`` (union sessions
|
|
# within ≤1 day of each other) is unable to bridge the 4-day silent
|
|
# stretch and splits the campaign into "before pause" and "after
|
|
# pause" clusters. Completeness collapses; the bound floor rejects
|
|
# this clusterer.
|
|
campaign:
|
|
id: paused-campaign-001
|
|
duration_days: 7
|
|
pause_windows:
|
|
- [2, 4] # campaign-wide silence days 3-5 (0-indexed)
|
|
actors:
|
|
- id: ops-sprint-1
|
|
asn: 64520
|
|
ip_pool: sticky
|
|
ja3: "771,4865-4867-49195-49199-49196-49200-157,0-23-65281-10-11-35-16-5-13-18-51-45-43-27,29-24,0"
|
|
hassh: "paused-op-dddddddd-dddddddd-dddddddd"
|
|
hours_active_utc: [9, 10, 11, 12, 13, 14, 15, 16]
|
|
jitter_seconds: 60
|
|
active_days: [0, 1]
|
|
- id: ops-sprint-2
|
|
asn: 64520 # same ASN — operator stays on same egress
|
|
ip_pool: sticky
|
|
ja3: "771,4865-4867-49195-49199-49196-49200-157,0-23-65281-10-11-35-16-5-13-18-51-45-43-27,29-24,0"
|
|
hassh: "paused-op-dddddddd-dddddddd-dddddddd"
|
|
hours_active_utc: [9, 10, 11, 12, 13, 14, 15, 16]
|
|
jitter_seconds: 60
|
|
active_days: [5, 6]
|
|
phases:
|
|
- name: delivery
|
|
actor: ops-sprint-1
|
|
target_selector: { service: ssh, count: 2 }
|
|
dwell_seconds: 1
|
|
- name: exploitation
|
|
actor: ops-sprint-1
|
|
tool_signature:
|
|
payload_hash: "paused-op-stage1-payload"
|
|
c2_callback: "c2.paused-op.example"
|
|
target_selector: { service: ssh, count: 2 }
|
|
dwell_seconds: 5
|
|
- name: discovery
|
|
actor: ops-sprint-2
|
|
target_selector: { service: ssh, count: 2 }
|
|
dwell_seconds: 5
|
|
- name: lateral_movement
|
|
actor: ops-sprint-2
|
|
tool_signature:
|
|
payload_hash: "paused-op-stage1-payload"
|
|
c2_callback: "c2.paused-op.example"
|
|
target_selector: { service: ssh, count: 2 }
|
|
dwell_seconds: 5
|
|
- name: exfiltration
|
|
actor: ops-sprint-2
|
|
tool_signature:
|
|
c2_callback: "c2.paused-op.example"
|
|
target_selector: { service: ssh, count: 2 }
|
|
dwell_seconds: 5
|