Skip to main content

Agent Firewall

CI Python License: MIT

A runtime security and governance layer that evaluates AI-agent tool requests before they execute and returns one of three decisions: allow, ask (require human approval), or block.

The engine is vendor-neutral and dependency-light (just pydantic + PyYAML) so it can run inside a Claude Code PreToolUse hook with no network and no database. The same engine can back an MCP gateway or an HTTP service.

It is a policy gate, not a sandbox. It never executes the tool it is judging, and it only reasons about what the integration hands it. Read docs/limitations.md for the honest threat model before you rely on it.

How it works

Every request flows through a fixed, security-critical pipeline (src/agent_firewall/engine.py):

  1. Authorization (fail-closed). Unknown tools and unauthorized agents are rejected before anything else. (authorization.py)
  2. Detection. Deterministic detectors appropriate to the action type run: shell-command shapes, filesystem path analysis, secret/DLP scanning, and prompt-injection indicators. (detection/)
  3. Risk scoring. Findings are turned into an additive, fully attributed 0–100 score with per-factor explanations — no learned component. (risk/)
  4. Policy evaluation. Declarative YAML policies decide allow / approval / deny, with the risk score available as a threshold. (policies/)
  5. Combine. Precedence is deny > approval > allow. A high score can only raise scrutiny; it can never rescue a denied request, and a low score can never override a deny.

Each decision is explainable (explanation, matched_policies, matched_rules, risk_factors) and recorded to a tamper-evident audit log.

Install

Requires Python ≥ 3.11.

pip install -e .          # core engine + CLI
pip install -e ".[dev]"   # + pytest, ruff, mypy, hypothesis
pip install -e ".[api]"   # + optional FastAPI/SQLAlchemy service deps
pip install -e ".[mcp]"   # + optional MCP gateway deps

The repo also works without installing: hooks/pre_tool_use.py inserts src/ onto sys.path itself.

Use it as a Claude Code hook

.claude/settings.json already wires the firewall as a PreToolUse hook over the file, shell, and network tools:

{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Bash|Read|Write|Edit|MultiEdit|NotebookEdit|WebFetch|Glob|Grep",
      "hooks": [{
        "type": "command",
        "command": "python",
        "args": ["${CLAUDE_PROJECT_DIR}/hooks/pre_tool_use.py"],
        "timeout": 10
      }]
    }]
  }
}

Decision mapping to the hook protocol:

Engine decision permissionDecision
BLOCK deny
ASK ask
ALLOW deferred to Claude Code's native flow (nothing emitted) unless AGENT_FIREWALL_EMIT_ALLOW=1

Deferring on allow means the firewall never disables native permission controls — it only ever tightens them. Remove the hooks block (or the file) to disable it entirely.

CLI

# Evaluate a synthetic request and print the full decision as JSON
agent-firewall evaluate --tool Bash --input '{"command": "rm -rf /"}'

# Validate a policy file against the schema
agent-firewall policy validate policies/default.yaml

# Inspect / verify the audit log
agent-firewall audit show --limit 20
agent-firewall audit verify

(agent-firewall hook runs the same stdin-driven hook adapter directly.)

Use it as an MCP gateway

The same engine can be exposed as a Model Context Protocol server so any MCP client can consult the firewall before running a tool:

pip install -e ".[mcp]"
agent-firewall mcp        # serve over stdio

It registers one tool, evaluate_tool_request, that returns an allow / ask / block decision for a described request. It is a consultative gate — the calling agent must honor the decision. See docs/mcp-gateway.md.

Policies

Policies are declarative YAML (policies/default.yaml). Selectors within a policy are ANDed; lists within a selector are ORed. Effects are allow, approval, or deny, with deny winning. The bundled set:

  • block-protected-credentials — never read/write .env, keys, .ssh, .aws, …
  • block-critical-risk — deny anything scoring ≥ 85 (how rm -rf /, mkfs, fork bombs, secret egress get hard-blocked — the score can't be argued away)
  • require-approval-elevated-risk — score ≥ 50 (< 85) needs human approval
  • require-approval-network-egress — outbound tools (WebFetch) need approval
  • allow-project-reads — reading non-sensitive files is allowed

Configuration

Read from the environment (src/agent_firewall/config.py):

Variable Default Meaning
AGENT_FIREWALL_POLICY_FILE bundled policies/default.yaml policy document
AGENT_FIREWALL_AUDIT_LOG .agent_firewall/audit.jsonl audit log path
AGENT_FIREWALL_ON_ERROR ask fail mode on error: ask / deny / allow
AGENT_FIREWALL_PROJECT_ROOT cwd project boundary for path checks
AGENT_FIREWALL_ENV development environment label
AGENT_FIREWALL_EMIT_ALLOW unset set 1 to emit explicit allow

Audit log

Append-only JSONL with a SHA-256 hash chain; audit verify detects truncation or edits, and concurrent processes serialize appends via a lock file. Request summaries are secret-scrubbed and raw tool input is never written verbatim. It is tamper-evident, not tamper-proof — see docs/limitations.md.

Development

python -m pytest        # test suite (unit tests in tests/)
python scripts/smoke_test.py   # end-to-end sanity check on synthetic inputs
ruff check .            # lint
mypy                    # strict type-check

All test fixtures and smoke-test inputs use synthetic credentials and safe, non-executed command strings.

Project layout

src/agent_firewall/
  engine.py           core evaluation pipeline
  authorization.py    fail-closed identity/tool gate
  normalize.py        vendor payload -> NormalizedRequest
  models.py           typed domain models (pydantic)
  config.py           env-driven CoreConfig
  detection/          command / filesystem / secrets / dlp / prompt-injection
  risk/               deterministic additive scorer + weights
  policies/           YAML schema, loader, evaluator
  audit/              hash-chained JSONL writer + verifier
  integrations/claude_code/   PreToolUse hook adapter
  integrations/mcp/           MCP gateway server (optional [mcp] extra)
hooks/pre_tool_use.py thin stdin entry point for Claude Code
policies/default.yaml bundled policy set
docs/limitations.md   threat model & known gaps

Contributing & security

Contributions are welcome — see CONTRIBUTING.md for the dev setup and the quality gate to run before a PR. To report a vulnerability, use GitHub private vulnerability reporting as described in SECURITY.md (please don't file public issues for security bugs).

License

MIT.

Metadata

Release files for agent-firewall1 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for agent-firewall1 0.1.0
File Size Uploaded
agent_firewall1-0.1.0.tar.gz 41.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for agent-firewall1 0.1.0
File Interpreter ABI Platform
agent_firewall1-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 91.9 kB

Release files / agent_firewall1-0.1.0.tar.gz

Download URL agent_firewall1-0.1.0.tar.gz
Size 41.5 kB
Tags Source
SHA-256 checksum
How to use checksums
6debd886e0f4074c272a9707685f2e2834a3e179c148993d3134c3882d5f8530
BLAKE2b-256 checksum
How to use checksums
2bdb77c1ceafce48d0ee080b83cf084bc73fd947b873d3566e33acd0ddd899a6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 10, 2026.

Transparency log

Release files / agent_firewall1-0.1.0-py3-none-any.whl

Download URL agent_firewall1-0.1.0-py3-none-any.whl
Size 50.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6da98824206fa37c74fd850e37ba8fac8dfb5d4dac6ea0efe909a811dc577ffe
BLAKE2b-256 checksum
How to use checksums
679991acd797d9d259f2ab2e8e742ad28c144d5903e1fbca13da64dff20f41f0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 10, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page