Stop the shell command before your AI agent runs it.
Your AI calls verify_shell_exec first. Deslint reads .deslint/policy.yml and returns allow / warn / deny in under a millisecond. Same input, same verdict, every time.
Why a deterministic firewall
An AI cannot be its own firewall.
Every IDE will ship an “AI reviews AI” layer by 2027. Useful for taste, unusable as a compliance control: variance per run, slow per call, cloud round-trip required, no signed verdict. Four properties separate the firewall from a reviewer:
| Property | Why AI can't match | Why customers need it |
|---|---|---|
| Determinism | LLMs have variance per run — same prompt, different output. | CI gates need pass/fail consistency. Auditors require reproducibility. |
| Sub-10ms latency | LLMs take 1–30 seconds. Agent loops can't tolerate that on every action. | Pre-execution gating only works if it's cheaper than the agent's own thinking cycle. |
| Local-first | Cloud AI services need data to function. Regulated industries forbid egress. | Finance, health, government, defense — entire verticals can't use cloud LLM tools. |
| Auditable verdicts | LLM verdicts can't be cited or signed. Same input, different verdict tomorrow. | Every block produces a citable reason and matched-pattern. Auditor-ready trail. |
The verify_before_* family
Five interceptors. One policy file.
The firewall ships interceptors incrementally. The policy DSL is stable from v0.10 forward — author the whole policy now and each interceptor starts enforcing as it lands.
verify_shell_exec Shipped v0.10Pre-execution gate for shell commands. Allow / deny / built-in dangerous-pattern checks.
verify_outbound_requestNextAllowlist hosts the agent may call; block private/loopback/metadata IPs (SSRF) at runtime.
verify_file_readPlannedConfine reads to project root; flag reads of .env, secrets directories, and credential stores.
verify_secret_accessPlannedIntercept process.env.* reads; flag access to unallowlisted secret keys.
verify_git_opPlannedBlock force-push to protected branches; block history rewrites and tag deletes.
Built-in dangerous-pattern checks
7 curated regexes you don't have to write yourself.
Every policy ships with a vetted set of dangerous-pattern detectors so you don't have to research the canonical exploit signatures. Three are on by default; the rest opt in via builtinChecks. Allowlist matches always win — legitimate use cases have an escape hatch.
Destructive rm
Default ondestructive-rmrm -rf /, rm -rf ~, rm -rf $HOME — but NOT rm -rf node_modules / ./build
curl | sh
Default oncurl-pipe-shellcurl ... | sh, wget ... | bash and variants — the canonical drive-by-install
Reverse shell
Default onreverse-shellnc -e, bash -i >& /dev/tcp/.../, python -c 'socket.connect(...)' — known exploitation signatures
sudo invocations
Opt-insudoAny sudo invocation. Off by default — opt in via builtinChecks
History rewrite
Opt-inhistory-rewritegit reset --hard, git filter-branch, git push --force, reflog expire
Process substitution
Opt-inprocess-substitution<(curl ...), >(...) — supply-chain exploit pattern
Crypto miners
Opt-incrypto-miningKnown miner binaries: xmrig, cgminer, minerd, ethminer, cpuminer
Authoring a policy
Copy this. Done.
Drop this into .deslint/policy.yml. The next shell command your agent proposes runs through it. Patterns prefixed re: are regex; everything else is a literal exact match. Deny beats allow on overlap, so “allow everything starting with pnpm, but deny pnpm publish” reads cleanly.
version: 1
name: acme-corp/strict
severity: error
shellExec:
deny:
- "pnpm publish"
- "re:^npm install -g"
- "re:^git push --force"
allow:
- "re:^pnpm (test|run |install$|build$)"
- "re:^git (status|diff|log|add|commit|fetch|pull)"
- "re:^ls( |$)"
- "pwd"
defaultAction: deny
builtinChecks:
- destructive-rm
- curl-pipe-shell
- reverse-shell
- history-rewrite
- sudoGet the firewall in your agent loop.
Install @deslint/mcp, point your agent at it, drop a .deslint/policy.yml in your repo. The next shell command your agent proposes runs through verify_shell_exec.