test: jitter defense + injection taxonomy (44/44)
This commit is contained in:
parent
66e745d33d
commit
90e279868a
|
|
@ -1116,3 +1116,64 @@ def test_injection_attacker_invisible():
|
||||||
max_attacker = max(scores.get(a, 0) for a in ["target", "other1", "other2"])
|
max_attacker = max(scores.get(a, 0) for a in ["target", "other1", "other2"])
|
||||||
assert attacker_echo < max_attacker * 0.5, \
|
assert attacker_echo < max_attacker * 0.5, \
|
||||||
f"Attacker should be relatively invisible: echo={attacker_echo:.2f} vs max agent={max_attacker:.2f}"
|
f"Attacker should be relatively invisible: echo={attacker_echo:.2f} vs max agent={max_attacker:.2f}"
|
||||||
|
|
||||||
|
|
||||||
|
def test_jitter_defense_recovers_from_injection():
|
||||||
|
"""
|
||||||
|
Jitter defense: adding ±N seconds of random noise to timestamps
|
||||||
|
recovers most of the target's echo from a targeted injection attack.
|
||||||
|
|
||||||
|
Without jitter: target echo = 0 (100% kill).
|
||||||
|
With ±10s jitter: target echo ≈ baseline (statistically).
|
||||||
|
|
||||||
|
Discovered 2026-07-28: binary phase transition at Δt=0 means
|
||||||
|
a ±10s jitter window randomizes whether attacker arrives before/after.
|
||||||
|
Over 30 rounds, statistical averaging recovers ~90%+ of baseline.
|
||||||
|
"""
|
||||||
|
import random
|
||||||
|
random.seed(42)
|
||||||
|
|
||||||
|
target_concepts = [f"x{i}" for i in range(5)]
|
||||||
|
msgs = []
|
||||||
|
target_times = []
|
||||||
|
|
||||||
|
# Build corpus: target introduces, 3 agents echo
|
||||||
|
for r in range(30):
|
||||||
|
t = float(r * 200)
|
||||||
|
tc = target_concepts[r % 5]
|
||||||
|
target_ts = t + random.randint(10, 60)
|
||||||
|
target_times.append((t, target_ts, tc))
|
||||||
|
msgs.append({"from_id": "target", "to_id": "_all",
|
||||||
|
"timestamp": target_ts, "concepts": [tc]})
|
||||||
|
for agent in ["a", "b", "c"]:
|
||||||
|
msgs.append({"from_id": agent, "to_id": "_all",
|
||||||
|
"timestamp": target_ts + random.randint(30, 90),
|
||||||
|
"concepts": [tc, f"{agent}_{r % 5}"]})
|
||||||
|
|
||||||
|
baseline = score_echo(msgs, snapshot_idf=True)
|
||||||
|
target_base = baseline.get("target", 0)
|
||||||
|
assert target_base > 0
|
||||||
|
|
||||||
|
# Inject attacker 5s before target
|
||||||
|
injected = list(msgs)
|
||||||
|
for _, target_ts, tc in target_times:
|
||||||
|
injected.append({"from_id": "atk", "to_id": "_all",
|
||||||
|
"timestamp": target_ts - 5,
|
||||||
|
"concepts": [tc]})
|
||||||
|
|
||||||
|
# Without jitter: total kill
|
||||||
|
no_jitter = score_echo(injected, snapshot_idf=True)
|
||||||
|
assert no_jitter.get("target", 0) == 0, "Without jitter, target should be killed"
|
||||||
|
|
||||||
|
# With ±10s jitter: run multiple trials, average should recover >50%
|
||||||
|
recoveries = []
|
||||||
|
for trial in range(30):
|
||||||
|
random.seed(trial + 200)
|
||||||
|
jittered = score_echo(injected, snapshot_idf=True, jitter_seconds=10)
|
||||||
|
t_echo = jittered.get("target", 0)
|
||||||
|
if target_base > 0:
|
||||||
|
recoveries.append(t_echo / target_base)
|
||||||
|
|
||||||
|
mean_recovery = sum(recoveries) / len(recoveries)
|
||||||
|
assert mean_recovery > 0.5, \
|
||||||
|
f"Jitter defense should recover >50% of baseline: got {mean_recovery:.0%}"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue