How every piece works.
The deep layer: the pipeline, every command and flag, the full detection catalogue with framework mappings, and the enforcement API. Built from the tool's own afw rules output at v1.3.1.
Architecture & data flow
Every scan runs one pipeline. Loaders turn a target into an Artifact; the rule engine produces findings; the policy turns findings into a verdict. Nothing is executed to be inspected.
The two rule kinds
Most detections are regex signatures — easy to audit and extend (adding one is usually a single compile_sig(...) call). Structural rules handle what regex can't: prompt-injection phrasing, invisible-Unicode and entropy analysis, permission overreach, and stateful baseline diffing.
The four defense tiers
Modelled on a real firewall's layered stack. The first three read artifacts and never execute them; the runtime tier controls what a running process can reach and say.
| Tier | What it does | Entry point |
|---|---|---|
| Pre-install static | Scan code, manifests and model-facing text for known dangers. | afw scan / verify / install |
| Install-time stateful | Pin an approved baseline; re-verify every update for silent drift. | afw pin · --baseline |
| Trust zone provenance | Assign a trust tier from signatures/SBOM; match against IoC feeds. | --verify-signatures · --intel |
| Runtime dynamic | Default-deny egress, MCP tool-call proxy, kernel network isolation. | afw run · afw mcp-proxy |
Commands & flags
Every command takes a directory, a single file, or a .zip. Exit codes: 0 allow/warn · 2 block · 1 error.
| Command | What it does |
|---|---|
afw scan <path>… | Inspect artifacts and print a report; exit reflects the worst verdict. |
afw verify <path>… | CI gate — exit 2 on any BLOCK (--fail-on-warn to also fail on warnings). |
afw install <path> --to <dir> | Pre-check, then copy into place only if it passes (--force to override). |
afw pin <path> | Record a trusted baseline (afw.lock) for later rug-pull detection. |
afw run --allow <host> -- <cmd> | Run behind a default-deny egress firewall (add --isolate for kernel-enforced). |
afw mcp-proxy -- <server> | Inspect / redact / block an MCP server's JSON-RPC tool calls and results live. |
afw serve | Launch the local web UI (loopback-only, token-protected). |
afw watch <dir> | Poll a directory and scan new/modified artifacts as they land. |
afw rules | List every detection with ID, severity, category and framework coverage. |
Common flags
# output & policy --format text|json|sarif # sarif → GitHub code-scanning --policy <file> # JSON/YAML policy file --strict # block on MEDIUM and above --fail-on <SEVERITY> # custom block threshold --ignore <RULE_ID> # suppress a rule or category --baseline <lock> # diff against a pinned afw.lock # trust & intel --intel <path> # add IoC feeds (JSON or names/domains/hashes/signers .txt) --no-intel # disable the bundled seed feed --verify-signatures --identity <id> # cryptographic provenance --no-tighten-untrusted # don't penalise unsigned artifacts # runtime containment (afw run) --allow <host> --allow-port <n> --allow-loopback --isolate # kernel network namespace (zero-network) --fail-on-egress # exit non-zero if anything was blocked
Trust tiers
Each artifact gets a tier from the provenance the scanner can find. Low-trust artifacts are held to a stricter policy automatically (unless you pass --no-tighten-untrusted).
No signature, attestation or baseline. Block threshold tightens one level.
A signature or SBOM is present but not cryptographically verified.
You hold a local afw.lock for it (via afw pin).
Signature cryptographically verified against an expected identity.
Policy & verdicts
Findings have a severity — INFO · LOW · MEDIUM · HIGH · CRITICAL — and the policy maps them to ALLOW, WARN or BLOCK. Default blocks on HIGH+.
# policy.yaml block_severity: HIGH # INFO | LOW | MEDIUM | HIGH | CRITICAL warn_severity: LOW ignore: [AFW-PERM-002] # accept an expected finding warn_only_categories: [install-hook] tighten_untrusted: true # stricter bar for unsigned/unpinned
Policy files are JSON or YAML (the simple schema needs no dependency; pip install AIAgentFirewall[yaml] for full YAML). Output formats: text (colourised), json (the ScanResult schema), sarif (code-scanning dashboards).
Detection catalogue
All 59 detections across 23 categories, each mapped to an industry framework. IDs ending in -* are structural rules that emit a family of findings; varies means severity depends on what's matched.
No detection matches that filter.
Secret & credential access
6Reads SSH/AWS/cloud keys, .env, dumps environment
AFW-SEC-001Reads SSH private keysCRITICALOWASP-LLM02:Sensitive-Information-DisclosureMITRE-ATLAS:Credential-AccessAFW-SEC-002Reads cloud credential filesCRITICALOWASP-LLM02:Sensitive-Information-DisclosureMITRE-ATLAS:Credential-AccessAFW-SEC-003Dumps environment variablesHIGHOWASP-LLM02:Sensitive-Information-DisclosureMITRE-ATLAS:Credential-AccessAFW-SEC-004Targets provider API keysHIGHOWASP-LLM02:Sensitive-Information-DisclosureMITRE-ATLAS:Credential-AccessAFW-SEC-005Reads dotenv / secret filesMEDIUMOWASP-LLM02:Sensitive-Information-DisclosureMITRE-ATLAS:Credential-AccessAFW-SEC-006Accesses OS keychain / secret storeHIGHOWASP-LLM02:Sensitive-Information-DisclosureMITRE-ATLAS:Credential-AccessEmbedded credentials
6Private keys / API tokens shipped inside the artifact
AFW-KEY-001Bundled private keyHIGHOWASP-LLM02:Sensitive-Information-DisclosureAFW-KEY-002AWS access key idHIGHOWASP-LLM02:Sensitive-Information-DisclosureAFW-KEY-003GitHub / GitLab tokenHIGHOWASP-LLM02:Sensitive-Information-DisclosureAFW-KEY-004AI provider API keyHIGHOWASP-LLM02:Sensitive-Information-DisclosureAFW-KEY-005Slack tokenHIGHOWASP-LLM02:Sensitive-Information-DisclosureAFW-KEY-006Generic secret assignmentMEDIUMOWASP-LLM02:Sensitive-Information-DisclosureData exfiltration
7Uploads to webhooks/paste sites, raw-IP egress, reverse shells
AFW-NET-001Exfiltration to paste/webhook serviceHIGHMITRE-ATLAS:ExfiltrationOWASP-LLM02:Sensitive-Information-DisclosureAFW-NET-002Pipes local data to the networkHIGHMITRE-ATLAS:ExfiltrationOWASP-LLM02:Sensitive-Information-DisclosureAFW-NET-003Raw IP address egressMEDIUMMITRE-ATLAS:ExfiltrationOWASP-LLM02:Sensitive-Information-DisclosureAFW-NET-004Reverse shell patternCRITICALMITRE-ATLAS:ExfiltrationOWASP-LLM02:Sensitive-Information-DisclosureAFW-NET-005DNS-based exfiltrationHIGHMITRE-ATLAS:ExfiltrationOWASP-LLM02:Sensitive-Information-DisclosureAFW-NET-006Suspicious outbound POST of dataMEDIUMMITRE-ATLAS:ExfiltrationOWASP-LLM02:Sensitive-Information-DisclosureAFW-NET-007Auto-fetched image/link with a dynamic URL (exfil channel)MEDIUMMITRE-ATLAS:ExfiltrationOWASP-LLM02:Sensitive-Information-DisclosureOWASP-LLM01:Prompt-InjectionObfuscation & dynamic exec
6curl|bash, base64|sh, eval/exec on runtime strings
AFW-BLOB-*High-entropy packed / encoded blobvariesAFW-OBF-001Download-and-execute (curl | bash)CRITICALMITRE-ATLAS:ExecutionMITRE-ATLAS:Defense-EvasionAFW-OBF-002Dynamic code executionHIGHMITRE-ATLAS:ExecutionMITRE-ATLAS:Defense-EvasionAFW-OBF-003Base64 decode-and-runHIGHMITRE-ATLAS:ExecutionMITRE-ATLAS:Defense-EvasionAFW-OBF-004Hex / escape-encoded payloadMEDIUMMITRE-ATLAS:ExecutionMITRE-ATLAS:Defense-EvasionAFW-OBF-005Shell obfuscation via IFS / variable splicingMEDIUMMITRE-ATLAS:ExecutionMITRE-ATLAS:Defense-EvasionUnsafe deserialization
3pickle/torch.load, unsafe yaml, bundled weight files
AFW-DSR-001Unsafe pickle deserializationHIGHOWASP-LLM04:Data-and-Model-PoisoningMITRE-ATLAS:ExecutionAFW-DSR-002Loads model weights that can execute codeMEDIUMOWASP-LLM04:Data-and-Model-PoisoningMITRE-ATLAS:ExecutionAFW-DSR-003Bundled pickle / weight fileLOWOWASP-LLM04:Data-and-Model-PoisoningMITRE-ATLAS:ExecutionDestructive actions
5rm -rf, mkfs, fork bombs, disabling TLS/firewalls, miners
AFW-DES-001Recursive force delete of a broad pathCRITICALMITRE-ATLAS:ImpactOWASP-LLM06:Excessive-AgencyAFW-DES-002Disk / filesystem destructionCRITICALMITRE-ATLAS:ImpactOWASP-LLM06:Excessive-AgencyAFW-DES-003Weakens system security postureHIGHMITRE-ATLAS:ImpactOWASP-LLM06:Excessive-AgencyAFW-DES-004Crypto-miner / resource abuseHIGHMITRE-ATLAS:ImpactOWASP-LLM06:Excessive-AgencyAFW-DES-005Persistence via cron / autostartHIGHMITRE-ATLAS:ImpactOWASP-LLM06:Excessive-AgencyFilesystem overreach
3Broad/sensitive filesystem writes and reads
AFW-FS-001Reads browser / app credential storesHIGHMITRE-ATLAS:Credential-AccessOWASP-LLM02:Sensitive-Information-DisclosureAFW-FS-002Reads shell / command historyMEDIUMMITRE-ATLAS:Credential-AccessOWASP-LLM02:Sensitive-Information-DisclosureAFW-FS-003Broad home-directory sweepMEDIUMMITRE-ATLAS:Credential-AccessOWASP-LLM02:Sensitive-Information-DisclosureAnti-forensics
2Clearing shell history, deleting logs
AFW-AF-001Clears shell historyMEDIUMOWASP-Agentic:Repudiation-and-UntraceabilityMITRE-ATLAS:Defense-EvasionAFW-AF-002Deletes or tampers with system logsHIGHOWASP-Agentic:Repudiation-and-UntraceabilityMITRE-ATLAS:Defense-EvasionPrompt injection
1"ignore previous instructions", "do not tell the user"
AFW-INJ-*Prompt-injection phrasing in model-facing textvariesOWASP-LLM01:Prompt-InjectionTool poisoning
1Hidden directives / secrets inside tool descriptions
AFW-TPZ-*Hidden directive / secret in a tool descriptionvariesMCP:Tool-PoisoningMemory / context poisoning
2Writes to CLAUDE.md, .cursorrules, auto-loaded config
AFW-MEM-001Writes to an agent instruction fileHIGHOWASP-ASI06:Memory-and-Context-PoisoningOWASP-LLM01:Prompt-InjectionAFW-MEM-002Modifies agent / MCP configurationHIGHOWASP-ASI06:Memory-and-Context-PoisoningOWASP-LLM01:Prompt-InjectionHidden content
1Zero-width chars, bidi override, invisible Unicode-Tag
AFW-UNI-*Invisible / bidirectional Unicode trickeryvariesOWASP-LLM01:Prompt-InjectionImproper output handling
2Model output into a shell/SQL/HTML sink
AFW-OUT-001Model output flows into a shell/interpreterHIGHOWASP-LLM05:Improper-Output-HandlingAFW-OUT-002Unsanitized output into SQL / HTMLMEDIUMOWASP-LLM05:Improper-Output-HandlingPermission overreach
1tools:"*", unrestricted Bash(*), silent grants
AFW-PERM-*Over-broad tool / permission grantvariesOWASP-LLM06:Excessive-AgencyInstall hooks
1Silent setup scripts that run on install
AFW-HOOK-*Silent install / setup hookvariesSLSA:Build-IntegrityTyposquatting
1Homoglyph / one-edit look-alike names
AFW-SQT-*Homoglyph / near-miss look-alike namevariesMITRE-ATLAS:Initial-AccessExcessive agency
3User input driving code exec; unscoped agent
AFW-AGENCY-001User input drives code executionCRITICALOWASP-LLM06:Excessive-AgencyOWASP-ASI05:Unexpected-Code-ExecutionOWASP-ASI02:Tool-Misuse-and-ExploitationAFW-AGENCY-002Agent exposes a code-execution/shell toolHIGHOWASP-LLM06:Excessive-AgencyOWASP-ASI05:Unexpected-Code-ExecutionOWASP-ASI02:Tool-Misuse-and-ExploitationAFW-AGENCY-003System prompt grants unrestricted scopeMEDIUMOWASP-LLM06:Excessive-AgencyOWASP-ASI02:Tool-Misuse-and-ExploitationBroken authorization
1Action before the quota/authz check (TOCTOU)
AFW-AUTHZ-*Check-after-act / client-only limit (business logic)variesOWASP-Agentic:Identity-and-Privilege-AbuseRAG / vector-store poisoning
2Untrusted content indexed for retrieval
AFW-RAG-001Untrusted content written into a vector store / KBMEDIUMOWASP-LLM08:Vector-and-Embedding-WeaknessesOWASP-ASI06:Memory-and-Context-PoisoningAFW-RAG-002Unvalidated web documents indexed into RAGMEDIUMOWASP-LLM08:Vector-and-Embedding-WeaknessesOWASP-ASI06:Memory-and-Context-PoisoningInsecure inter-agent (A2A)
2No-auth agent cards, disabled signature checks
AFW-A2A-001Agent-to-agent endpoint declared without authenticationHIGHOWASP-ASI07:Insecure-Inter-Agent-CommunicationOWASP-ASI03:Identity-and-Privilege-AbuseAFW-A2A-002Inter-agent identity/signature verification disabledHIGHOWASP-ASI07:Insecure-Inter-Agent-CommunicationOWASP-ASI03:Identity-and-Privilege-AbuseRug pulls (stateful)
1Files/tools/permissions changed since baseline
AFW-DRIFT-*Baseline drift / rug pull (--baseline)variesMCP:Rug-PullOWASP-ASI03:Identity-and-Privilege-AbuseWeak provenance
1Unsigned / unattested → lower trust tier
AFW-PROV-*Provenance / trust tierINFOSLSA:Provenance-and-IntegritySLSA:Unsigned-ArtifactKnown-bad IoCs
1Name/hash/domain/signer on a threat-intel feed
AFW-IOC-*Threat-intel / IoC match, incl. slopsquatting (--intel)variesThreat-Intel:Known-Malicious-IoCThreat-Intel:Revoked-SignerFramework glossary
Every finding cites at least one industry framework, so findings map to a language security teams already use.
OWASP-LLM01…10The canonical LLM-application risk list — prompt injection, sensitive-information disclosure, improper output handling, data/model poisoning, vector-and-embedding weaknesses, excessive agency, and more.
OWASP-ASI01…10The agent-specific list: tool misuse, identity/privilege abuse, unexpected code execution, memory-and-context poisoning, insecure inter-agent communication, and related agentic threats.
MITRE-ATLAS:*Adversarial tactics & techniques for AI systems — Credential-Access, Execution, Exfiltration, Defense-Evasion, Impact — the ATT&CK analogue for ML.
MCP:*Model Context Protocol failure modes — tool poisoning and the rug-pull class where a tool's behavior or description mutates after approval.
SLSA:*Supply-chain integrity levels — provenance, signed vs. unsigned artifacts, build integrity.
Threat-Intel:*Indicator matches — known-malicious names/hashes/domains and revoked or impersonated signer identities.
Guardrails API (outbound)
Detection finds the deployed-agent bugs; agentfirewall.guardrails enforces the fix at request time. Zero-dependency, embeddable.
from agentfirewall.guardrails import ( InputGuard, ScopePolicy, PreconditionGate, InMemoryQuota, taint, ) # Confine the agent to its business tools; deny code exec. guard = InputGuard(ScopePolicy.for_tools("search_menu", "place_order")) # Reserve quota BEFORE the agent runs, idempotent by request id. gate = PreconditionGate(InMemoryQuota(balances={"user-1": 5})) def handle_chat(user_id, prompt, request_id): guard.check_input(prompt).raise_if_blocked() return gate.run(user_id, lambda: agent.run(prompt), idempotency_key=request_id) # refresh can't replay # Taint untrusted-derived data so it can't drive sensitive sinks. retrieved = taint(vector_store.query(q)) guard.check_tool_call("send_email", {"body": retrieved}).raise_if_blocked() # BLOCKED
| Component | Enforces |
|---|---|
PreconditionGate | Atomic check-before-act quota + idempotent replay (kills the refresh token-burn). Refunds on failure. |
InputGuard / ScopePolicy | Tool allowlist; denies code/shell tools and prompt-injection/exfiltration in inputs and arguments. |
taint() / Tainted | CaMeL-style provenance — untrusted-derived data can't reach code-exec/email/http/sql/payment sinks. |
Production note. The bundled InMemoryQuota / InMemoryIdempotencyStore are single-process references — back them with Redis or a database behind the same interface for real deployments.
Source docs & extending
The Markdown docs in the repo go deeper on internals and process.
Adding a detection
A regex detection is usually one line in agentfirewall/rules/signatures.py:
compile_sig(
"AFW-NET-099", "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 rules/structural.py and register it. See CONTRIBUTING.md.