agent-firewall-security 2.2.0__tar.gz → 2.4.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.
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/PKG-INFO +232 -3
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/README.md +231 -2
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/agent_firewall_security.egg-info/PKG-INFO +232 -3
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/agent_firewall_security.egg-info/SOURCES.txt +28 -2
- agent_firewall_security-2.4.0/firewall/a2a/__init__.py +34 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/a2a/auth.py +81 -4
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/adapters/anthropic.py +57 -46
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/adapters/generic.py +60 -46
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/adapters/openai.py +57 -46
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/adversarial/__init__.py +31 -17
- agent_firewall_security-2.4.0/firewall/aegis/__init__.py +209 -0
- agent_firewall_security-2.4.0/firewall/aegis/blast.py +450 -0
- agent_firewall_security-2.4.0/firewall/aegis/controller.py +1177 -0
- agent_firewall_security-2.4.0/firewall/aegis/decay.py +267 -0
- agent_firewall_security-2.4.0/firewall/aegis/envelope.py +979 -0
- agent_firewall_security-2.4.0/firewall/aegis/explain.py +451 -0
- agent_firewall_security-2.4.0/firewall/aegis/preflight.py +614 -0
- agent_firewall_security-2.4.0/firewall/aegis/response.py +557 -0
- agent_firewall_security-2.4.0/firewall/aegis/restriction.py +523 -0
- agent_firewall_security-2.4.0/firewall/aegis/state.py +384 -0
- agent_firewall_security-2.4.0/firewall/attenuation.py +134 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/authorization.py +37 -0
- agent_firewall_security-2.4.0/firewall/benchmarks.py +1191 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/cli_v21.py +20 -4
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/continuous_auth/engine.py +20 -1
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/deception/__init__.py +28 -4
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/defense/mesh.py +20 -1
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/delegation.py +14 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/delegation_budget.py +73 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/evidence_integrity/__init__.py +6 -6
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/immune/engine.py +14 -3
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/intel/__init__.py +2 -0
- agent_firewall_security-2.4.0/firewall/intel/engine.py +875 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/invariants/__init__.py +28 -2
- agent_firewall_security-2.4.0/firewall/invariants/__main__.py +163 -0
- agent_firewall_security-2.4.0/firewall/invariants/exercise.py +399 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/invariants/registry.py +74 -9
- agent_firewall_security-2.4.0/firewall/invariants/runtime.py +2683 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/invariants/source.py +1 -1
- agent_firewall_security-2.4.0/firewall/policy_analysis/__init__.py +799 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/recorder/checkpoint.py +12 -1
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/sdk.py +520 -1
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/security_memory/__init__.py +26 -13
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/ui/introspect.py +43 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/pyproject.toml +1 -1
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/tests/test_v2_2_adversarial_defense.py +28 -4
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/tests/test_v2_2_deception_integrity.py +2 -2
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/tests/test_v2_2_evidence_integrity.py +6 -6
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/tests/test_v2_2_invariants.py +56 -90
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/tests/test_v2_2_security_memory.py +4 -4
- agent_firewall_security-2.4.0/tests/test_v2_3_duplicate_concepts.py +412 -0
- agent_firewall_security-2.4.0/tests/test_v2_3_intel_coordination.py +465 -0
- agent_firewall_security-2.4.0/tests/test_v2_3_invariant_gate.py +436 -0
- agent_firewall_security-2.4.0/tests/test_v2_3_policy_analysis.py +652 -0
- agent_firewall_security-2.4.0/tests/test_v2_3_self_attack.py +2686 -0
- agent_firewall_security-2.4.0/tests/test_v2_4_aegis_adversarial.py +1468 -0
- agent_firewall_security-2.4.0/tests/test_v2_4_aegis_concurrency.py +1155 -0
- agent_firewall_security-2.4.0/tests/test_v2_4_aegis_controller.py +1450 -0
- agent_firewall_security-2.4.0/tests/test_v2_4_aegis_corrections.py +477 -0
- agent_firewall_security-2.4.0/tests/test_v2_4_aegis_fuzz.py +949 -0
- agent_firewall_security-2.4.0/tests/test_v2_4_aegis_gate.py +504 -0
- agent_firewall_security-2.4.0/tests/test_v2_4_aegis_state.py +633 -0
- agent_firewall_security-2.4.0/tests/test_v2_4_attenuation_narrowing.py +369 -0
- agent_firewall_security-2.4.0/tests/test_v2_4_benchmarks.py +337 -0
- agent_firewall_security-2.4.0/tests/test_v2_4_envelope_soundness.py +154 -0
- agent_firewall_security-2.4.0/tests/test_v2_4_integration_boundary.py +979 -0
- agent_firewall_security-2.2.0/firewall/a2a/__init__.py +0 -21
- agent_firewall_security-2.2.0/firewall/attenuation.py +0 -159
- agent_firewall_security-2.2.0/firewall/benchmarks.py +0 -295
- agent_firewall_security-2.2.0/firewall/correlation/__init__.py +0 -552
- agent_firewall_security-2.2.0/firewall/intel/engine.py +0 -495
- agent_firewall_security-2.2.0/firewall/invariants/__main__.py +0 -121
- agent_firewall_security-2.2.0/firewall/invariants/runtime.py +0 -1173
- agent_firewall_security-2.2.0/firewall/policy_analysis/__init__.py +0 -409
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/agent_firewall_security.egg-info/dependency_links.txt +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/agent_firewall_security.egg-info/entry_points.txt +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/agent_firewall_security.egg-info/requires.txt +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/agent_firewall_security.egg-info/top_level.txt +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/adapters/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/adapters/normalize.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/agents/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/agents/adapters.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/agents/base.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/artifact/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/artifact/reader.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/artifact/writer.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/attackgraph/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/attackgraph/engine.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/attest/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/attest/authority.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/capability.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/capability2/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/capability2/constraints.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/cli.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/cli_v19.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/cli_v20.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/containment/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/containment/controller.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/continuous_auth/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/continuous_auth/monitor.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/continuous_auth/predicates.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/defense/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/delegation_lineage.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/delegation_store.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/engine.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/evidence.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/evidence_graph/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/evidence_graph/graph.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/explain.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/http.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/ident/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/ident/registry.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/identity.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/immune/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/incident/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/incident/package.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/invariants/model.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/invariants/static.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/key_management.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/key_store.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/lab/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/lab/engine.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/lifecycle.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/lifecycle_store.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/mcp.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/namespace.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/network/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/network/attack_path.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/network/behavior.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/network/correlation.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/network/graph.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/network/model.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/network/response.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/network/simulator.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/network/state.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/north_star.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/passport/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/passport/builder.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/platform/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/platform/provenance.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/policy.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/posture/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/posture/engine.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/protect.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/provenance/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/provenance/registry.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/recorder/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/recorder/encoding.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/recorder/events.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/recorder/identity.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/recorder/recorder.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/recorder/redact.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/refusal_state.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/replay.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/replay_store.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/replaylab/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/replaylab/laboratory.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/research/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/research/lab.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/response2/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/response2/adaptive.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/revocation.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/revocation_store.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/risk_context.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/security_context.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/security_decision.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/semantic_chain.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/simulation/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/simulation/case.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/simulation/recorder.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/simulation/replay.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/simulation/report.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/simulation/rollout.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/simulation/ruleset.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/task/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/task/registry.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/timeline/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/timeline/graph.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/timeline/timeline.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/timeline/trajectory.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/tools.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/transport.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/trust/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/trust/graph.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/twin/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/twin/adversarial.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/twin/twin.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/ui/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/ui/__main__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/ui/control.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/ui/demo.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/ui/server.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/ui/service.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/ui/static/console.css +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/ui/static/console.js +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/ui/static/control.js +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/ui/static/index.html +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/ui/static/recorder.css +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/ui/static/recorder.js +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/ui/static/soc.css +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/ui/static/soc.js +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/ui/static/v20.css +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/ui/static/v20.js +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/ui/static/v21.css +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/ui/static/v21.js +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/ui/v18.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/ui/v19.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/ui/v20.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/ui/v21.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/verify/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/firewall/verify/verifier.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/setup.cfg +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/tests/test_continuous_auth.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/tests/test_engine.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/tests/test_sdk_continuous_auth.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/tests/test_security_decision.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/tests/test_v2_2_adversarial_twin.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.0}/tests/test_v2_2_capability_delegation.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.4.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.
|
|
3
|
+
Version: 2.4.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,10 @@ 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.4** adds **Aegis**, an adaptive authority control plane: a live grant can be narrowed, suspended, revalidated or revoked while a task is running. It adds no second authorization path — Aegis reaches `FirewallSDK.authorize()` through one gate that can only deny or abstain, and learns what happened through one callback the SDK invokes *after* the decision exists.
|
|
32
|
+
>
|
|
33
|
+
> **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.
|
|
34
|
+
>
|
|
31
35
|
> **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
36
|
|
|
33
37
|
---
|
|
@@ -90,6 +94,166 @@ When required evidence is unavailable, verification fails, identity is unknown,
|
|
|
90
94
|
|
|
91
95
|
---
|
|
92
96
|
|
|
97
|
+
## What v2.4 adds
|
|
98
|
+
|
|
99
|
+
v2.2 made the platform adaptive *above* the boundary: when watched state
|
|
100
|
+
changed, a decision was re-run. v2.4 makes **authority itself** adaptive. A
|
|
101
|
+
grant that is already in use can be narrowed, suspended, revalidated or
|
|
102
|
+
revoked, and the change is enforced on the next authorization — and, for
|
|
103
|
+
suspension, inside the commit transaction of one already in flight.
|
|
104
|
+
|
|
105
|
+
Aegis is off by default. `FirewallSDK(aegis_enabled=True)` opts in; with no
|
|
106
|
+
controller attached the adaptive gate abstains and v2.3 behaviour is exact.
|
|
107
|
+
|
|
108
|
+
### One deny-only seam
|
|
109
|
+
|
|
110
|
+
Nine modules, 5,382 lines, none of which imports `firewall.sdk`. Everything
|
|
111
|
+
Aegis does reaches the outside world through two points: `_gate_aegis`, the
|
|
112
|
+
ninth of eleven gates, which can only deny or abstain; and
|
|
113
|
+
`observe_authorization`, which the SDK calls *after* a decision exists.
|
|
114
|
+
Because the callback runs after, no Aegis state can be a precondition of the
|
|
115
|
+
allow it observes. Authority flows from the boundary into Aegis and never the
|
|
116
|
+
other way.
|
|
117
|
+
|
|
118
|
+
```text
|
|
119
|
+
change -> classify -> KEEP | REVALIDATE | NARROW | SUSPEND | REVOKE
|
|
120
|
+
|
|
|
121
|
+
v
|
|
122
|
+
restriction written
|
|
123
|
+
|
|
|
124
|
+
v
|
|
125
|
+
FirewallSDK.authorize() -> _gate_aegis -> DENY
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
There is no arrow from Aegis to ALLOW. That absence is the design.
|
|
129
|
+
|
|
130
|
+
### The authority envelope
|
|
131
|
+
|
|
132
|
+
An `AuthorityEnvelope` is a bound on what a capability may still do, folded
|
|
133
|
+
across its delegation chain by a per-dimension greatest-lower-bound. Twelve
|
|
134
|
+
fields — patterns, tool, time window, constraint bounds, depth, its ceiling,
|
|
135
|
+
issuers, issuer trust, revocation, budget, chain membership, and a bottom
|
|
136
|
+
marker — and every dimension that bounds a request has a named enforcement
|
|
137
|
+
site at the boundary. A dimension with no enforcement site would be
|
|
138
|
+
decoration.
|
|
139
|
+
|
|
140
|
+
The theorem is one-directional and stated that way: if the envelope
|
|
141
|
+
**excludes** a request, the boundary denies it. The converse does not hold,
|
|
142
|
+
and the API is named so that misuse reads wrong — `excludes()` returns a
|
|
143
|
+
reason, `may_admit()` means "this envelope does not itself refuse", and
|
|
144
|
+
`__bool__` **raises** on an envelope, a grant, a preflight, a blast radius
|
|
145
|
+
and a classification, so `if preflight(...)` is an error rather than an
|
|
146
|
+
accidental allow.
|
|
147
|
+
|
|
148
|
+
### Seven states ordered by residual authority
|
|
149
|
+
|
|
150
|
+
`ISSUED`, `ACTIVE`, `REVALIDATING`, `NARROWED`, `SUSPENDED`, `REVOKED`,
|
|
151
|
+
`EXPIRED`. A transition is legal only if it does not increase residual
|
|
152
|
+
authority. `REVALIDATING -> ACTIVE` is the only edge that restores authority,
|
|
153
|
+
and it requires an `AuthorizationResult` that is allowed, reasoned
|
|
154
|
+
`authorized`, and traced to that capability's fingerprint. `REVOKED` and
|
|
155
|
+
`EXPIRED` are terminal and checked before the ordering rule, so no evidence
|
|
156
|
+
or clock change produces an edge out of either.
|
|
157
|
+
|
|
158
|
+
The state machine is a record and a legality check. It is deliberately **not**
|
|
159
|
+
an enforcement channel: the gate reads restrictions, not states, because
|
|
160
|
+
wiring the state model into the gate would make the authorization path depend
|
|
161
|
+
on a structure whose own updates require an authorization result.
|
|
162
|
+
|
|
163
|
+
### Unknown never becomes safe
|
|
164
|
+
|
|
165
|
+
An unrecognised trigger is `REVALIDATE`, not `KEEP` (which would make an
|
|
166
|
+
unknown event benign) and not `REVOKE` (which would make any unknown string a
|
|
167
|
+
denial-of-service lever). Nothing maps to `KEEP` at all — it is reachable
|
|
168
|
+
only by establishing five positive conditions, so "nothing changed" must be
|
|
169
|
+
shown rather than assumed. An unreadable budget is exhausted, not unlimited;
|
|
170
|
+
an unreadable restriction matches; unestablished issuer trust is `None`, not
|
|
171
|
+
`True`; an unresolvable chain yields the bottom envelope; a traversal that
|
|
172
|
+
exceeds its bounds is `UNANALYZABLE` rather than a partial answer presented
|
|
173
|
+
as complete. `UNKNOWN_NON_AUTHORIZATION` checks these exhaustively.
|
|
174
|
+
|
|
175
|
+
### What it does not claim
|
|
176
|
+
|
|
177
|
+
Envelope soundness runs in one direction. `canonical_allow_for` is a
|
|
178
|
+
structural check, not a cryptographic one — it bounds mistakes, not an
|
|
179
|
+
adversary already inside the process. The commit-time re-read covers
|
|
180
|
+
suspension only. The restriction cap trades availability for integrity:
|
|
181
|
+
sixteen narrowings drive a grant to `SUSPEND`, which is fail-closed and still
|
|
182
|
+
a lever. §16 of [`docs/v2.4-aegis.md`](docs/v2.4-aegis.md) is the full list,
|
|
183
|
+
and §17 maps each guarantee to the test file that establishes it.
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## What v2.3 changes
|
|
188
|
+
|
|
189
|
+
v2.3 adds no subsystem. Three requests that v2.2 allowed are now denied,
|
|
190
|
+
each found by attacking the shipped implementation rather than by reviewing
|
|
191
|
+
the design.
|
|
192
|
+
|
|
193
|
+
### Three fail-open paths closed
|
|
194
|
+
|
|
195
|
+
- **A non-finite request value satisfied every numeric bound.** Numeric
|
|
196
|
+
constraints are enforced by negation — admit unless `actual > expected` —
|
|
197
|
+
and `NaN` compares `False` against everything, so `{"amount": NaN}` passed
|
|
198
|
+
an `amount_max` of 100 and an `amount_min` of 10 at the same time.
|
|
199
|
+
`json.loads` accepts the bare tokens `NaN`, `Infinity` and `-Infinity`, so
|
|
200
|
+
the value arrived through ordinary request bodies and tool output. Now
|
|
201
|
+
`constraint_denied`.
|
|
202
|
+
- **The first decision taken while a configured dependency was blind
|
|
203
|
+
reported as `authorized`.** v2.2 gated all three revalidation paths but
|
|
204
|
+
not the initial decision, so a capability was allowed once and denied by
|
|
205
|
+
every revalidation of the same request. Now
|
|
206
|
+
`security_dependency_unavailable: <names>`, applied at the boundary — the
|
|
207
|
+
engine still returns a `(bool, reason)` pair and mints no verdict.
|
|
208
|
+
- **Reconfiguring a delegation budget reset the consumed total.** An
|
|
209
|
+
exhausted lineage's whole allowance was restored by an administrative call
|
|
210
|
+
that revoked, re-issued and signed nothing — and the idempotent case was
|
|
211
|
+
the dangerous one, since a startup path re-applying the same limit cleared
|
|
212
|
+
the ledger on every restart. `configure` now adjusts the ceiling and
|
|
213
|
+
leaves the ledger alone.
|
|
214
|
+
|
|
215
|
+
All three share one shape: **an admission must be positively established,
|
|
216
|
+
not inferred from the absence of a violation.** See
|
|
217
|
+
[`docs/v2.3-security-corrections.md`](docs/v2.3-security-corrections.md).
|
|
218
|
+
|
|
219
|
+
### The self-attack suite
|
|
220
|
+
|
|
221
|
+
`tests/test_v2_3_self_attack.py` is 116 tests, one section per question in
|
|
222
|
+
the mission's final self-attack list, each attempting the attack through the
|
|
223
|
+
real public API. Two rules govern the file: attack through the front door,
|
|
224
|
+
and where the system makes no guarantee, pin the non-guarantee instead of
|
|
225
|
+
faking one. A completeness test maps each of the thirteen questions to its
|
|
226
|
+
section, so deleting one fails rather than quietly shrinking the suite. See
|
|
227
|
+
[`docs/v2.3-self-attack.md`](docs/v2.3-self-attack.md).
|
|
228
|
+
|
|
229
|
+
### A strict invariant gate that can pass
|
|
230
|
+
|
|
231
|
+
`python -m firewall.invariants --strict` exited 2 on every invocation,
|
|
232
|
+
because seven of the fifteen invariants are claims about live state that a
|
|
233
|
+
source-only run never reaches. A gate that always fails is a gate that gets
|
|
234
|
+
removed, so those seven were effectively ungated in CI.
|
|
235
|
+
`firewall/invariants/exercise.py` builds the canonical estate through the
|
|
236
|
+
SDK's public API only, and CI now runs the source-only and exercised gates
|
|
237
|
+
as separate steps. What a green exercised run establishes is bounded to that
|
|
238
|
+
estate, and the printed output says so. See
|
|
239
|
+
[`docs/v2.3-invariant-gate.md`](docs/v2.3-invariant-gate.md).
|
|
240
|
+
|
|
241
|
+
### One name, one guarantee
|
|
242
|
+
|
|
243
|
+
Three renames separate a cryptographic result from an analytical one that
|
|
244
|
+
shared its name — `deception.ClaimIntegrityReport`,
|
|
245
|
+
`security_memory.EvidenceCheckpoint`, and
|
|
246
|
+
`AgentSecurityProfile.finding_score`. The third mattered most:
|
|
247
|
+
`MeshState.trust_score` is 0.0 when identity could not be verified, while
|
|
248
|
+
the profile's was 1.0 until something was found, so wiring the profile into
|
|
249
|
+
the mesh's `trust_provider` would have delivered an unchecked agent as fully
|
|
250
|
+
trusted. `firewall.correlation` is deleted; coordination detection moved to
|
|
251
|
+
`firewall.intel`, where every finding carries supporting facts, a rationale
|
|
252
|
+
and `basis="inferred"`. See
|
|
253
|
+
[`docs/v2.3-migration.md`](docs/v2.3-migration.md).
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
93
257
|
## What v2.2 adds
|
|
94
258
|
|
|
95
259
|
### Continuous authorization
|
|
@@ -358,7 +522,9 @@ The architecture is designed around explicit security invariants.
|
|
|
358
522
|
- **A check that could not run is not a check that passed.**
|
|
359
523
|
- **Security failures default toward refusal rather than implicit trust.**
|
|
360
524
|
|
|
361
|
-
These are implementation properties of the system, not a claim that any deployment is universally secure.
|
|
525
|
+
These are implementation properties of the system, not a claim that any deployment is universally secure. Fifteen 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, [`docs/v2.3-invariant-gate.md`](docs/v2.3-invariant-gate.md) for what a green gate run does and does not establish, and [`docs/v2.4-aegis.md`](docs/v2.4-aegis.md) for the four the authority control plane adds.
|
|
526
|
+
|
|
527
|
+
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
528
|
|
|
363
529
|
---
|
|
364
530
|
|
|
@@ -394,7 +560,7 @@ Verification distinguishes states including `verified`, `failed`, `unverifiable`
|
|
|
394
560
|
Python 3.10+ is required.
|
|
395
561
|
|
|
396
562
|
```bash
|
|
397
|
-
pip install agent-firewall-security==2.
|
|
563
|
+
pip install agent-firewall-security==2.3.0
|
|
398
564
|
```
|
|
399
565
|
|
|
400
566
|
Development installation:
|
|
@@ -442,6 +608,17 @@ The authorization path evaluates the security conditions required by the capabil
|
|
|
442
608
|
|
|
443
609
|
## Command surface
|
|
444
610
|
|
|
611
|
+
v2.4 adds no new CLI subcommands. One existing command changes what it
|
|
612
|
+
prints: `firewall delegate-authorize` now labels an allow it reached without
|
|
613
|
+
an attached `sdk_provider` as `ALLOWED (relationship only)` and says not to
|
|
614
|
+
enforce on it. The exit status stays 0 — the question asked was answered
|
|
615
|
+
affirmatively — but the answer no longer overstates itself.
|
|
616
|
+
|
|
617
|
+
v2.3 adds no new CLI subcommands. It adds one flag to the invariant
|
|
618
|
+
checker: `python -m firewall.invariants --exercise --strict` builds the
|
|
619
|
+
canonical estate so that all fifteen invariants can be reached, which makes
|
|
620
|
+
`--strict` a gate that can pass and is therefore worth failing.
|
|
621
|
+
|
|
445
622
|
v2.2 adds no new CLI subcommands. Its one new entry point is the invariant
|
|
446
623
|
checker, `python -m firewall.invariants`, shown above.
|
|
447
624
|
|
|
@@ -492,6 +669,50 @@ python -m firewall.benchmarks
|
|
|
492
669
|
|
|
493
670
|
The repository contains unit, integration, adversarial, hardening, evidence, UI/API, benchmark, and research tests.
|
|
494
671
|
|
|
672
|
+
The v2.4 test surface adds 384 tests across eleven files — 4,087 in the suite
|
|
673
|
+
as a whole, on Python 3.10, 3.11 and 3.12 — including stateful security-state
|
|
674
|
+
fuzzing, eight named concurrency races, and an integration-boundary sweep.
|
|
675
|
+
Among the properties covered:
|
|
676
|
+
|
|
677
|
+
- no path from adaptive analysis to an allow, and no Aegis module able to
|
|
678
|
+
construct an `AuthorizationResult`
|
|
679
|
+
- every forbidden transition refused individually: out of `REVOKED`, out of
|
|
680
|
+
`EXPIRED`, `NARROWED` to wider, `SUSPENDED` to `ACTIVE` without a canonical
|
|
681
|
+
allow, and a child exceeding its parent
|
|
682
|
+
- an envelope that excludes a request always accompanied by a boundary that
|
|
683
|
+
denies it
|
|
684
|
+
- no field of the envelope widening under delegation or attenuation
|
|
685
|
+
- a `nan` bound bounding nothing being denied, while an `inf` bound still
|
|
686
|
+
means unbounded
|
|
687
|
+
- a 400-digit integer producing a decision rather than an exception out of
|
|
688
|
+
`authorize()`
|
|
689
|
+
- every authorization-reachable Aegis method total, including against an
|
|
690
|
+
injected hostile controller
|
|
691
|
+
- a restriction written mid-flight being observed, and a suspension being
|
|
692
|
+
caught inside the commit transaction
|
|
693
|
+
- `__bool__` raising on every analysis object, so a truthiness test cannot
|
|
694
|
+
become an allow
|
|
695
|
+
- every integration surface reaching the canonical boundary, with no local
|
|
696
|
+
allow anywhere
|
|
697
|
+
- no dataclass field in the package carrying a default that Python 3.11
|
|
698
|
+
rejects, so a defect that hides on the development interpreter cannot wait
|
|
699
|
+
for CI to find it
|
|
700
|
+
|
|
701
|
+
The v2.3 test surface adds the thirteen-question self-attack suite, which
|
|
702
|
+
covers, among other properties:
|
|
703
|
+
|
|
704
|
+
- a non-finite request value satisfying no numeric ceiling or floor
|
|
705
|
+
- the first decision under a blind dependency being withheld, and agreeing
|
|
706
|
+
with its own revalidations
|
|
707
|
+
- a delegation budget's consumed total surviving reconfiguration
|
|
708
|
+
- erasing or re-pointing the delegation registry failing closed against the
|
|
709
|
+
child's own signature
|
|
710
|
+
- naming an issuer as trusted not importing its keys
|
|
711
|
+
- `revoke_issuer` containing a compromised signer where `retire_key` does not
|
|
712
|
+
- no gate in the firewall reading the untrusted-data taint marker, because a
|
|
713
|
+
type and a signature are the barrier rather than a filter
|
|
714
|
+
- the degradation subtraction being unable to turn a denial into an allow
|
|
715
|
+
|
|
495
716
|
The v2.2 test surface covers, among other properties:
|
|
496
717
|
|
|
497
718
|
- capability attenuation
|
|
@@ -603,6 +824,14 @@ See [`SECURITY.md`](SECURITY.md) for the project's security reporting policy.
|
|
|
603
824
|
|
|
604
825
|
Detailed specifications are maintained in the repository:
|
|
605
826
|
|
|
827
|
+
- `docs/v2.4-aegis.md`
|
|
828
|
+
- `docs/v2.4-aegis-design.md`
|
|
829
|
+
- `docs/v2.4-performance.md`
|
|
830
|
+
- `docs/v2.4-migration.md`
|
|
831
|
+
- `docs/v2.3-security-corrections.md`
|
|
832
|
+
- `docs/v2.3-self-attack.md`
|
|
833
|
+
- `docs/v2.3-invariant-gate.md`
|
|
834
|
+
- `docs/v2.3-migration.md`
|
|
606
835
|
- `docs/v2.2-architecture.md`
|
|
607
836
|
- `docs/v2.2-security-model.md`
|
|
608
837
|
- `docs/v2.2-threat-model.md`
|
|
@@ -4,6 +4,10 @@
|
|
|
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.4** adds **Aegis**, an adaptive authority control plane: a live grant can be narrowed, suspended, revalidated or revoked while a task is running. It adds no second authorization path — Aegis reaches `FirewallSDK.authorize()` through one gate that can only deny or abstain, and learns what happened through one callback the SDK invokes *after* the decision exists.
|
|
8
|
+
>
|
|
9
|
+
> **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.
|
|
10
|
+
>
|
|
7
11
|
> **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
12
|
|
|
9
13
|
---
|
|
@@ -66,6 +70,166 @@ When required evidence is unavailable, verification fails, identity is unknown,
|
|
|
66
70
|
|
|
67
71
|
---
|
|
68
72
|
|
|
73
|
+
## What v2.4 adds
|
|
74
|
+
|
|
75
|
+
v2.2 made the platform adaptive *above* the boundary: when watched state
|
|
76
|
+
changed, a decision was re-run. v2.4 makes **authority itself** adaptive. A
|
|
77
|
+
grant that is already in use can be narrowed, suspended, revalidated or
|
|
78
|
+
revoked, and the change is enforced on the next authorization — and, for
|
|
79
|
+
suspension, inside the commit transaction of one already in flight.
|
|
80
|
+
|
|
81
|
+
Aegis is off by default. `FirewallSDK(aegis_enabled=True)` opts in; with no
|
|
82
|
+
controller attached the adaptive gate abstains and v2.3 behaviour is exact.
|
|
83
|
+
|
|
84
|
+
### One deny-only seam
|
|
85
|
+
|
|
86
|
+
Nine modules, 5,382 lines, none of which imports `firewall.sdk`. Everything
|
|
87
|
+
Aegis does reaches the outside world through two points: `_gate_aegis`, the
|
|
88
|
+
ninth of eleven gates, which can only deny or abstain; and
|
|
89
|
+
`observe_authorization`, which the SDK calls *after* a decision exists.
|
|
90
|
+
Because the callback runs after, no Aegis state can be a precondition of the
|
|
91
|
+
allow it observes. Authority flows from the boundary into Aegis and never the
|
|
92
|
+
other way.
|
|
93
|
+
|
|
94
|
+
```text
|
|
95
|
+
change -> classify -> KEEP | REVALIDATE | NARROW | SUSPEND | REVOKE
|
|
96
|
+
|
|
|
97
|
+
v
|
|
98
|
+
restriction written
|
|
99
|
+
|
|
|
100
|
+
v
|
|
101
|
+
FirewallSDK.authorize() -> _gate_aegis -> DENY
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
There is no arrow from Aegis to ALLOW. That absence is the design.
|
|
105
|
+
|
|
106
|
+
### The authority envelope
|
|
107
|
+
|
|
108
|
+
An `AuthorityEnvelope` is a bound on what a capability may still do, folded
|
|
109
|
+
across its delegation chain by a per-dimension greatest-lower-bound. Twelve
|
|
110
|
+
fields — patterns, tool, time window, constraint bounds, depth, its ceiling,
|
|
111
|
+
issuers, issuer trust, revocation, budget, chain membership, and a bottom
|
|
112
|
+
marker — and every dimension that bounds a request has a named enforcement
|
|
113
|
+
site at the boundary. A dimension with no enforcement site would be
|
|
114
|
+
decoration.
|
|
115
|
+
|
|
116
|
+
The theorem is one-directional and stated that way: if the envelope
|
|
117
|
+
**excludes** a request, the boundary denies it. The converse does not hold,
|
|
118
|
+
and the API is named so that misuse reads wrong — `excludes()` returns a
|
|
119
|
+
reason, `may_admit()` means "this envelope does not itself refuse", and
|
|
120
|
+
`__bool__` **raises** on an envelope, a grant, a preflight, a blast radius
|
|
121
|
+
and a classification, so `if preflight(...)` is an error rather than an
|
|
122
|
+
accidental allow.
|
|
123
|
+
|
|
124
|
+
### Seven states ordered by residual authority
|
|
125
|
+
|
|
126
|
+
`ISSUED`, `ACTIVE`, `REVALIDATING`, `NARROWED`, `SUSPENDED`, `REVOKED`,
|
|
127
|
+
`EXPIRED`. A transition is legal only if it does not increase residual
|
|
128
|
+
authority. `REVALIDATING -> ACTIVE` is the only edge that restores authority,
|
|
129
|
+
and it requires an `AuthorizationResult` that is allowed, reasoned
|
|
130
|
+
`authorized`, and traced to that capability's fingerprint. `REVOKED` and
|
|
131
|
+
`EXPIRED` are terminal and checked before the ordering rule, so no evidence
|
|
132
|
+
or clock change produces an edge out of either.
|
|
133
|
+
|
|
134
|
+
The state machine is a record and a legality check. It is deliberately **not**
|
|
135
|
+
an enforcement channel: the gate reads restrictions, not states, because
|
|
136
|
+
wiring the state model into the gate would make the authorization path depend
|
|
137
|
+
on a structure whose own updates require an authorization result.
|
|
138
|
+
|
|
139
|
+
### Unknown never becomes safe
|
|
140
|
+
|
|
141
|
+
An unrecognised trigger is `REVALIDATE`, not `KEEP` (which would make an
|
|
142
|
+
unknown event benign) and not `REVOKE` (which would make any unknown string a
|
|
143
|
+
denial-of-service lever). Nothing maps to `KEEP` at all — it is reachable
|
|
144
|
+
only by establishing five positive conditions, so "nothing changed" must be
|
|
145
|
+
shown rather than assumed. An unreadable budget is exhausted, not unlimited;
|
|
146
|
+
an unreadable restriction matches; unestablished issuer trust is `None`, not
|
|
147
|
+
`True`; an unresolvable chain yields the bottom envelope; a traversal that
|
|
148
|
+
exceeds its bounds is `UNANALYZABLE` rather than a partial answer presented
|
|
149
|
+
as complete. `UNKNOWN_NON_AUTHORIZATION` checks these exhaustively.
|
|
150
|
+
|
|
151
|
+
### What it does not claim
|
|
152
|
+
|
|
153
|
+
Envelope soundness runs in one direction. `canonical_allow_for` is a
|
|
154
|
+
structural check, not a cryptographic one — it bounds mistakes, not an
|
|
155
|
+
adversary already inside the process. The commit-time re-read covers
|
|
156
|
+
suspension only. The restriction cap trades availability for integrity:
|
|
157
|
+
sixteen narrowings drive a grant to `SUSPEND`, which is fail-closed and still
|
|
158
|
+
a lever. §16 of [`docs/v2.4-aegis.md`](docs/v2.4-aegis.md) is the full list,
|
|
159
|
+
and §17 maps each guarantee to the test file that establishes it.
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## What v2.3 changes
|
|
164
|
+
|
|
165
|
+
v2.3 adds no subsystem. Three requests that v2.2 allowed are now denied,
|
|
166
|
+
each found by attacking the shipped implementation rather than by reviewing
|
|
167
|
+
the design.
|
|
168
|
+
|
|
169
|
+
### Three fail-open paths closed
|
|
170
|
+
|
|
171
|
+
- **A non-finite request value satisfied every numeric bound.** Numeric
|
|
172
|
+
constraints are enforced by negation — admit unless `actual > expected` —
|
|
173
|
+
and `NaN` compares `False` against everything, so `{"amount": NaN}` passed
|
|
174
|
+
an `amount_max` of 100 and an `amount_min` of 10 at the same time.
|
|
175
|
+
`json.loads` accepts the bare tokens `NaN`, `Infinity` and `-Infinity`, so
|
|
176
|
+
the value arrived through ordinary request bodies and tool output. Now
|
|
177
|
+
`constraint_denied`.
|
|
178
|
+
- **The first decision taken while a configured dependency was blind
|
|
179
|
+
reported as `authorized`.** v2.2 gated all three revalidation paths but
|
|
180
|
+
not the initial decision, so a capability was allowed once and denied by
|
|
181
|
+
every revalidation of the same request. Now
|
|
182
|
+
`security_dependency_unavailable: <names>`, applied at the boundary — the
|
|
183
|
+
engine still returns a `(bool, reason)` pair and mints no verdict.
|
|
184
|
+
- **Reconfiguring a delegation budget reset the consumed total.** An
|
|
185
|
+
exhausted lineage's whole allowance was restored by an administrative call
|
|
186
|
+
that revoked, re-issued and signed nothing — and the idempotent case was
|
|
187
|
+
the dangerous one, since a startup path re-applying the same limit cleared
|
|
188
|
+
the ledger on every restart. `configure` now adjusts the ceiling and
|
|
189
|
+
leaves the ledger alone.
|
|
190
|
+
|
|
191
|
+
All three share one shape: **an admission must be positively established,
|
|
192
|
+
not inferred from the absence of a violation.** See
|
|
193
|
+
[`docs/v2.3-security-corrections.md`](docs/v2.3-security-corrections.md).
|
|
194
|
+
|
|
195
|
+
### The self-attack suite
|
|
196
|
+
|
|
197
|
+
`tests/test_v2_3_self_attack.py` is 116 tests, one section per question in
|
|
198
|
+
the mission's final self-attack list, each attempting the attack through the
|
|
199
|
+
real public API. Two rules govern the file: attack through the front door,
|
|
200
|
+
and where the system makes no guarantee, pin the non-guarantee instead of
|
|
201
|
+
faking one. A completeness test maps each of the thirteen questions to its
|
|
202
|
+
section, so deleting one fails rather than quietly shrinking the suite. See
|
|
203
|
+
[`docs/v2.3-self-attack.md`](docs/v2.3-self-attack.md).
|
|
204
|
+
|
|
205
|
+
### A strict invariant gate that can pass
|
|
206
|
+
|
|
207
|
+
`python -m firewall.invariants --strict` exited 2 on every invocation,
|
|
208
|
+
because seven of the fifteen invariants are claims about live state that a
|
|
209
|
+
source-only run never reaches. A gate that always fails is a gate that gets
|
|
210
|
+
removed, so those seven were effectively ungated in CI.
|
|
211
|
+
`firewall/invariants/exercise.py` builds the canonical estate through the
|
|
212
|
+
SDK's public API only, and CI now runs the source-only and exercised gates
|
|
213
|
+
as separate steps. What a green exercised run establishes is bounded to that
|
|
214
|
+
estate, and the printed output says so. See
|
|
215
|
+
[`docs/v2.3-invariant-gate.md`](docs/v2.3-invariant-gate.md).
|
|
216
|
+
|
|
217
|
+
### One name, one guarantee
|
|
218
|
+
|
|
219
|
+
Three renames separate a cryptographic result from an analytical one that
|
|
220
|
+
shared its name — `deception.ClaimIntegrityReport`,
|
|
221
|
+
`security_memory.EvidenceCheckpoint`, and
|
|
222
|
+
`AgentSecurityProfile.finding_score`. The third mattered most:
|
|
223
|
+
`MeshState.trust_score` is 0.0 when identity could not be verified, while
|
|
224
|
+
the profile's was 1.0 until something was found, so wiring the profile into
|
|
225
|
+
the mesh's `trust_provider` would have delivered an unchecked agent as fully
|
|
226
|
+
trusted. `firewall.correlation` is deleted; coordination detection moved to
|
|
227
|
+
`firewall.intel`, where every finding carries supporting facts, a rationale
|
|
228
|
+
and `basis="inferred"`. See
|
|
229
|
+
[`docs/v2.3-migration.md`](docs/v2.3-migration.md).
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
69
233
|
## What v2.2 adds
|
|
70
234
|
|
|
71
235
|
### Continuous authorization
|
|
@@ -334,7 +498,9 @@ The architecture is designed around explicit security invariants.
|
|
|
334
498
|
- **A check that could not run is not a check that passed.**
|
|
335
499
|
- **Security failures default toward refusal rather than implicit trust.**
|
|
336
500
|
|
|
337
|
-
These are implementation properties of the system, not a claim that any deployment is universally secure.
|
|
501
|
+
These are implementation properties of the system, not a claim that any deployment is universally secure. Fifteen 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, [`docs/v2.3-invariant-gate.md`](docs/v2.3-invariant-gate.md) for what a green gate run does and does not establish, and [`docs/v2.4-aegis.md`](docs/v2.4-aegis.md) for the four the authority control plane adds.
|
|
502
|
+
|
|
503
|
+
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
504
|
|
|
339
505
|
---
|
|
340
506
|
|
|
@@ -370,7 +536,7 @@ Verification distinguishes states including `verified`, `failed`, `unverifiable`
|
|
|
370
536
|
Python 3.10+ is required.
|
|
371
537
|
|
|
372
538
|
```bash
|
|
373
|
-
pip install agent-firewall-security==2.
|
|
539
|
+
pip install agent-firewall-security==2.3.0
|
|
374
540
|
```
|
|
375
541
|
|
|
376
542
|
Development installation:
|
|
@@ -418,6 +584,17 @@ The authorization path evaluates the security conditions required by the capabil
|
|
|
418
584
|
|
|
419
585
|
## Command surface
|
|
420
586
|
|
|
587
|
+
v2.4 adds no new CLI subcommands. One existing command changes what it
|
|
588
|
+
prints: `firewall delegate-authorize` now labels an allow it reached without
|
|
589
|
+
an attached `sdk_provider` as `ALLOWED (relationship only)` and says not to
|
|
590
|
+
enforce on it. The exit status stays 0 — the question asked was answered
|
|
591
|
+
affirmatively — but the answer no longer overstates itself.
|
|
592
|
+
|
|
593
|
+
v2.3 adds no new CLI subcommands. It adds one flag to the invariant
|
|
594
|
+
checker: `python -m firewall.invariants --exercise --strict` builds the
|
|
595
|
+
canonical estate so that all fifteen invariants can be reached, which makes
|
|
596
|
+
`--strict` a gate that can pass and is therefore worth failing.
|
|
597
|
+
|
|
421
598
|
v2.2 adds no new CLI subcommands. Its one new entry point is the invariant
|
|
422
599
|
checker, `python -m firewall.invariants`, shown above.
|
|
423
600
|
|
|
@@ -468,6 +645,50 @@ python -m firewall.benchmarks
|
|
|
468
645
|
|
|
469
646
|
The repository contains unit, integration, adversarial, hardening, evidence, UI/API, benchmark, and research tests.
|
|
470
647
|
|
|
648
|
+
The v2.4 test surface adds 384 tests across eleven files — 4,087 in the suite
|
|
649
|
+
as a whole, on Python 3.10, 3.11 and 3.12 — including stateful security-state
|
|
650
|
+
fuzzing, eight named concurrency races, and an integration-boundary sweep.
|
|
651
|
+
Among the properties covered:
|
|
652
|
+
|
|
653
|
+
- no path from adaptive analysis to an allow, and no Aegis module able to
|
|
654
|
+
construct an `AuthorizationResult`
|
|
655
|
+
- every forbidden transition refused individually: out of `REVOKED`, out of
|
|
656
|
+
`EXPIRED`, `NARROWED` to wider, `SUSPENDED` to `ACTIVE` without a canonical
|
|
657
|
+
allow, and a child exceeding its parent
|
|
658
|
+
- an envelope that excludes a request always accompanied by a boundary that
|
|
659
|
+
denies it
|
|
660
|
+
- no field of the envelope widening under delegation or attenuation
|
|
661
|
+
- a `nan` bound bounding nothing being denied, while an `inf` bound still
|
|
662
|
+
means unbounded
|
|
663
|
+
- a 400-digit integer producing a decision rather than an exception out of
|
|
664
|
+
`authorize()`
|
|
665
|
+
- every authorization-reachable Aegis method total, including against an
|
|
666
|
+
injected hostile controller
|
|
667
|
+
- a restriction written mid-flight being observed, and a suspension being
|
|
668
|
+
caught inside the commit transaction
|
|
669
|
+
- `__bool__` raising on every analysis object, so a truthiness test cannot
|
|
670
|
+
become an allow
|
|
671
|
+
- every integration surface reaching the canonical boundary, with no local
|
|
672
|
+
allow anywhere
|
|
673
|
+
- no dataclass field in the package carrying a default that Python 3.11
|
|
674
|
+
rejects, so a defect that hides on the development interpreter cannot wait
|
|
675
|
+
for CI to find it
|
|
676
|
+
|
|
677
|
+
The v2.3 test surface adds the thirteen-question self-attack suite, which
|
|
678
|
+
covers, among other properties:
|
|
679
|
+
|
|
680
|
+
- a non-finite request value satisfying no numeric ceiling or floor
|
|
681
|
+
- the first decision under a blind dependency being withheld, and agreeing
|
|
682
|
+
with its own revalidations
|
|
683
|
+
- a delegation budget's consumed total surviving reconfiguration
|
|
684
|
+
- erasing or re-pointing the delegation registry failing closed against the
|
|
685
|
+
child's own signature
|
|
686
|
+
- naming an issuer as trusted not importing its keys
|
|
687
|
+
- `revoke_issuer` containing a compromised signer where `retire_key` does not
|
|
688
|
+
- no gate in the firewall reading the untrusted-data taint marker, because a
|
|
689
|
+
type and a signature are the barrier rather than a filter
|
|
690
|
+
- the degradation subtraction being unable to turn a denial into an allow
|
|
691
|
+
|
|
471
692
|
The v2.2 test surface covers, among other properties:
|
|
472
693
|
|
|
473
694
|
- capability attenuation
|
|
@@ -579,6 +800,14 @@ See [`SECURITY.md`](SECURITY.md) for the project's security reporting policy.
|
|
|
579
800
|
|
|
580
801
|
Detailed specifications are maintained in the repository:
|
|
581
802
|
|
|
803
|
+
- `docs/v2.4-aegis.md`
|
|
804
|
+
- `docs/v2.4-aegis-design.md`
|
|
805
|
+
- `docs/v2.4-performance.md`
|
|
806
|
+
- `docs/v2.4-migration.md`
|
|
807
|
+
- `docs/v2.3-security-corrections.md`
|
|
808
|
+
- `docs/v2.3-self-attack.md`
|
|
809
|
+
- `docs/v2.3-invariant-gate.md`
|
|
810
|
+
- `docs/v2.3-migration.md`
|
|
582
811
|
- `docs/v2.2-architecture.md`
|
|
583
812
|
- `docs/v2.2-security-model.md`
|
|
584
813
|
- `docs/v2.2-threat-model.md`
|