test_guard.py 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. """安全闸门与三层状态判定守卫(任务书 P1-B 安全 / P1-G 结果分层)。"""
  2. from __future__ import annotations
  3. import pytest
  4. from clients import aidop_probe
  5. ALLOWED = ["127.0.0.1", "localhost"]
  6. @pytest.mark.parametrize("url", [
  7. "http://127.0.0.1:5005",
  8. "http://localhost:5005",
  9. ])
  10. def test_whitelisted_hosts_pass(url):
  11. aidop_probe.assert_host_allowed(url, ALLOWED)
  12. @pytest.mark.parametrize("url", [
  13. "http://10.20.30.40:5005",
  14. "https://aidop.customer.com",
  15. "http://192.168.1.99:5005",
  16. "http://prod-aidop.internal",
  17. ])
  18. def test_non_whitelisted_hosts_blocked(url):
  19. with pytest.raises(aidop_probe.HostNotAllowedError):
  20. aidop_probe.assert_host_allowed(url, ALLOWED)
  21. @pytest.mark.parametrize("status,body,expected", [
  22. (0, {"message": "SOURCE_UNREACHABLE: refused"}, "SOURCE_UNREACHABLE"),
  23. (200, {"code": 0, "data": {"fields": []}}, "READY"),
  24. (404, {"message": "entity not registered"}, "CONFIG_NOT_REGISTERED"),
  25. (403, {"message": "inbound not enabled for entity"}, "CONFIG_NOT_ENABLED"),
  26. (403, {"message": "grant missing for accessKey"}, "GRANT_MISSING"),
  27. (401, {"message": "AccessKey 未绑定开放身份"}, "GRANT_MISSING"),
  28. (401, {"message": "signature mismatch"}, "CONFIG_NOT_ENABLED"),
  29. (500, {"message": "boom"}, "RUN_FAILED"),
  30. ])
  31. def test_precheck_state_classification(status, body, expected):
  32. assert aidop_probe.classify_inbound_precheck(status, body) == expected
  33. def test_unreachable_target_reports_source_unreachable():
  34. result = aidop_probe.check_reachable("http://127.0.0.1:1", ALLOWED, timeout=2)
  35. assert result["reachable"] is False
  36. def test_probe_rejects_non_whitelisted_target():
  37. with pytest.raises(aidop_probe.HostNotAllowedError):
  38. aidop_probe.check_reachable("http://10.0.0.9:5005", ALLOWED, timeout=2)
  39. def test_run_store_redacts_before_persist():
  40. """运行记录写入前必须脱敏(不依赖已启动服务,直接测 RunStore)。"""
  41. import app as sim_app
  42. store = sim_app.RunStore(limit=5)
  43. entry = store.add(
  44. run_id="20260930120000-abc123", object_code="ITEM", channel="API_INBOUND",
  45. request_summary={"operation": "push"},
  46. response={"headers": {"X-Signature": "SIG==", "Authorization": "Bearer abc.def"},
  47. "note": "password=hunter2"},
  48. layers={"code": "SUPPORTED", "config": "READY", "data": "ACCEPTED"},
  49. followup_sql=["SELECT 1;"])
  50. dumped = str(entry)
  51. assert "SIG==" not in dumped
  52. assert "abc.def" not in dumped
  53. assert "hunter2" not in dumped
  54. assert entry["layers"]["data"] == "ACCEPTED"
  55. assert store.recent()[0]["runId"] == "20260930120000-abc123"
  56. def test_run_store_respects_limit():
  57. import app as sim_app
  58. store = sim_app.RunStore(limit=3)
  59. for i in range(6):
  60. store.add(str(i), "ITEM", "DB_SYNC", {}, {}, {})
  61. assert len(store.runs) == 3
  62. assert [r["runId"] for r in store.recent()] == ["5", "4", "3"]