Bash command security interceptor for LLM agent sandboxing. Parses commands into ASTs via tree-sitter, runs pluggable security rules, returns structured verdicts (ALLOW/BLOCK/CONFIRM/REDIRECT).
Three-stage pipeline — detection orthogonal to response:
bash string → parse() → audit() → decide() → Verdict
[CST] [Findings] [ALLOW/BLOCK/CONFIRM/REDIRECT]
uv pip install -e ".[dev]"echo "$CLAUDE_HOOK_INPUT" | bashguard hookbashguard analyze --command 'git push --force origin main'bashguard log --verdict block --rule network.unknown_host -n 20 --json
bashguard stats --days 7| Rule | Detects | ActionType |
|---|---|---|
parse.error_node |
malformed/obfuscated commands | OBFUSCATED |
credentials.privileged_path |
~/.ssh, ~/.aws, /etc, .env | CREDENTIAL_ACCESS |
network.unknown_host |
curl/wget/nc to unknown hosts | NETWORK_OUTBOUND |
network.dev_tcp |
/dev/tcp bash trick | NETWORK_OUTBOUND |
destructive.irreversible |
rm -rf, dd, mkfs, shred | FILESYSTEM_DELETE |
package_install.global |
brew/apt/npm -g | PACKAGE_INSTALL |
git.destructive |
force push, reset --hard, branch -D | GIT_DESTRUCTIVE |
paths.protected_write |
write redirects to /etc /usr /sys | SYSTEM_CONFIG |
content.secret_in_args |
API keys/PEM/tokens in args | CREDENTIAL_ACCESS |
content.exfiltration_pattern |
sensitive files piped to network | NETWORK_OUTBOUND |
content.outside_boundary |
file access outside worktree | FILESYSTEM_READ |
self_protection.* |
attempts to modify bashguard itself | SYSTEM_CONFIG |
comms.* |
email/SMS/webhook sending | NETWORK_OUTBOUND |
13 evasion.* rules |
eval, shell-in-shell, decode pipelines, IFS, etc. | OBFUSCATED/ENV_MUTATION |
Allowlist-only: blocks any command not in the safe vocabulary. Not registered by default.
from bashguard.strict_mode import StrictModeRuleCan only tighten policy (allow→block), never relax (block→allow):
policy:
severity:
medium: block
rules:
- rule_id: git.destructive
verdict: block
context:
allowed_hosts:
- internal.corp.comOptional LLM second opinion for CONFIRM verdicts:
export BASHGUARD_LLM_FALLBACK=1
export BASHGUARD_LLM_KEY=sk-...from bashguard.auditor import audit
from bashguard.context import make_context
from bashguard.policy import PolicyConfig, decide
ctx = make_context()
findings = audit("rm -rf /", ctx)
verdict = decide(findings, ctx, PolicyConfig.default()).venv/bin/pytest tests/ -q