aps-agent/tests/golden/test_audit_export.py

116 lines
4.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 审计合规导出与保留脱敏黄金测试(plan.md §3.6 / 矩阵「全审计」)
# ============================================================
# 覆盖:脱敏(敏感键掩码/嵌套/非敏感保留)、json 导出可解析、csv 导出可解析、
# 导出元信息、网关端点。
from __future__ import annotations
import csv
import io
import json
import pytest
from fastapi.testclient import TestClient
from server.agent_core.audit_export import (
REDACTED, export_events, sanitize_event,
)
def _event(i: int = 1) -> dict:
return {"id": i, "at": "2026-08-01T00:00:00+08:00", "actor": "tester",
"category": "TEST", "action": f"test.{i}",
"target": {"type": "T"}, "power": "P0", "rationale": {},
"evidenceRefs": ["run-1"], "result": "SUCCESS",
"prevHash": "GENESIS", "hash": "0" * 64}
def test_sanitize_redacts_sensitive_keys():
"""敏感键值被掩码;非敏感值保留。"""
event = {
"id": 1,
"actor": "planner",
"rationale": {"confirmId": "abc", "token": "secret-token", "ok": True},
"meta": {"apiKey": "k-123", "engine": "CP"},
"list": [{"password": "p"}, {"note": "keep"}],
}
cleaned = sanitize_event(event)
assert cleaned["actor"] == "planner" # 非敏感保留
assert cleaned["rationale"]["confirmId"] == "abc"
assert cleaned["rationale"]["token"] == REDACTED
assert cleaned["meta"]["apiKey"] == REDACTED
assert cleaned["meta"]["engine"] == "CP"
assert cleaned["list"][0]["password"] == REDACTED
assert cleaned["list"][1]["note"] == "keep"
def test_export_json_parsable_and_complete():
"""json 导出可解析、事件完整、含元信息。"""
events = [_event(1), _event(2)]
body = export_events(events, fmt="json")
data = json.loads(body)
assert data["count"] == 2
assert len(data["events"]) == 2
assert "exportedAt" in data
assert data["events"][0]["action"] == "test.1"
def test_export_csv_parsable():
"""csv 导出可解析,表头含事件键。"""
events = [_event(1), _event(2)]
body = export_events(events, fmt="csv")
reader = list(csv.reader(io.StringIO(body)))
assert reader[0] == sorted(_event(1).keys()) # 表头
assert len(reader) == 3 # 表头 + 2 行
assert any("test.1" in row for row in reader[1:])
def test_sanitize_preserves_worldkey_and_similar():
"""精确匹配:worldKey/monkey 等含 'key' 子串的非敏感字段保留,apiKey/token 掩码。"""
event = {
"worldKey": "personal-1001",
"monkey": "banana",
"rationale": {"apiKey": "k-123", "token": "t-9", "worldKey": "w-1"},
"secret": "s",
}
cleaned = sanitize_event(event)
assert cleaned["worldKey"] == "personal-1001"
assert cleaned["monkey"] == "banana"
assert cleaned["rationale"]["worldKey"] == "w-1"
assert cleaned["rationale"]["apiKey"] == REDACTED
assert cleaned["rationale"]["token"] == REDACTED
assert cleaned["secret"] == REDACTED
def test_export_rejects_unknown_format():
"""未知格式拒绝。"""
with pytest.raises(ValueError):
export_events([_event()], fmt="xml")
def test_gateway_export_endpoint(monkeypatch, tmp_path):
"""网关导出端点:登录后返回 ok + 脱敏 body。"""
import server.gateway.app as gateway_module
from tests.auth_provider import install_test_auth
install_test_auth(monkeypatch, "tenant-export-test")
class _Store:
tenant_uuid = "platform"
world_key = "default"
data = {"auditEvents": [_event(1)]}
def next_id(self, _k): return 2
def save(self): pass
monkeypatch.setattr(gateway_module, "get_store", lambda: _Store())
client = TestClient(gateway_module.create_app())
login = client.post("/api/auth/login", json={"username": "planner", "password": "test"})
assert login.status_code == 200, login.text
resp = client.get("/api/gov/audit/export?format=json")
assert resp.status_code == 200, resp.text
data = resp.json()
assert data["ok"] is True and data["count"] == 1
payload = json.loads(data["body"])
assert payload["events"][0]["action"] == "test.1"
# csv 格式
resp2 = client.get("/api/gov/audit/export?format=csv")
assert resp2.status_code == 200