Warden
beta · v4.12.0
beta · v4.12.0
Documentation

Rule Engine

Warden ships with hundreds of compiled patterns organized into categories:

CategoryExamples
SafetyBlock rm -rf, sudo, chmod 777, history rewrites (git filter-branch), credential file writes, reading or uploading SSH keys and cloud credentials
DestructiveBlock work-destroying operations that are not undoable — see below
SubstitutionSteer greprg, findfd, curlxh, catbat
HallucinationDetect invented CLI flags, non-existent subcommands, wrong argument patterns
Path protectionPrevent writes to .env, credentials, system files, lock files
ChainBlock relationships between commands — a download piped into a shell, the environment piped to the network
Git policyBlock all mutating git commands (add, commit, push, merge, rebase) — off unless you set git_readonly = true
AdvisorySuggest better approaches, warn about large file reads, flag verification debt

Rules are compiled into the binary — no runtime parsing, no config files to maintain. New rules ship with every release. Run warden --debug restrictions list to see every rule ID with its category and severity, or warden --debug describe --all for the raw pattern dump.

How Rules Work

All patterns are evaluated simultaneously using a compiled regex set. The evaluation cost doesn’t grow linearly with the number of rules — a single pass checks everything.

Rules are available the instant Warden loads. They can’t go missing, get corrupted, or fail to download. When you update Warden, you get the exact rule set tested against that version.

You can extend the built-in rules with TOML overrides (see rules.toml), but the compiled floor is always present.

Rule Categories in Detail

Safety — universally dangerous operations that are always blocked:

  • rm -rf on broad paths (~, /, ., *)
  • Privilege escalation (sudo, su -, doas, runas)
  • Dangerous permissions (chmod 777, chmod -R 777, chmod a+w)
  • System damage (mkfs, dd if=, format C:, diskpart)
  • Process killing (kill -9 1, killall, pkill -9)
  • Environment destruction (export PATH=, unset PATH)
  • Reading credential material into the transcript (cat ~/.ssh/id_rsa, ~/.aws/credentials, .netrc) or uploading it with a data flag

Destructive — always on, and distinct from the opt-in git policy below. An agent committing and pushing its work is ordinary; an agent destroying work that was never committed is a different risk class, and only the second is blocked by default:

  • git push --force — use --force-with-lease, and ask first
  • git reset --hard — discards uncommitted work
  • git clean -f — deletes untracked files irreversibly
  • git checkout . / git restore . — discards every uncommitted change at once
  • Tools that auto-modify code (knip --fix, sg -r, madge --image)

Rehearsals are exempt: --dry-run, -n, and --what-if reach the real command untouched.

Chain — patterns whose subject spans a pipe or &&, matched against the raw command before it is split:

  • A download piped into a shell or an interpreter that executes the stream
  • The environment or a credential file piped to a network tool
  • Download-then-execute and download-then-chmod +x sequences
  • A fork bomb, and redirects onto a raw block device

Piping a download into an interpreter that is only parsing it — xh GET … | python -c "json.load(sys.stdin)" — is ordinary work and is not blocked. That distinction was measured: on 13,162 real commands it was the difference between a 0.099% and a 0.038% false-block rate.

Substitution — steers legacy tools toward modern alternatives. Most are advisory: the command runs and the agent is told what to use next time. Only three are rewritten before execution.

Rewritten in place:

  • ls to eza — better directory listings
  • du to dust — visual disk usage tree
  • ts-node to tsx — for plain invocations; calls with ts-node-specific flags get an advisory instead

Advisory (the command still runs):

  • grep to rg (ripgrep) — faster search that respects .gitignore, so less output reaches the context window
  • find to fd — faster file discovery with sane defaults
  • curl to xh — friendlier HTTP client with colored output
  • cat to bat — syntax highlighting and line numbers
  • tar/zip/unzip/gzip to ouch — auto-detecting archive tool
  • sort | uniq to huniq — preserves insertion order

Blocked outright:

  • sd on Windows — mangles newlines, use the Edit tool instead

A rule is skipped entirely when its target tool isn’t installed — rewriting to a missing binary breaks the command, and teaching one is noise.

Hallucination — catches commands the agent fabricated:

  • URL-encoded path traversal (%2e%2e/)
  • Null byte injection (\x00, %00)
  • Reverse shell patterns (/dev/tcp/, socat EXEC:, ncat -e)
  • Credential exfiltration (piping .ssh/id_rsa or .env to curl/wget)
  • Command hijacking (alias sudo=..., eval $(curl ...))
  • Base64-decoded command execution

Path protection — prevents writes to sensitive locations:

  • SSH keys and config (~/.ssh/)
  • GPG keys (~/.gnupg/)
  • Cloud credentials (~/.aws/credentials, ~/.azure/, ~/.kube/config, ~/.gcloud/)
  • Docker credentials (~/.docker/config.json)
  • System directories (/etc/, /usr/, C:\Windows, C:\Program Files)
  • Certificate and key files (.pem, .key, .p12, .pfx)
  • Terraform state (~/.terraform/)

Git policy — the whole-of-git lock, off by default and separate from the work-destroying subset above, which is always on. Set git_readonly = true at the top level of rules.toml to turn it on. When on:

  • Blocks git add, git commit, git tag
  • Blocks git push (including force), git pull, git merge, git rebase, git reset, git clean
  • Blocks git checkout, git restore, git revert, git cherry-pick, git stash, git bisect, git am, git apply
  • Blocks git branch -d/-D
  • Allows read-only commands: log, status, diff, show, branch (list), blame

Advisory — non-blocking hints for better practices:

  • Docker CLI usage when MCP tools are available
  • rg for symbol lookups when aidex is available
  • rg for structural patterns when ast-grep is available
  • npm install, cargo add, pip install warnings about environment modification
  • git clone warnings about disk space and time

Shadow Mode

Rules can be set to shadow mode, where they log what they would have done without actually blocking. This is useful for testing new rules before deploying them:

# In ~/.warden/rules.toml
[safety]
patterns = [
  { match = "some-new-pattern", msg = "Testing this rule", shadow = true }
]

Shadow-mode entries show up in the dashboard and in warden --debug export data, so you can evaluate their accuracy before making them live.

Rule IDs

Every rule carries an ID, and there are two shapes of ID depending on where the rule comes from.

Named rules live in the restriction registry. Their IDs are slugs, and they are what the governance handlers — read, write, redirect, permission, substitution — report:

IDCategorySeverityDisableable
safety.rm-rfSafetyHardDenyno
safety.sudoSafetyHardDenyno
substitution.grepSubstitutionHardDenyyes
substitution.catSubstitutionHardDenyyes
read.dedupGovernanceAdvisoryyes
read.large-fileGovernanceSoftDenyyes
write.sensitive-pathGovernanceHardDenyno
redirect.grep-toolRedirectHardDenyyes
permission.credentialsPermissionHardDenyno

Substitution IDs are keyed by the command being replaced: substitution.grep, find, curl, cat, ts-node, ls, sd, du, tar, sort, rg.

Compiled pattern rules — the long regex lists for safety, destructive commands, hallucination, advisories, and sensitive paths — get positional IDs assigned at merge time, in list order: safety.0, destructive.3, hallucination.12, advisory.4, sensitive_deny.0, zero_trace.0, git_readonly.1. Patterns you add yourself are numbered separately by where they came from: <category>.global_0 for your ~/.warden/rules.toml, <category>.project_0 for a project’s .warden/rules.toml.

Positional IDs shift when the compiled list changes between releases, so treat them as debugging handles rather than stable configuration keys.

Rule IDs are used for:

  • Disabling rules in rules.toml: [restrictions] disable = ["substitution.cat", "read.post-edit"]. Anything in the safety, destructive, hallucination, zero_trace, permission, or git_readonly namespaces is refused — that is the immutable floor.
  • Identifying a denial — the deny message ends with the ID in brackets.
  • Rule-fire accounting — seeing how often a specific rule fires.

Viewing All Rules

warden --debug restrictions list
warden --debug restrictions list --category Substitution

This prints every registry rule with its ID, handler, category, severity (HardDeny, SoftDeny, or Advisory), whether it can be disabled, and a description.

warden --debug describe --all is the other view: a JSON dump of the compiled patterns and your overrides. Its rule entries carry pattern and message only — the substitutions block is the exception, carrying id, source, and target.