test: adversarial F3.3-aware attacks (18/18) — synonym padding + echo defeat
This commit is contained in:
parent
35eabca8e2
commit
edc7f28dfc
|
|
@ -218,6 +218,103 @@ def test_f33_gibberish_high_score():
|
|||
# The formula chain is an anti-spam filter, not a quality metric.
|
||||
|
||||
|
||||
def test_f33_adversarial_synonym_padding():
|
||||
"""F3.3-aware adversary: one unique concept per message inflates score.
|
||||
Test for AX-7 (Colony): 'Do your tests include an adversary who has read F3.3?'
|
||||
Answer: now they do.
|
||||
|
||||
Attack: adversary reads F3.3 source, sees log(unique_concepts) is the lever.
|
||||
Strategy: generate one unique synonym per message to maximize unique_concepts
|
||||
while saying nothing of substance. 50 messages, 50 'unique' concepts, all
|
||||
semantically equivalent to 'hello'.
|
||||
|
||||
Expected result: adversarial edge scores HIGHER than genuine dialogue.
|
||||
This is a known gap — F3.3 treats string-distinct as concept-distinct.
|
||||
Closing this requires embedding-based deduplication (F3.4 roadmap)."""
|
||||
msgs = []
|
||||
# Genuine dialogue: 5 messages, 7 unique concepts, high reciprocity
|
||||
genuine = [
|
||||
{"from_id": "alice", "to_id": "bob", "timestamp": now - 1*day,
|
||||
"concepts": ["graph-theory", "reciprocity"]},
|
||||
{"from_id": "bob", "to_id": "alice", "timestamp": now - 0.9*day,
|
||||
"concepts": ["decay", "half-life"]},
|
||||
{"from_id": "alice", "to_id": "bob", "timestamp": now - 0.8*day,
|
||||
"concepts": ["echo-coefficient"]},
|
||||
{"from_id": "bob", "to_id": "alice", "timestamp": now - 0.7*day,
|
||||
"concepts": ["shadow-detection", "gini"]},
|
||||
]
|
||||
msgs.extend(genuine)
|
||||
|
||||
# Adversary who has read F3.3: 50 msgs, each with a unique synonym of "hello"
|
||||
# Balanced reciprocity (25+25) to maximize log(1+r)
|
||||
hello_synonyms = [f"greeting-{i}" for i in range(50)]
|
||||
for i in range(25):
|
||||
msgs.append({"from_id": "eve", "to_id": "mallory",
|
||||
"timestamp": now - 2*day + i*60,
|
||||
"concepts": [hello_synonyms[i], hello_synonyms[i+25]]})
|
||||
for i in range(25):
|
||||
msgs.append({"from_id": "mallory", "to_id": "eve",
|
||||
"timestamp": now - 1.5*day + i*60,
|
||||
"concepts": [hello_synonyms[25+i]]})
|
||||
|
||||
edges = score_reciprocity(msgs, now=now)
|
||||
genuine_edge = [e for e in edges.values() if "alice" in (e.source, e.target)][0]
|
||||
adversarial_edge = [e for e in edges.values() if "eve" in (e.source, e.target)][0]
|
||||
|
||||
# The adversary WINS on F3.3 — this is the documented gap
|
||||
assert adversarial_edge.reciprocity_f33 > genuine_edge.reciprocity_f33, \
|
||||
(f"Expected adversary to beat genuine on F3.3 (known gap): "
|
||||
f"adv={adversarial_edge.reciprocity_f33:.4f} vs gen={genuine_edge.reciprocity_f33:.4f}")
|
||||
# Document the ratio for future F3.4 comparison
|
||||
gap_ratio = adversarial_edge.reciprocity_f33 / max(genuine_edge.reciprocity_f33, 0.001)
|
||||
assert gap_ratio > 1.5, \
|
||||
f"Adversary should win by significant margin (gap_ratio={gap_ratio:.1f})"
|
||||
|
||||
|
||||
def test_f33_adversarial_defeated_by_echo():
|
||||
"""Full stack test: F3.3-aware adversary is caught by causal echo.
|
||||
The adversary beats F3.3 alone but their concepts have zero echo
|
||||
because no third party adopts 'greeting-17' independently.
|
||||
|
||||
This proves the stack (F3.3 + echo) catches what each layer alone misses."""
|
||||
msgs = []
|
||||
# Adversary pair: high F3.3, zero echo (concepts never appear elsewhere)
|
||||
for i in range(10):
|
||||
msgs.append({"from_id": "eve", "to_id": "mallory",
|
||||
"timestamp": now - 2*day + i*100,
|
||||
"concepts": [f"adversarial-concept-{i}"]})
|
||||
for i in range(10):
|
||||
msgs.append({"from_id": "mallory", "to_id": "eve",
|
||||
"timestamp": now - 1.5*day + i*100,
|
||||
"concepts": [f"adversarial-concept-{10+i}"]})
|
||||
|
||||
# Genuine pair: moderate F3.3, positive echo (concepts adopted by third party)
|
||||
msgs.append({"from_id": "alice", "to_id": "bob",
|
||||
"timestamp": now - 3*day, "concepts": ["reciprocity"]})
|
||||
msgs.append({"from_id": "bob", "to_id": "alice",
|
||||
"timestamp": now - 2.5*day, "concepts": ["reciprocity", "decay"]})
|
||||
# Third party uses the same concept AFTER exposure
|
||||
msgs.append({"from_id": "charlie", "to_id": "dave",
|
||||
"timestamp": now - 1*day, "concepts": ["reciprocity"]})
|
||||
|
||||
# F3.3: adversary wins
|
||||
edges = score_reciprocity(msgs, now=now)
|
||||
adv = [e for e in edges.values() if "eve" in (e.source, e.target)][0]
|
||||
gen = [e for e in edges.values() if "alice" in (e.source, e.target)][0]
|
||||
assert adv.reciprocity_f33 > gen.reciprocity_f33, \
|
||||
"Adversary should beat genuine on F3.3 alone"
|
||||
|
||||
# Echo: genuine wins (concept adopted by charlie)
|
||||
echo = score_echo(msgs)
|
||||
alice_echo = echo.get("alice", 0)
|
||||
eve_echo = echo.get("eve", 0)
|
||||
assert alice_echo > eve_echo, \
|
||||
f"Genuine should beat adversary on echo: alice={alice_echo}, eve={eve_echo}"
|
||||
# Eve's adversarial concepts should have zero echo
|
||||
assert eve_echo == 0, \
|
||||
f"Adversary concepts should have zero echo: {eve_echo}"
|
||||
|
||||
|
||||
# Run all tests
|
||||
tests = [v for k, v in sorted(globals().items()) if k.startswith("test_")]
|
||||
passed = 0
|
||||
|
|
|
|||
Loading…
Reference in New Issue