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.
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/PKG-INFO +101 -3
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/README.md +100 -2
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/agent_firewall_security.egg-info/PKG-INFO +101 -3
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/agent_firewall_security.egg-info/SOURCES.txt +7 -2
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/adversarial/__init__.py +31 -17
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/authorization.py +16 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/continuous_auth/engine.py +20 -1
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/deception/__init__.py +28 -4
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/defense/mesh.py +20 -1
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/delegation_budget.py +73 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/evidence_integrity/__init__.py +6 -6
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/immune/engine.py +14 -3
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/intel/__init__.py +2 -0
- agent_firewall_security-2.3.0/firewall/intel/engine.py +875 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/invariants/__init__.py +14 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/invariants/__main__.py +51 -13
- agent_firewall_security-2.3.0/firewall/invariants/exercise.py +269 -0
- agent_firewall_security-2.3.0/firewall/policy_analysis/__init__.py +799 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/recorder/checkpoint.py +12 -1
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/sdk.py +126 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/security_memory/__init__.py +26 -13
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/pyproject.toml +1 -1
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/tests/test_v2_2_adversarial_defense.py +28 -4
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/tests/test_v2_2_deception_integrity.py +2 -2
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/tests/test_v2_2_evidence_integrity.py +6 -6
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/tests/test_v2_2_invariants.py +23 -74
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/tests/test_v2_2_security_memory.py +4 -4
- agent_firewall_security-2.3.0/tests/test_v2_3_duplicate_concepts.py +412 -0
- agent_firewall_security-2.3.0/tests/test_v2_3_intel_coordination.py +465 -0
- agent_firewall_security-2.3.0/tests/test_v2_3_invariant_gate.py +304 -0
- agent_firewall_security-2.3.0/tests/test_v2_3_policy_analysis.py +652 -0
- agent_firewall_security-2.3.0/tests/test_v2_3_self_attack.py +2686 -0
- 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/policy_analysis/__init__.py +0 -409
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/agent_firewall_security.egg-info/dependency_links.txt +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/agent_firewall_security.egg-info/entry_points.txt +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/agent_firewall_security.egg-info/requires.txt +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/agent_firewall_security.egg-info/top_level.txt +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/a2a/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/a2a/auth.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/adapters/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/adapters/anthropic.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/adapters/generic.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/adapters/normalize.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/adapters/openai.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/agents/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/agents/adapters.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/agents/base.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/artifact/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/artifact/reader.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/artifact/writer.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/attackgraph/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/attackgraph/engine.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/attenuation.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/attest/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/attest/authority.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/benchmarks.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/capability.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/capability2/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/capability2/constraints.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/cli.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/cli_v19.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/cli_v20.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/cli_v21.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/containment/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/containment/controller.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/continuous_auth/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/continuous_auth/monitor.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/continuous_auth/predicates.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/defense/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/delegation.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/delegation_lineage.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/delegation_store.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/engine.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/evidence.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/evidence_graph/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/evidence_graph/graph.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/explain.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/http.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ident/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ident/registry.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/identity.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/immune/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/incident/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/incident/package.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/invariants/model.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/invariants/registry.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/invariants/runtime.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/invariants/source.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/invariants/static.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/key_management.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/key_store.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/lab/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/lab/engine.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/lifecycle.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/lifecycle_store.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/mcp.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/namespace.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/network/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/network/attack_path.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/network/behavior.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/network/correlation.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/network/graph.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/network/model.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/network/response.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/network/simulator.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/network/state.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/north_star.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/passport/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/passport/builder.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/platform/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/platform/provenance.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/policy.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/posture/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/posture/engine.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/protect.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/provenance/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/provenance/registry.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/recorder/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/recorder/encoding.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/recorder/events.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/recorder/identity.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/recorder/recorder.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/recorder/redact.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/refusal_state.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/replay.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/replay_store.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/replaylab/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/replaylab/laboratory.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/research/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/research/lab.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/response2/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/response2/adaptive.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/revocation.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/revocation_store.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/risk_context.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/security_context.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/security_decision.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/semantic_chain.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/simulation/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/simulation/case.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/simulation/recorder.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/simulation/replay.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/simulation/report.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/simulation/rollout.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/simulation/ruleset.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/task/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/task/registry.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/timeline/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/timeline/graph.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/timeline/timeline.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/timeline/trajectory.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/tools.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/transport.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/trust/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/trust/graph.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/twin/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/twin/adversarial.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/twin/twin.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/__main__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/control.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/demo.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/introspect.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/server.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/service.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/static/console.css +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/static/console.js +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/static/control.js +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/static/index.html +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/static/recorder.css +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/static/recorder.js +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/static/soc.css +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/static/soc.js +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/static/v20.css +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/static/v20.js +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/static/v21.css +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/static/v21.js +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/v18.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/v19.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/v20.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/ui/v21.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/verify/__init__.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/verify/verifier.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/setup.cfg +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/tests/test_continuous_auth.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/tests/test_engine.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/tests/test_sdk_continuous_auth.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/tests/test_security_decision.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/tests/test_v2_2_adversarial_twin.py +0 -0
- {agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/tests/test_v2_2_capability_delegation.py +0 -0
- {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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
{agent_firewall_security-2.2.0 → agent_firewall_security-2.3.0}/firewall/adversarial/__init__.py
RENAMED
|
@@ -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, ``
|
|
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.
|
|
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.
|
|
168
|
-
#:
|
|
169
|
-
#:
|
|
170
|
-
#:
|
|
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
|
-
``
|
|
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
|
-
|
|
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.
|
|
304
|
-
raise ValueError("
|
|
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
|
-
"
|
|
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 ``(
|
|
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 ``
|
|
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 ``
|
|
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
|
-
|
|
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
|
-
|
|
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
|
):
|