agent-firewall-security 2.2.0__tar.gz → 2.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/PKG-INFO +101 -3
  2. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/README.md +100 -2
  3. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/agent_firewall_security.egg-info/PKG-INFO +101 -3
  4. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/agent_firewall_security.egg-info/SOURCES.txt +7 -2
  5. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/adversarial/__init__.py +31 -17
  6. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/authorization.py +16 -0
  7. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/continuous_auth/engine.py +20 -1
  8. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/deception/__init__.py +28 -4
  9. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/defense/mesh.py +20 -1
  10. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/delegation_budget.py +73 -0
  11. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/evidence_integrity/__init__.py +6 -6
  12. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/immune/engine.py +14 -3
  13. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/intel/__init__.py +2 -0
  14. agent_firewall_security-2.3.0/firewall/intel/engine.py +875 -0
  15. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/invariants/__init__.py +14 -0
  16. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/invariants/__main__.py +51 -13
  17. agent_firewall_security-2.3.0/firewall/invariants/exercise.py +269 -0
  18. agent_firewall_security-2.3.0/firewall/policy_analysis/__init__.py +799 -0
  19. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/recorder/checkpoint.py +12 -1
  20. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/sdk.py +126 -0
  21. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/security_memory/__init__.py +26 -13
  22. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/pyproject.toml +1 -1
  23. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/tests/test_v2_2_adversarial_defense.py +28 -4
  24. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/tests/test_v2_2_deception_integrity.py +2 -2
  25. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/tests/test_v2_2_evidence_integrity.py +6 -6
  26. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/tests/test_v2_2_invariants.py +23 -74
  27. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/tests/test_v2_2_security_memory.py +4 -4
  28. agent_firewall_security-2.3.0/tests/test_v2_3_duplicate_concepts.py +412 -0
  29. agent_firewall_security-2.3.0/tests/test_v2_3_intel_coordination.py +465 -0
  30. agent_firewall_security-2.3.0/tests/test_v2_3_invariant_gate.py +304 -0
  31. agent_firewall_security-2.3.0/tests/test_v2_3_policy_analysis.py +652 -0
  32. agent_firewall_security-2.3.0/tests/test_v2_3_self_attack.py +2686 -0
  33. agent_firewall_security-2.2.0/firewall/correlation/__init__.py +0 -552
  34. agent_firewall_security-2.2.0/firewall/intel/engine.py +0 -495
  35. agent_firewall_security-2.2.0/firewall/policy_analysis/__init__.py +0 -409
  36. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/agent_firewall_security.egg-info/dependency_links.txt +0 -0
  37. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/agent_firewall_security.egg-info/entry_points.txt +0 -0
  38. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/agent_firewall_security.egg-info/requires.txt +0 -0
  39. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/agent_firewall_security.egg-info/top_level.txt +0 -0
  40. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/__init__.py +0 -0
  41. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/a2a/__init__.py +0 -0
  42. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/a2a/auth.py +0 -0
  43. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/adapters/__init__.py +0 -0
  44. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/adapters/anthropic.py +0 -0
  45. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/adapters/generic.py +0 -0
  46. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/adapters/normalize.py +0 -0
  47. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/adapters/openai.py +0 -0
  48. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/agents/__init__.py +0 -0
  49. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/agents/adapters.py +0 -0
  50. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/agents/base.py +0 -0
  51. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/artifact/__init__.py +0 -0
  52. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/artifact/reader.py +0 -0
  53. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/artifact/writer.py +0 -0
  54. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/attackgraph/__init__.py +0 -0
  55. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/attackgraph/engine.py +0 -0
  56. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/attenuation.py +0 -0
  57. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/attest/__init__.py +0 -0
  58. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/attest/authority.py +0 -0
  59. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/benchmarks.py +0 -0
  60. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/capability.py +0 -0
  61. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/capability2/__init__.py +0 -0
  62. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/capability2/constraints.py +0 -0
  63. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/cli.py +0 -0
  64. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/cli_v19.py +0 -0
  65. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/cli_v20.py +0 -0
  66. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/cli_v21.py +0 -0
  67. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/containment/__init__.py +0 -0
  68. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/containment/controller.py +0 -0
  69. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/continuous_auth/__init__.py +0 -0
  70. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/continuous_auth/monitor.py +0 -0
  71. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/continuous_auth/predicates.py +0 -0
  72. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/defense/__init__.py +0 -0
  73. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/delegation.py +0 -0
  74. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/delegation_lineage.py +0 -0
  75. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/delegation_store.py +0 -0
  76. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/engine.py +0 -0
  77. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/evidence.py +0 -0
  78. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/evidence_graph/__init__.py +0 -0
  79. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/evidence_graph/graph.py +0 -0
  80. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/explain.py +0 -0
  81. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/http.py +0 -0
  82. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ident/__init__.py +0 -0
  83. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ident/registry.py +0 -0
  84. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/identity.py +0 -0
  85. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/immune/__init__.py +0 -0
  86. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/incident/__init__.py +0 -0
  87. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/incident/package.py +0 -0
  88. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/invariants/model.py +0 -0
  89. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/invariants/registry.py +0 -0
  90. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/invariants/runtime.py +0 -0
  91. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/invariants/source.py +0 -0
  92. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/invariants/static.py +0 -0
  93. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/key_management.py +0 -0
  94. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/key_store.py +0 -0
  95. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/lab/__init__.py +0 -0
  96. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/lab/engine.py +0 -0
  97. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/lifecycle.py +0 -0
  98. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/lifecycle_store.py +0 -0
  99. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/mcp.py +0 -0
  100. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/namespace.py +0 -0
  101. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/network/__init__.py +0 -0
  102. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/network/attack_path.py +0 -0
  103. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/network/behavior.py +0 -0
  104. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/network/correlation.py +0 -0
  105. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/network/graph.py +0 -0
  106. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/network/model.py +0 -0
  107. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/network/response.py +0 -0
  108. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/network/simulator.py +0 -0
  109. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/network/state.py +0 -0
  110. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/north_star.py +0 -0
  111. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/passport/__init__.py +0 -0
  112. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/passport/builder.py +0 -0
  113. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/platform/__init__.py +0 -0
  114. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/platform/provenance.py +0 -0
  115. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/policy.py +0 -0
  116. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/posture/__init__.py +0 -0
  117. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/posture/engine.py +0 -0
  118. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/protect.py +0 -0
  119. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/provenance/__init__.py +0 -0
  120. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/provenance/registry.py +0 -0
  121. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/recorder/__init__.py +0 -0
  122. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/recorder/encoding.py +0 -0
  123. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/recorder/events.py +0 -0
  124. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/recorder/identity.py +0 -0
  125. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/recorder/recorder.py +0 -0
  126. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/recorder/redact.py +0 -0
  127. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/refusal_state.py +0 -0
  128. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/replay.py +0 -0
  129. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/replay_store.py +0 -0
  130. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/replaylab/__init__.py +0 -0
  131. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/replaylab/laboratory.py +0 -0
  132. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/research/__init__.py +0 -0
  133. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/research/lab.py +0 -0
  134. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/response2/__init__.py +0 -0
  135. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/response2/adaptive.py +0 -0
  136. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/revocation.py +0 -0
  137. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/revocation_store.py +0 -0
  138. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/risk_context.py +0 -0
  139. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/security_context.py +0 -0
  140. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/security_decision.py +0 -0
  141. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/semantic_chain.py +0 -0
  142. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/simulation/__init__.py +0 -0
  143. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/simulation/case.py +0 -0
  144. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/simulation/recorder.py +0 -0
  145. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/simulation/replay.py +0 -0
  146. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/simulation/report.py +0 -0
  147. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/simulation/rollout.py +0 -0
  148. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/simulation/ruleset.py +0 -0
  149. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/task/__init__.py +0 -0
  150. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/task/registry.py +0 -0
  151. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/timeline/__init__.py +0 -0
  152. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/timeline/graph.py +0 -0
  153. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/timeline/timeline.py +0 -0
  154. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/timeline/trajectory.py +0 -0
  155. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/tools.py +0 -0
  156. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/transport.py +0 -0
  157. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/trust/__init__.py +0 -0
  158. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/trust/graph.py +0 -0
  159. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/twin/__init__.py +0 -0
  160. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/twin/adversarial.py +0 -0
  161. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/twin/twin.py +0 -0
  162. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/__init__.py +0 -0
  163. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/__main__.py +0 -0
  164. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/control.py +0 -0
  165. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/demo.py +0 -0
  166. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/introspect.py +0 -0
  167. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/server.py +0 -0
  168. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/service.py +0 -0
  169. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/static/console.css +0 -0
  170. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/static/console.js +0 -0
  171. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/static/control.js +0 -0
  172. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/static/index.html +0 -0
  173. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/static/recorder.css +0 -0
  174. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/static/recorder.js +0 -0
  175. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/static/soc.css +0 -0
  176. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/static/soc.js +0 -0
  177. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/static/v20.css +0 -0
  178. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/static/v20.js +0 -0
  179. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/static/v21.css +0 -0
  180. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/static/v21.js +0 -0
  181. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/v18.py +0 -0
  182. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/v19.py +0 -0
  183. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/v20.py +0 -0
  184. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/v21.py +0 -0
  185. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/verify/__init__.py +0 -0
  186. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/verify/verifier.py +0 -0
  187. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/setup.cfg +0 -0
  188. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/tests/test_continuous_auth.py +0 -0
  189. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/tests/test_engine.py +0 -0
  190. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/tests/test_sdk_continuous_auth.py +0 -0
  191. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/tests/test_security_decision.py +0 -0
  192. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/tests/test_v2_2_adversarial_twin.py +0 -0
  193. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/tests/test_v2_2_capability_delegation.py +0 -0
  194. {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/tests/test_v2_2_platform_provenance.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agent-firewall-security
3
- Version: 2.2.0
3
+ Version: 2.3.0
4
4
  Summary: Security and canonical authorization infrastructure for AI agents and automated tool use.
5
5
  Project-URL: Homepage, https://github.com/Shubhbhangoo/agent-firewall
6
6
  Project-URL: Repository, https://github.com/Shubhbhangoo/agent-firewall
@@ -28,6 +28,8 @@ Requires-Dist: hypothesis>=6.0; extra == "dev"
28
28
 
29
29
  Agent Firewall is built around one security boundary: **authorization remains deterministic, explicit, and fail-closed**. Identity, provenance, monitoring, behavioral analysis, simulation, evidence, and response provide security context around that boundary, but they do not become an alternative path to authorization.
30
30
 
31
+ > **v2.3** is a correctness release. It adds no subsystem and no authorization path: the work was to attack v2.2's shipped behaviour, fix the three fail-open paths that broke, stop analytical output from reading as verified when it was not, and make the strict invariant gate something CI can actually fail on.
32
+ >
31
33
  > **v2.2** makes the control plane adaptive: authority is re-evaluated when the state it rested on changes, contradictions between independent claim sources are reported rather than resolved, and the architectural properties the design rests on are checked by code instead of asserted in prose.
32
34
 
33
35
  ---
@@ -90,6 +92,76 @@ When required evidence is unavailable, verification fails, identity is unknown,
90
92
 
91
93
  ---
92
94
 
95
+ ## What v2.3 changes
96
+
97
+ v2.3 adds no subsystem. Three requests that v2.2 allowed are now denied,
98
+ each found by attacking the shipped implementation rather than by reviewing
99
+ the design.
100
+
101
+ ### Three fail-open paths closed
102
+
103
+ - **A non-finite request value satisfied every numeric bound.** Numeric
104
+ constraints are enforced by negation — admit unless `actual > expected` —
105
+ and `NaN` compares `False` against everything, so `{"amount": NaN}` passed
106
+ an `amount_max` of 100 and an `amount_min` of 10 at the same time.
107
+ `json.loads` accepts the bare tokens `NaN`, `Infinity` and `-Infinity`, so
108
+ the value arrived through ordinary request bodies and tool output. Now
109
+ `constraint_denied`.
110
+ - **The first decision taken while a configured dependency was blind
111
+ reported as `authorized`.** v2.2 gated all three revalidation paths but
112
+ not the initial decision, so a capability was allowed once and denied by
113
+ every revalidation of the same request. Now
114
+ `security_dependency_unavailable: <names>`, applied at the boundary — the
115
+ engine still returns a `(bool, reason)` pair and mints no verdict.
116
+ - **Reconfiguring a delegation budget reset the consumed total.** An
117
+ exhausted lineage's whole allowance was restored by an administrative call
118
+ that revoked, re-issued and signed nothing — and the idempotent case was
119
+ the dangerous one, since a startup path re-applying the same limit cleared
120
+ the ledger on every restart. `configure` now adjusts the ceiling and
121
+ leaves the ledger alone.
122
+
123
+ All three share one shape: **an admission must be positively established,
124
+ not inferred from the absence of a violation.** See
125
+ [`docs/v2.3-security-corrections.md`](docs/v2.3-security-corrections.md).
126
+
127
+ ### The self-attack suite
128
+
129
+ `tests/test_v2_3_self_attack.py` is 116 tests, one section per question in
130
+ the mission's final self-attack list, each attempting the attack through the
131
+ real public API. Two rules govern the file: attack through the front door,
132
+ and where the system makes no guarantee, pin the non-guarantee instead of
133
+ faking one. A completeness test maps each of the thirteen questions to its
134
+ section, so deleting one fails rather than quietly shrinking the suite. See
135
+ [`docs/v2.3-self-attack.md`](docs/v2.3-self-attack.md).
136
+
137
+ ### A strict invariant gate that can pass
138
+
139
+ `python -m firewall.invariants --strict` exited 2 on every invocation,
140
+ because five of the eleven invariants are claims about live state that a
141
+ source-only run never reaches. A gate that always fails is a gate that gets
142
+ removed, so those five were effectively ungated in CI.
143
+ `firewall/invariants/exercise.py` builds the canonical estate through the
144
+ SDK's public API only, and CI now runs the source-only and exercised gates
145
+ as separate steps. What a green exercised run establishes is bounded to that
146
+ estate, and the printed output says so. See
147
+ [`docs/v2.3-invariant-gate.md`](docs/v2.3-invariant-gate.md).
148
+
149
+ ### One name, one guarantee
150
+
151
+ Three renames separate a cryptographic result from an analytical one that
152
+ shared its name — `deception.ClaimIntegrityReport`,
153
+ `security_memory.EvidenceCheckpoint`, and
154
+ `AgentSecurityProfile.finding_score`. The third mattered most:
155
+ `MeshState.trust_score` is 0.0 when identity could not be verified, while
156
+ the profile's was 1.0 until something was found, so wiring the profile into
157
+ the mesh's `trust_provider` would have delivered an unchecked agent as fully
158
+ trusted. `firewall.correlation` is deleted; coordination detection moved to
159
+ `firewall.intel`, where every finding carries supporting facts, a rationale
160
+ and `basis="inferred"`. See
161
+ [`docs/v2.3-migration.md`](docs/v2.3-migration.md).
162
+
163
+ ---
164
+
93
165
  ## What v2.2 adds
94
166
 
95
167
  ### Continuous authorization
@@ -358,7 +430,9 @@ The architecture is designed around explicit security invariants.
358
430
  - **A check that could not run is not a check that passed.**
359
431
  - **Security failures default toward refusal rather than implicit trust.**
360
432
 
361
- These are implementation properties of the system, not a claim that any deployment is universally secure. Eleven such properties are additionally stated once in `firewall.invariants` and checked by code rather than asserted in prose alone; see [`docs/v2.2-invariants.md`](docs/v2.2-invariants.md).
433
+ These are implementation properties of the system, not a claim that any deployment is universally secure. Eleven such properties are additionally stated once in `firewall.invariants` and checked by code rather than asserted in prose alone; see [`docs/v2.2-invariants.md`](docs/v2.2-invariants.md) for the invariants themselves and [`docs/v2.3-invariant-gate.md`](docs/v2.3-invariant-gate.md) for what a green gate run does and does not establish.
434
+
435
+ Where a property does **not** hold, it is stated rather than left to be inferred. A posture change is detected but does not by itself flip a verdict; `retire_key` is not containment for a stolen key, since a retired key's signatures keep verifying so that rotation does not invalidate capabilities in flight; an `amount_max` ceiling is per request, so two siblings each holding one can spend it twice unless a lineage budget is configured; and possession of a trusted signing key is authority, which no cryptography can undo. [`docs/v2.3-self-attack.md`](docs/v2.3-self-attack.md) records each of these against the test that pins it.
362
436
 
363
437
  ---
364
438
 
@@ -394,7 +468,7 @@ Verification distinguishes states including `verified`, `failed`, `unverifiable`
394
468
  Python 3.10+ is required.
395
469
 
396
470
  ```bash
397
- pip install agent-firewall-security==2.2.0
471
+ pip install agent-firewall-security==2.3.0
398
472
  ```
399
473
 
400
474
  Development installation:
@@ -442,6 +516,11 @@ The authorization path evaluates the security conditions required by the capabil
442
516
 
443
517
  ## Command surface
444
518
 
519
+ v2.3 adds no new CLI subcommands. It adds one flag to the invariant
520
+ checker: `python -m firewall.invariants --exercise --strict` builds the
521
+ canonical estate so that all eleven invariants can be reached, which makes
522
+ `--strict` a gate that can pass and is therefore worth failing.
523
+
445
524
  v2.2 adds no new CLI subcommands. Its one new entry point is the invariant
446
525
  checker, `python -m firewall.invariants`, shown above.
447
526
 
@@ -492,6 +571,21 @@ python -m firewall.benchmarks
492
571
 
493
572
  The repository contains unit, integration, adversarial, hardening, evidence, UI/API, benchmark, and research tests.
494
573
 
574
+ The v2.3 test surface adds the thirteen-question self-attack suite, which
575
+ covers, among other properties:
576
+
577
+ - a non-finite request value satisfying no numeric ceiling or floor
578
+ - the first decision under a blind dependency being withheld, and agreeing
579
+ with its own revalidations
580
+ - a delegation budget's consumed total surviving reconfiguration
581
+ - erasing or re-pointing the delegation registry failing closed against the
582
+ child's own signature
583
+ - naming an issuer as trusted not importing its keys
584
+ - `revoke_issuer` containing a compromised signer where `retire_key` does not
585
+ - no gate in the firewall reading the untrusted-data taint marker, because a
586
+ type and a signature are the barrier rather than a filter
587
+ - the degradation subtraction being unable to turn a denial into an allow
588
+
495
589
  The v2.2 test surface covers, among other properties:
496
590
 
497
591
  - capability attenuation
@@ -603,6 +697,10 @@ See [`SECURITY.md`](SECURITY.md) for the project's security reporting policy.
603
697
 
604
698
  Detailed specifications are maintained in the repository:
605
699
 
700
+ - `docs/v2.3-security-corrections.md`
701
+ - `docs/v2.3-self-attack.md`
702
+ - `docs/v2.3-invariant-gate.md`
703
+ - `docs/v2.3-migration.md`
606
704
  - `docs/v2.2-architecture.md`
607
705
  - `docs/v2.2-security-model.md`
608
706
  - `docs/v2.2-threat-model.md`
@@ -4,6 +4,8 @@
4
4
 
5
5
  Agent Firewall is built around one security boundary: **authorization remains deterministic, explicit, and fail-closed**. Identity, provenance, monitoring, behavioral analysis, simulation, evidence, and response provide security context around that boundary, but they do not become an alternative path to authorization.
6
6
 
7
+ > **v2.3** is a correctness release. It adds no subsystem and no authorization path: the work was to attack v2.2's shipped behaviour, fix the three fail-open paths that broke, stop analytical output from reading as verified when it was not, and make the strict invariant gate something CI can actually fail on.
8
+ >
7
9
  > **v2.2** makes the control plane adaptive: authority is re-evaluated when the state it rested on changes, contradictions between independent claim sources are reported rather than resolved, and the architectural properties the design rests on are checked by code instead of asserted in prose.
8
10
 
9
11
  ---
@@ -66,6 +68,76 @@ When required evidence is unavailable, verification fails, identity is unknown,
66
68
 
67
69
  ---
68
70
 
71
+ ## What v2.3 changes
72
+
73
+ v2.3 adds no subsystem. Three requests that v2.2 allowed are now denied,
74
+ each found by attacking the shipped implementation rather than by reviewing
75
+ the design.
76
+
77
+ ### Three fail-open paths closed
78
+
79
+ - **A non-finite request value satisfied every numeric bound.** Numeric
80
+ constraints are enforced by negation — admit unless `actual > expected` —
81
+ and `NaN` compares `False` against everything, so `{"amount": NaN}` passed
82
+ an `amount_max` of 100 and an `amount_min` of 10 at the same time.
83
+ `json.loads` accepts the bare tokens `NaN`, `Infinity` and `-Infinity`, so
84
+ the value arrived through ordinary request bodies and tool output. Now
85
+ `constraint_denied`.
86
+ - **The first decision taken while a configured dependency was blind
87
+ reported as `authorized`.** v2.2 gated all three revalidation paths but
88
+ not the initial decision, so a capability was allowed once and denied by
89
+ every revalidation of the same request. Now
90
+ `security_dependency_unavailable: <names>`, applied at the boundary — the
91
+ engine still returns a `(bool, reason)` pair and mints no verdict.
92
+ - **Reconfiguring a delegation budget reset the consumed total.** An
93
+ exhausted lineage's whole allowance was restored by an administrative call
94
+ that revoked, re-issued and signed nothing — and the idempotent case was
95
+ the dangerous one, since a startup path re-applying the same limit cleared
96
+ the ledger on every restart. `configure` now adjusts the ceiling and
97
+ leaves the ledger alone.
98
+
99
+ All three share one shape: **an admission must be positively established,
100
+ not inferred from the absence of a violation.** See
101
+ [`docs/v2.3-security-corrections.md`](docs/v2.3-security-corrections.md).
102
+
103
+ ### The self-attack suite
104
+
105
+ `tests/test_v2_3_self_attack.py` is 116 tests, one section per question in
106
+ the mission's final self-attack list, each attempting the attack through the
107
+ real public API. Two rules govern the file: attack through the front door,
108
+ and where the system makes no guarantee, pin the non-guarantee instead of
109
+ faking one. A completeness test maps each of the thirteen questions to its
110
+ section, so deleting one fails rather than quietly shrinking the suite. See
111
+ [`docs/v2.3-self-attack.md`](docs/v2.3-self-attack.md).
112
+
113
+ ### A strict invariant gate that can pass
114
+
115
+ `python -m firewall.invariants --strict` exited 2 on every invocation,
116
+ because five of the eleven invariants are claims about live state that a
117
+ source-only run never reaches. A gate that always fails is a gate that gets
118
+ removed, so those five were effectively ungated in CI.
119
+ `firewall/invariants/exercise.py` builds the canonical estate through the
120
+ SDK's public API only, and CI now runs the source-only and exercised gates
121
+ as separate steps. What a green exercised run establishes is bounded to that
122
+ estate, and the printed output says so. See
123
+ [`docs/v2.3-invariant-gate.md`](docs/v2.3-invariant-gate.md).
124
+
125
+ ### One name, one guarantee
126
+
127
+ Three renames separate a cryptographic result from an analytical one that
128
+ shared its name — `deception.ClaimIntegrityReport`,
129
+ `security_memory.EvidenceCheckpoint`, and
130
+ `AgentSecurityProfile.finding_score`. The third mattered most:
131
+ `MeshState.trust_score` is 0.0 when identity could not be verified, while
132
+ the profile's was 1.0 until something was found, so wiring the profile into
133
+ the mesh's `trust_provider` would have delivered an unchecked agent as fully
134
+ trusted. `firewall.correlation` is deleted; coordination detection moved to
135
+ `firewall.intel`, where every finding carries supporting facts, a rationale
136
+ and `basis="inferred"`. See
137
+ [`docs/v2.3-migration.md`](docs/v2.3-migration.md).
138
+
139
+ ---
140
+
69
141
  ## What v2.2 adds
70
142
 
71
143
  ### Continuous authorization
@@ -334,7 +406,9 @@ The architecture is designed around explicit security invariants.
334
406
  - **A check that could not run is not a check that passed.**
335
407
  - **Security failures default toward refusal rather than implicit trust.**
336
408
 
337
- These are implementation properties of the system, not a claim that any deployment is universally secure. Eleven such properties are additionally stated once in `firewall.invariants` and checked by code rather than asserted in prose alone; see [`docs/v2.2-invariants.md`](docs/v2.2-invariants.md).
409
+ These are implementation properties of the system, not a claim that any deployment is universally secure. Eleven such properties are additionally stated once in `firewall.invariants` and checked by code rather than asserted in prose alone; see [`docs/v2.2-invariants.md`](docs/v2.2-invariants.md) for the invariants themselves and [`docs/v2.3-invariant-gate.md`](docs/v2.3-invariant-gate.md) for what a green gate run does and does not establish.
410
+
411
+ Where a property does **not** hold, it is stated rather than left to be inferred. A posture change is detected but does not by itself flip a verdict; `retire_key` is not containment for a stolen key, since a retired key's signatures keep verifying so that rotation does not invalidate capabilities in flight; an `amount_max` ceiling is per request, so two siblings each holding one can spend it twice unless a lineage budget is configured; and possession of a trusted signing key is authority, which no cryptography can undo. [`docs/v2.3-self-attack.md`](docs/v2.3-self-attack.md) records each of these against the test that pins it.
338
412
 
339
413
  ---
340
414
 
@@ -370,7 +444,7 @@ Verification distinguishes states including `verified`, `failed`, `unverifiable`
370
444
  Python 3.10+ is required.
371
445
 
372
446
  ```bash
373
- pip install agent-firewall-security==2.2.0
447
+ pip install agent-firewall-security==2.3.0
374
448
  ```
375
449
 
376
450
  Development installation:
@@ -418,6 +492,11 @@ The authorization path evaluates the security conditions required by the capabil
418
492
 
419
493
  ## Command surface
420
494
 
495
+ v2.3 adds no new CLI subcommands. It adds one flag to the invariant
496
+ checker: `python -m firewall.invariants --exercise --strict` builds the
497
+ canonical estate so that all eleven invariants can be reached, which makes
498
+ `--strict` a gate that can pass and is therefore worth failing.
499
+
421
500
  v2.2 adds no new CLI subcommands. Its one new entry point is the invariant
422
501
  checker, `python -m firewall.invariants`, shown above.
423
502
 
@@ -468,6 +547,21 @@ python -m firewall.benchmarks
468
547
 
469
548
  The repository contains unit, integration, adversarial, hardening, evidence, UI/API, benchmark, and research tests.
470
549
 
550
+ The v2.3 test surface adds the thirteen-question self-attack suite, which
551
+ covers, among other properties:
552
+
553
+ - a non-finite request value satisfying no numeric ceiling or floor
554
+ - the first decision under a blind dependency being withheld, and agreeing
555
+ with its own revalidations
556
+ - a delegation budget's consumed total surviving reconfiguration
557
+ - erasing or re-pointing the delegation registry failing closed against the
558
+ child's own signature
559
+ - naming an issuer as trusted not importing its keys
560
+ - `revoke_issuer` containing a compromised signer where `retire_key` does not
561
+ - no gate in the firewall reading the untrusted-data taint marker, because a
562
+ type and a signature are the barrier rather than a filter
563
+ - the degradation subtraction being unable to turn a denial into an allow
564
+
471
565
  The v2.2 test surface covers, among other properties:
472
566
 
473
567
  - capability attenuation
@@ -579,6 +673,10 @@ See [`SECURITY.md`](SECURITY.md) for the project's security reporting policy.
579
673
 
580
674
  Detailed specifications are maintained in the repository:
581
675
 
676
+ - `docs/v2.3-security-corrections.md`
677
+ - `docs/v2.3-self-attack.md`
678
+ - `docs/v2.3-invariant-gate.md`
679
+ - `docs/v2.3-migration.md`
582
680
  - `docs/v2.2-architecture.md`
583
681
  - `docs/v2.2-security-model.md`
584
682
  - `docs/v2.2-threat-model.md`
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agent-firewall-security
3
- Version: 2.2.0
3
+ Version: 2.3.0
4
4
  Summary: Security and canonical authorization infrastructure for AI agents and automated tool use.
5
5
  Project-URL: Homepage, https://github.com/Shubhbhangoo/agent-firewall
6
6
  Project-URL: Repository, https://github.com/Shubhbhangoo/agent-firewall
@@ -28,6 +28,8 @@ Requires-Dist: hypothesis>=6.0; extra == "dev"
28
28
 
29
29
  Agent Firewall is built around one security boundary: **authorization remains deterministic, explicit, and fail-closed**. Identity, provenance, monitoring, behavioral analysis, simulation, evidence, and response provide security context around that boundary, but they do not become an alternative path to authorization.
30
30
 
31
+ > **v2.3** is a correctness release. It adds no subsystem and no authorization path: the work was to attack v2.2's shipped behaviour, fix the three fail-open paths that broke, stop analytical output from reading as verified when it was not, and make the strict invariant gate something CI can actually fail on.
32
+ >
31
33
  > **v2.2** makes the control plane adaptive: authority is re-evaluated when the state it rested on changes, contradictions between independent claim sources are reported rather than resolved, and the architectural properties the design rests on are checked by code instead of asserted in prose.
32
34
 
33
35
  ---
@@ -90,6 +92,76 @@ When required evidence is unavailable, verification fails, identity is unknown,
90
92
 
91
93
  ---
92
94
 
95
+ ## What v2.3 changes
96
+
97
+ v2.3 adds no subsystem. Three requests that v2.2 allowed are now denied,
98
+ each found by attacking the shipped implementation rather than by reviewing
99
+ the design.
100
+
101
+ ### Three fail-open paths closed
102
+
103
+ - **A non-finite request value satisfied every numeric bound.** Numeric
104
+ constraints are enforced by negation — admit unless `actual > expected` —
105
+ and `NaN` compares `False` against everything, so `{"amount": NaN}` passed
106
+ an `amount_max` of 100 and an `amount_min` of 10 at the same time.
107
+ `json.loads` accepts the bare tokens `NaN`, `Infinity` and `-Infinity`, so
108
+ the value arrived through ordinary request bodies and tool output. Now
109
+ `constraint_denied`.
110
+ - **The first decision taken while a configured dependency was blind
111
+ reported as `authorized`.** v2.2 gated all three revalidation paths but
112
+ not the initial decision, so a capability was allowed once and denied by
113
+ every revalidation of the same request. Now
114
+ `security_dependency_unavailable: <names>`, applied at the boundary — the
115
+ engine still returns a `(bool, reason)` pair and mints no verdict.
116
+ - **Reconfiguring a delegation budget reset the consumed total.** An
117
+ exhausted lineage's whole allowance was restored by an administrative call
118
+ that revoked, re-issued and signed nothing — and the idempotent case was
119
+ the dangerous one, since a startup path re-applying the same limit cleared
120
+ the ledger on every restart. `configure` now adjusts the ceiling and
121
+ leaves the ledger alone.
122
+
123
+ All three share one shape: **an admission must be positively established,
124
+ not inferred from the absence of a violation.** See
125
+ [`docs/v2.3-security-corrections.md`](docs/v2.3-security-corrections.md).
126
+
127
+ ### The self-attack suite
128
+
129
+ `tests/test_v2_3_self_attack.py` is 116 tests, one section per question in
130
+ the mission's final self-attack list, each attempting the attack through the
131
+ real public API. Two rules govern the file: attack through the front door,
132
+ and where the system makes no guarantee, pin the non-guarantee instead of
133
+ faking one. A completeness test maps each of the thirteen questions to its
134
+ section, so deleting one fails rather than quietly shrinking the suite. See
135
+ [`docs/v2.3-self-attack.md`](docs/v2.3-self-attack.md).
136
+
137
+ ### A strict invariant gate that can pass
138
+
139
+ `python -m firewall.invariants --strict` exited 2 on every invocation,
140
+ because five of the eleven invariants are claims about live state that a
141
+ source-only run never reaches. A gate that always fails is a gate that gets
142
+ removed, so those five were effectively ungated in CI.
143
+ `firewall/invariants/exercise.py` builds the canonical estate through the
144
+ SDK's public API only, and CI now runs the source-only and exercised gates
145
+ as separate steps. What a green exercised run establishes is bounded to that
146
+ estate, and the printed output says so. See
147
+ [`docs/v2.3-invariant-gate.md`](docs/v2.3-invariant-gate.md).
148
+
149
+ ### One name, one guarantee
150
+
151
+ Three renames separate a cryptographic result from an analytical one that
152
+ shared its name — `deception.ClaimIntegrityReport`,
153
+ `security_memory.EvidenceCheckpoint`, and
154
+ `AgentSecurityProfile.finding_score`. The third mattered most:
155
+ `MeshState.trust_score` is 0.0 when identity could not be verified, while
156
+ the profile's was 1.0 until something was found, so wiring the profile into
157
+ the mesh's `trust_provider` would have delivered an unchecked agent as fully
158
+ trusted. `firewall.correlation` is deleted; coordination detection moved to
159
+ `firewall.intel`, where every finding carries supporting facts, a rationale
160
+ and `basis="inferred"`. See
161
+ [`docs/v2.3-migration.md`](docs/v2.3-migration.md).
162
+
163
+ ---
164
+
93
165
  ## What v2.2 adds
94
166
 
95
167
  ### Continuous authorization
@@ -358,7 +430,9 @@ The architecture is designed around explicit security invariants.
358
430
  - **A check that could not run is not a check that passed.**
359
431
  - **Security failures default toward refusal rather than implicit trust.**
360
432
 
361
- These are implementation properties of the system, not a claim that any deployment is universally secure. Eleven such properties are additionally stated once in `firewall.invariants` and checked by code rather than asserted in prose alone; see [`docs/v2.2-invariants.md`](docs/v2.2-invariants.md).
433
+ These are implementation properties of the system, not a claim that any deployment is universally secure. Eleven such properties are additionally stated once in `firewall.invariants` and checked by code rather than asserted in prose alone; see [`docs/v2.2-invariants.md`](docs/v2.2-invariants.md) for the invariants themselves and [`docs/v2.3-invariant-gate.md`](docs/v2.3-invariant-gate.md) for what a green gate run does and does not establish.
434
+
435
+ Where a property does **not** hold, it is stated rather than left to be inferred. A posture change is detected but does not by itself flip a verdict; `retire_key` is not containment for a stolen key, since a retired key's signatures keep verifying so that rotation does not invalidate capabilities in flight; an `amount_max` ceiling is per request, so two siblings each holding one can spend it twice unless a lineage budget is configured; and possession of a trusted signing key is authority, which no cryptography can undo. [`docs/v2.3-self-attack.md`](docs/v2.3-self-attack.md) records each of these against the test that pins it.
362
436
 
363
437
  ---
364
438
 
@@ -394,7 +468,7 @@ Verification distinguishes states including `verified`, `failed`, `unverifiable`
394
468
  Python 3.10+ is required.
395
469
 
396
470
  ```bash
397
- pip install agent-firewall-security==2.2.0
471
+ pip install agent-firewall-security==2.3.0
398
472
  ```
399
473
 
400
474
  Development installation:
@@ -442,6 +516,11 @@ The authorization path evaluates the security conditions required by the capabil
442
516
 
443
517
  ## Command surface
444
518
 
519
+ v2.3 adds no new CLI subcommands. It adds one flag to the invariant
520
+ checker: `python -m firewall.invariants --exercise --strict` builds the
521
+ canonical estate so that all eleven invariants can be reached, which makes
522
+ `--strict` a gate that can pass and is therefore worth failing.
523
+
445
524
  v2.2 adds no new CLI subcommands. Its one new entry point is the invariant
446
525
  checker, `python -m firewall.invariants`, shown above.
447
526
 
@@ -492,6 +571,21 @@ python -m firewall.benchmarks
492
571
 
493
572
  The repository contains unit, integration, adversarial, hardening, evidence, UI/API, benchmark, and research tests.
494
573
 
574
+ The v2.3 test surface adds the thirteen-question self-attack suite, which
575
+ covers, among other properties:
576
+
577
+ - a non-finite request value satisfying no numeric ceiling or floor
578
+ - the first decision under a blind dependency being withheld, and agreeing
579
+ with its own revalidations
580
+ - a delegation budget's consumed total surviving reconfiguration
581
+ - erasing or re-pointing the delegation registry failing closed against the
582
+ child's own signature
583
+ - naming an issuer as trusted not importing its keys
584
+ - `revoke_issuer` containing a compromised signer where `retire_key` does not
585
+ - no gate in the firewall reading the untrusted-data taint marker, because a
586
+ type and a signature are the barrier rather than a filter
587
+ - the degradation subtraction being unable to turn a denial into an allow
588
+
495
589
  The v2.2 test surface covers, among other properties:
496
590
 
497
591
  - capability attenuation
@@ -603,6 +697,10 @@ See [`SECURITY.md`](SECURITY.md) for the project's security reporting policy.
603
697
 
604
698
  Detailed specifications are maintained in the repository:
605
699
 
700
+ - `docs/v2.3-security-corrections.md`
701
+ - `docs/v2.3-self-attack.md`
702
+ - `docs/v2.3-invariant-gate.md`
703
+ - `docs/v2.3-migration.md`
606
704
  - `docs/v2.2-architecture.md`
607
705
  - `docs/v2.2-security-model.md`
608
706
  - `docs/v2.2-threat-model.md`
@@ -71,7 +71,6 @@ firewall/continuous_auth/__init__.py
71
71
  firewall/continuous_auth/engine.py
72
72
  firewall/continuous_auth/monitor.py
73
73
  firewall/continuous_auth/predicates.py
74
- firewall/correlation/__init__.py
75
74
  firewall/deception/__init__.py
76
75
  firewall/defense/__init__.py
77
76
  firewall/defense/mesh.py
@@ -88,6 +87,7 @@ firewall/intel/__init__.py
88
87
  firewall/intel/engine.py
89
88
  firewall/invariants/__init__.py
90
89
  firewall/invariants/__main__.py
90
+ firewall/invariants/exercise.py
91
91
  firewall/invariants/model.py
92
92
  firewall/invariants/registry.py
93
93
  firewall/invariants/runtime.py
@@ -181,4 +181,9 @@ tests/test_v2_2_deception_integrity.py
181
181
  tests/test_v2_2_evidence_integrity.py
182
182
  tests/test_v2_2_invariants.py
183
183
  tests/test_v2_2_platform_provenance.py
184
- tests/test_v2_2_security_memory.py
184
+ tests/test_v2_2_security_memory.py
185
+ tests/test_v2_3_duplicate_concepts.py
186
+ tests/test_v2_3_intel_coordination.py
187
+ tests/test_v2_3_invariant_gate.py
188
+ tests/test_v2_3_policy_analysis.py
189
+ tests/test_v2_3_self_attack.py
@@ -43,12 +43,12 @@ Every check that raises produces a ``DiscrepancyType.UNKNOWN`` signal at
43
43
  ``unknown`` provenance plus a gap naming the check. The previous version
44
44
  wrapped six of these in ``except Exception: pass``, so a registry that
45
45
  threw produced a profile that looked exactly like a clean one: no
46
- signals, an empty capability list, ``trust_score`` 1.0 and
46
+ signals, an empty capability list, ``finding_score`` 1.0 and
47
47
  ``risk_level`` ``"low"``.
48
48
 
49
49
  Risk is triage, not authority
50
50
  -----------------------------
51
- :attr:`AgentSecurityProfile.trust_score` and
51
+ :attr:`AgentSecurityProfile.finding_score` and
52
52
  :attr:`AgentSecurityProfile.risk_level` order findings for a human or a
53
53
  containment operator. They are **not** an authorization input.
54
54
  ``FirewallSDK.authorize`` does not read them, and nothing in this module
@@ -164,10 +164,10 @@ class DiscrepancyType(str, Enum):
164
164
  #: PROVENANCE_INTEGRITY in :mod:`firewall.invariants` reports any such
165
165
  #: duplicate.
166
166
  #:
167
- #: It is an alias rather than a deletion because ``firewall.correlation``
168
- #: and ``firewall.deception`` import ``ProvenanceLevel`` from here, and
169
- #: the point of the change is to collapse the two definitions onto one
170
- #: object, not to rename anything. ``ProvenanceLevel is Provenance`` now
167
+ #: It is an alias rather than a deletion because ``firewall.deception``
168
+ #: imports ``ProvenanceLevel`` from here, and the point of the change is
169
+ #: to collapse the two definitions onto one object, not to rename
170
+ #: anything. ``ProvenanceLevel is Provenance`` now
171
171
  #: holds, so the algebra in :mod:`firewall.platform` -- ``combine``,
172
172
  #: ``coerce``, ``is_factual`` -- applies to these members directly.
173
173
  ProvenanceLevel = Provenance
@@ -280,8 +280,20 @@ class AgentSecurityProfile:
280
280
  ``gaps`` names every check that could not be performed, in words. A
281
281
  profile with gaps is never ``risk_level="low"``.
282
282
 
283
- ``trust_score`` and ``risk_level`` are triage. Nothing authorizes on
283
+ ``finding_score`` and ``risk_level`` are triage. Nothing authorizes on
284
284
  them; ``FirewallSDK.authorize`` does not read this type.
285
+
286
+ ``finding_score`` was called ``trust_score`` until v2.3. It never
287
+ measured trust: it starts at 1.0 and is multiplied down once per
288
+ finding, so an agent nothing could be checked about keeps a score near
289
+ 1.0 while ``risk_level`` reports ``"unknown"`` and ``gaps`` names what
290
+ was skipped. The old name collided with
291
+ :attr:`firewall.defense.mesh.MeshState.trust_score`, which is the
292
+ opposite convention -- a real trust score that is forced to 0.0 when
293
+ identity cannot be verified, and that the mesh compares against its
294
+ quarantine threshold. Feeding this field into that one would have
295
+ delivered an unchecked agent as fully trusted, which is why the two no
296
+ longer share a name.
285
297
  """
286
298
 
287
299
  agent_id: str
@@ -292,7 +304,7 @@ class AgentSecurityProfile:
292
304
  live_capabilities: tuple[str, ...] = ()
293
305
  delegation_depth: int = 0
294
306
  posture: str = "unknown"
295
- trust_score: float = 1.0
307
+ finding_score: float = 1.0
296
308
  risk_level: str = "unknown"
297
309
  gaps: tuple[str, ...] = ()
298
310
  last_evaluated: float = 0.0
@@ -300,8 +312,8 @@ class AgentSecurityProfile:
300
312
  def __post_init__(self) -> None:
301
313
  if self.risk_level not in RISK_LEVELS:
302
314
  raise ValueError(f"unknown risk level: {self.risk_level!r}")
303
- if not 0.0 <= float(self.trust_score) <= 1.0:
304
- raise ValueError("trust_score must be within [0.0, 1.0]")
315
+ if not 0.0 <= float(self.finding_score) <= 1.0:
316
+ raise ValueError("finding_score must be within [0.0, 1.0]")
305
317
 
306
318
  @property
307
319
  def factual_signals(self) -> tuple[SecuritySignal, ...]:
@@ -319,7 +331,7 @@ class AgentSecurityProfile:
319
331
  "live_capabilities": list(self.live_capabilities),
320
332
  "delegation_depth": self.delegation_depth,
321
333
  "posture": self.posture,
322
- "trust_score": self.trust_score,
334
+ "finding_score": self.finding_score,
323
335
  "risk_level": self.risk_level,
324
336
  "gaps": list(self.gaps),
325
337
  "last_evaluated": self.last_evaluated,
@@ -331,7 +343,7 @@ def assess_risk(
331
343
  *,
332
344
  gaps: tuple[str, ...] | list[str] = (),
333
345
  ) -> tuple[float, str]:
334
- """Derive ``(trust_score, risk_level)`` from signals and gaps.
346
+ """Derive ``(finding_score, risk_level)`` from signals and gaps.
335
347
 
336
348
  A pure function so the derivation can be tested without building a
337
349
  control plane, and so there is one place where the ordering rules
@@ -342,8 +354,8 @@ def assess_risk(
342
354
  *Unknown is not trusted.* It weighted three discrepancy types and
343
355
  ignored the other fifteen, so an unknown identity, an untrusted
344
356
  issuer, an expired capability, a revoked delegation ancestor and a
345
- task belonging to somebody else all left ``trust_score`` at 1.0 and
346
- ``risk_level`` at ``"low"``. ``DELEGATION_WIDENING`` was one of the
357
+ task belonging to somebody else all left ``finding_score`` at 1.0
358
+ and ``risk_level`` at ``"low"``. ``DELEGATION_WIDENING`` was one of the
347
359
  three it did weight, and nothing emitted it.
348
360
 
349
361
  *A gap is not a pass.* ``risk_level`` is never ``"low"`` while any
@@ -406,7 +418,7 @@ def assess_risk(
406
418
  return score, level
407
419
 
408
420
 
409
- #: How far one factual finding moves ``trust_score``. Multiplicative, so
421
+ #: How far one factual finding moves ``finding_score``. Multiplicative, so
410
422
  #: independent findings compound rather than saturating at the worst one.
411
423
  _SIGNAL_WEIGHTS: dict[DiscrepancyType, float] = {
412
424
  DiscrepancyType.IDENTITY_MISMATCH: 0.2,
@@ -551,7 +563,9 @@ class AdversarialAgentDefense:
551
563
  )
552
564
  gaps.extend(fact_gaps)
553
565
 
554
- trust_score, risk_level = assess_risk(tuple(signals), gaps=tuple(gaps))
566
+ finding_score, risk_level = assess_risk(
567
+ tuple(signals), gaps=tuple(gaps)
568
+ )
555
569
 
556
570
  profile = AgentSecurityProfile(
557
571
  agent_id=agent_id,
@@ -562,7 +576,7 @@ class AdversarialAgentDefense:
562
576
  live_capabilities=facts["live_capabilities"],
563
577
  delegation_depth=facts["delegation_depth"],
564
578
  posture=facts["posture"],
565
- trust_score=trust_score,
579
+ finding_score=finding_score,
566
580
  risk_level=risk_level,
567
581
  gaps=tuple(gaps),
568
582
  last_evaluated=timestamp,
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import math
3
4
  from typing import Optional
4
5
 
5
6
  from firewall.capability import (
@@ -251,6 +252,21 @@ def _check_constraints(
251
252
  ):
252
253
  return False
253
254
 
255
+ # A ceiling here is enforced by its negation: the request is
256
+ # admitted unless ``actual > expected``. NaN compares False
257
+ # against every bound, so ``nan > 100`` is False and a NaN
258
+ # request value would satisfy every ``_max`` ceiling -- and
259
+ # ``nan < 10`` is False too, so it would satisfy every
260
+ # ``_min`` floor. A value that cannot be ordered cannot be
261
+ # shown to be within the bound, and unknown is not trusted.
262
+ #
263
+ # ``json.loads`` accepts the bare tokens ``NaN``,
264
+ # ``Infinity`` and ``-Infinity`` by default, so this reaches
265
+ # the gate from any JSON request body or tool output without
266
+ # the caller doing anything unusual.
267
+ if not math.isfinite(actual):
268
+ return False
269
+
254
270
  if key.endswith(
255
271
  "_max"
256
272
  ):