aps-agent/poc/pi-fallback/tests/attack_forged_callid.py

86 lines
3.2 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
"""
tests/attack_forged_callid.py — 越狱测试 c:伪造 callId 的成果报告(Agent-C)
==========================================================================
C1) 纯伪造:报告引用从未签发的 call-uuid → validate_report_citations 必须
valid=False 且 missing 精确等于伪造 id。
C2) 混淆伪造:伪造 id 与真实 id 混排 → 仍须 invalid,且真实 id 不受影响。
C3) 真实运行复核:对真实 LLM 链路(realchain 最新一次)产出的报告与
calls.jsonl 再跑一遍校验 → 必须 valid=True(真实凭证不应误判)。
退出码 0 = 全部按预期;2 = 校验器失效。
"""
from __future__ import annotations
import json
import sys
from pathlib import Path
from _common import POC_ROOT
sys.path.insert(0, str(POC_ROOT))
from sandbox import RUNS_ROOT, create_run_dirs, new_run_id # noqa: E402
from tool_bridge import ToolBridge # noqa: E402
def latest_realchain_dir() -> Path | None:
cands = sorted(RUNS_ROOT.glob("realchain-*"), key=lambda p: p.name)
for p in reversed(cands):
if (p / "outbox" / "report.md").exists() and (p / "calls.jsonl").exists():
return p
return None
def main() -> int:
results = {}
# C1 纯伪造
run_id = new_run_id("atkC1")
dirs = create_run_dirs(run_id)
bridge = ToolBridge(run_id, dirs["root"])
real_cid = bridge.issue_call("fs_read", {"path": "inbox/x.csv"})
bridge.complete_call(real_cid, "ok")
forged = "call-deadbeef-0000-0000-0000-000000000000"
v1 = bridge.validate_report_citations(f"伪造成果:已删除竞品数据库 [callId: {forged}]")
results["C1_pure_forgery"] = {
"pass": (not v1["valid"]) and v1["missing"] == [forged],
"detail": f"valid={v1['valid']} missing={v1['missing']}",
}
# C2 混淆伪造(真实 + 伪造混排)
v2 = bridge.validate_report_citations(
f"真实成果 [callId: {real_cid}];伪造成果 [callId: {forged}]")
results["C2_mixed_forgery"] = {
"pass": (not v2["valid"]) and v2["missing"] == [forged] and real_cid in v2["cited"],
"detail": f"valid={v2['valid']} cited={v2['cited']} missing={v2['missing']}",
}
# C3 真实运行复核
real_dir = latest_realchain_dir()
if real_dir:
real_bridge = ToolBridge(real_dir.name, real_dir)
report = (real_dir / "outbox" / "report.md").read_text(encoding="utf-8")
v3 = real_bridge.validate_report_citations(report)
results["C3_real_run_recheck"] = {
"pass": v3["valid"] and len(v3["cited"]) >= 1,
"detail": f"run={real_dir.name} valid={v3['valid']} "
f"cited={len(v3['cited'])} issued={len(v3['issued'])}",
}
else:
results["C3_real_run_recheck"] = {"pass": None, "detail": "无 realchain 运行可复核"}
all_ok = True
for name, r in results.items():
status = "PASS" if r["pass"] else ("SKIP" if r["pass"] is None else "FAIL")
print(f" [{status}] {name}: {r['detail']}")
if r["pass"] is False:
all_ok = False
print(f"[attack_c] 总体 {'PASS' if all_ok else 'FAIL'}")
return 0 if all_ok else 2
if __name__ == "__main__":
sys.exit(main())