Skip to main content

Rule Catalog & Categories

Belay decides what an AI coding agent may do using a deterministic rule catalog — a single, versioned source of truth that the daemon compiles in at build time and matches against every tool call. There is no LLM on the gating path: the same input always produces the same verdict, in well under 100ms.

When this matters

Every allow, ask, or deny you see in the audit log traces back to a rule in this catalog (or to the session correlation layer that sits on top of it). Understanding the categories helps you read verdicts, tune false positives in --observe mode, and map findings to the security standards your organisation reports against.

Mental model

A tool call (a Bash command, a file Read/Write/Edit, a Grep, a WebFetch, or an MCP tools/call) is projected into a normalized shape and tested against each rule. A rule declares which tools it applies_to and a set of match patterns (command regexes or path-glob regexes). When a rule matches, it contributes its decisionallow, ask, or deny — along with a severity and its standards tags.

Categories

The catalog groups rules into behaviour categories. Each category below lists representative matches; it is not exhaustive.

CategoryWhat it catchesTypical verdict
SecretsReading .env, ~/.aws/credentials, SSH private keys, .netrc, .npmrc, kube/docker/gcloud config; dumping the process environment; grepping the filesystem for API_KEY/TOKEN/AKIA/sk-ant-; touching the OS/browser credential storeask (often arms the session — see below)
EgressOutbound to known exfiltration sinks (webhook.site, requestbin, ngrok, pastebin, transfer.sh, …); uploading .env or secret-shaped values over curl/scp/rsync; DNS-encoded exfiltration; reverse shellsaskdeny for the most dangerous forms
Destructiverm -rf of a dangerous target, --no-preserve-root, disk/partition wipes (dd of=/dev/…, mkfs, wipefs), destructive SQL (DROP, TRUNCATE), git push --force to main / git reset --harddeny (critical) or ask (git force)
RCEcurl … | sh and other download-and-pipe-to-interpreter patterns, decode-and-execute (base64 -d | sh, eval $(…))deny
Supply-chainPackage installs that may run install scripts (npm/pnpm/yarn install, pip install, gem/cargo/go install, npx -y)ask
PersistenceWriting SSH authorized_keys, editing shell profiles (.bashrc/.zshrc), scheduled tasks (crontab, /etc/cron, systemctl enable, launchctl load)deny (authorized_keys) or ask
Privilege-escalationsudo / doas / pkexecask
ReconFilesystem-wide secret sweeps (find … -name *.env), reading another agent's config/memory, cloud/identity probes (aws sts get-caller-identity, gh auth token, kubectl config view), local LLM-runtime probingask
Config-tamperModifying agent configuration (~/.claude/settings.json, apiKeyHelper, ANTHROPIC_BASE_URL, Codex config.toml) or Belay's own rules/audit/binarydeny (critical)

Verdict precedence

When several rules match one tool call, Belay combines them with a strict priority:

Deny > Ask > Allow

If any matching rule says deny, the call is denied — a lower-priority ask or allow can never override it. If no rule denies but at least one asks, the call escalates to a human. Only when nothing denies or asks does the call proceed.

warning

The engine is fail-closed. An internal error, an evaluation timeout, or an unanswered ask in the headless MCP proxy all resolve to deny — never to a silent allow.

The dev-toolchain allowlist

Everyday build and version-control commands would otherwise trip ask rules constantly (a pip install, a git add). To keep signal high, the catalog ships a small dev-toolchain allowlist that suppresses benign activity:

  • cargo build|test|check|run|fmt|clippy|doc
  • npm run|test|ci|build|start (and the pnpm/yarn equivalents)
  • go build|test|vet|run|fmt
  • docker build|compose build|run --rm
  • git status|diff|log|add|commit|fetch|pull|branch|checkout
  • make
danger

The allowlist can only downgrade a non-deny decision. It can never rescue a DENY. A destructive or exfiltration command that happens to start with an allowlisted verb is still denied — the allowlist is a false-positive damper, not an escape hatch.

Every verdict explains itself

A rule match is never just a bare deny. Each verdict carries a curated, plain-English explanation — a short summary, what the command does, why it is risky, when it is normal, and a suggested action — so you can decide without reverse-engineering the rule. See Explain & Advise for the built-in explanations and the optional (off-by-default) AI explainer.

Standards tags on every rule

Each rule carries three machine-readable tags so a finding is immediately mappable to the frameworks security teams already use:

  • OWASP Agentic Security Initiative (ASI) Top 10 — e.g. ASI02, ASI03, ASI05
  • OWASP LLM Top 10 — e.g. LLM01, LLM02
  • MITRE ATLAS — e.g. AML.Exfiltration, AML.CodeExecution, AML.Persistence

See Standards Alignment for the full mapping and how those tags flow into the scanner's SARIF 2.1.0 output.

Tuning false positives

Run belay protect <agent> --observe to log would-be decisions without blocking anything, then review with belay logs. Once the audit trail looks right for your workflow, re-run protect without --observe to enforce.