中文说明 · Security policy · Releases · Homebrew Tap
Keep coding agents useful—without making every tool call a leap of faith.
Bash Guard is a local-first safety gate for Claude Code and Codex. It intercepts Bash, Read, Edit, Write, Glob, and Grep before execution, then applies a Rust permission policy you can inspect and tune. Add a Jev API key to layer in a confidence-aware semantic decision; remove it and the original local policy continues unchanged.
- Still enforced when permissions are bypassed. The
PreToolUseHook runs before the supported client's permission decision. - Fail closed. A missing binary, invalid Hook input, or audit-write failure denies the protected tool call instead of silently allowing it.
- Permission-inspired least privilege. The policy expresses sensitive capabilities with Linux-file-permission-inspired bits; grants must be explicit, and ungranted capabilities stay denied. It is an application policy model, not an operating-system file-permission implementation.
- Jev when you want semantic judgment. With a key, Jev chooses
allowordenyfor the exact operation and returns a probability. That maps directly to a Codex/Claude Hook:denyemits a block;allowemits no output and lets the call proceed. Without a key—or when Jev is unavailable—Bash Guard uses the local policy. - Small operational footprint. Registration creates a minimal local Claude Code plugin adapter; it records the binary path and never copies the binary.
- Auditable by default. JSONL audit records capture the client, tool name, each decision, operation summary, caller working directory, and policy requirement. Write content and Edit replacement text are recorded only as lengths.
- Shared policy semantics. Command classification uses the same Rust policy implementation and denial wording as Bash Agent.
Agent tool call
│
├── no Jev key ────────────────► local Rust policy ──► allow / deny
│
└── Jev key configured ───────► Jev allow / deny + probability
│
API error ───┴──► local Rust policy
deny ────────────────────────► deny
The local policy is never removed. This keeps the hook useful offline and gives Jev errors a predictable, reviewable fallback path.
Homebrew:
brew tap lloydzhou/tap
brew install bash-guardOr download the archive for your platform from GitHub Releases.
bash-guard claude register --scope userScopes are user, project, and local; user is the default. Registration uses the official Claude Code plugin CLI to add a local Marketplace and install the adapter.
bash-guard claude status
bash-guard codex status --scope userStart Claude Code or Codex normally. Bash Guard is invoked automatically for each Bash, Read, Edit, Write, Glob, and Grep tool call in the registered scope. Read, Glob, and Grep require read permission; Edit and Write require write permission; Bash keeps its existing command classification.
bash-guard codex register --scope user
# Or write to the current Git repository's .codex/hooks.json.
bash-guard codex register --scope projectCodex registration only handles its PreToolUse event with matcher ^(Bash|Read|Edit|Write|Glob|Grep)$. It merges a marked command hook into ~/.codex/hooks.json for user, or the Git project root's .codex/hooks.json for project; existing hooks are preserved. The configured command is the exact registered binary path followed by codex hook, with a five-second timeout. Review and trust the unmanaged hook through Codex's /hooks workflow as required by Codex.
unregister removes only an entry that carries the Bash Guard marker and exactly matches the binary path recorded at registration. It preserves all other configuration, and deletes hooks.json only when the file would otherwise be empty.
The default policy mode is 0467. The four octal digits control the system, external, network, and workspace scopes from left to right; within each group, read, write, and execute map to 4, 2, and 1:
BASH_GUARD_MODE = 0 4 6 7
| | | |
| | | `- workspace
| | `--- network
| `----- external
`------- system
See the Bash tool policy for permission bits, command categories, recommended modes, and examples.
Set a mode only for the client process you start:
BASH_GUARD_MODE=4447 claude
BASH_GUARD_MODE=4447 codexInvalid modes fail closed as 0000.
Jev is TypeSafe AI's System One model for typed decisions with probabilities. The existing local policy remains the baseline. When no Jev API key is configured, Bash Guard uses that policy exactly as before. Configure a key to add a Jev allow / deny decision before a tool call:
JEV_API_KEY=... JEV_BASE_URL=https://api.typesafe.ai JEV_MODEL=jev-latest claudeJEV_API_KEY takes precedence over TYPESAFE_API_KEY; JEV_BASE_URL (or TYPESAFE_BASE_URL) is extended with /v1/systemone; and JEV_MODEL (or TYPESAFE_DEFAULT_MODEL) defaults to jev-latest. BASH_GUARD_JEV_API_KEY, BASH_GUARD_JEV_ENDPOINT, and BASH_GUARD_JEV_MODEL are Bash Guard-specific highest-priority overrides. If a configured Jev request times out, fails, or returns an invalid response, Bash Guard records the error and falls back to the existing local policy. In the Hook protocol, deny blocks and allow proceeds; set BASH_GUARD_JEV_TIMEOUT_MS (100–30000) to tune the request deadline.
The Hook-supplied absolute cwd is sent as workspace_path. The Jev prompt treats it as the workspace boundary: an external, parent-traversal, or indeterminate target must be denied. It is the client's declared working directory, not a separately discovered Git root.
Jev also receives the normalized active BASH_GUARD_MODE and the complete policy contract: scope order system / external / network / workspace; exact boundaries for protected system and credential paths, external paths, network reads/writes/remote execution, and workspace operations; plus read=4, write=2, execute=1 meanings. The prompt makes that contract authoritative and requires Jev to deny any operation whose required capability is not explicitly granted by the four-digit octal mode.
When Jev blocks an operation, the denial identifies source=jev decision=deny local_policy=unknown. The normal Jev path does not run the local policy, so it must not invent local required/allowed modes or a prose risk explanation. Original policy text appears only on the no-key and Jev-error fallback paths.
Audit logging is enabled by default. Claude Code writes to $HOME/.claude/bash-guard-audit.jsonl; Codex writes to $HOME/.codex/bash-guard-audit.jsonl. To use another path, set BASH_GUARD_AUDIT_LOG before launching the client:
BASH_GUARD_AUDIT_LOG="$HOME/logs/bash-guard.jsonl" codex
tail -f "$HOME/logs/bash-guard.jsonl"Every line is one JSON object and includes client (claude or codex), so a deliberately shared override path remains attributable. cwd records the working directory supplied by the client for that tool call; it is useful for identifying the originating project and is not the path being accessed by the command. When Jev is used, audit rows include decision_source, the choice, probability, model, and (on fallback) jev_error; command summaries redact common credential arguments before they are logged or sent to Jev. If the log directory cannot be created, written, or synchronized, Bash Guard denies the command.
A typical policy denial is:
command blocked by bash safety policy (required=4000 allowed=0467; mode=system/external/network/workspace bits=4:read,2:write,1:execute)
# Remove the Claude Code integration.
bash-guard claude unregister --scope user
# Remove the Codex integration.
bash-guard codex unregister --scope user
# Then remove the Homebrew package, if desired.
brew uninstall bash-guardFor automation or nonstandard installations:
BASH_GUARD_BINARYoverrides the binary path recorded by registration.BASH_GUARD_STATE_DIRoverrides the registration directory (default:~/.claude/bash-guard).
Bash Guard protects only Bash, Read, Edit, Write, Glob, and Grep tool calls issued through registered Claude Code or Codex hooks. Codex PreToolUse hooks are a protective control, not a complete security boundary; Codex may expose shell-like execution paths or other tools not covered by this hook. A user who controls the host can still run shell commands directly, alter their own client configuration, or remove a user-installed integration.
For organization-wide enforcement, administrators should use managed client settings and trusted distribution channels to require the appropriate integration and restrict untrusted configuration changes.
Issues and pull requests are welcome. Please include coverage for policy or Hook-protocol changes, and run the checks below before opening a PR.
cargo fmt --check
cargo test
cargo build --release
printf '%s' '{"hook_event_name":"PreToolUse","tool_name":"Bash","cwd":"/tmp/project","tool_input":{"command":"cat README.md"}}' \
./target/debug/bash-guard codex hook
sh tests/hook.sh ./target/debug/bash-guard
sh tests/codex-registration.sh ./target/debug/bash-guardTo test the repository adapter directly, build the binary first and expose the debug binary as bash-guard on PATH:
PATH="$PWD/target/debug:$PATH" claude --plugin-dir ./plugins/bash-guard
claude plugin validate ./plugins/bash-guard
claude plugin validate .src/
├── main.rs # Hook protocol, audit logging, registration, and status
├── jev.rs # Optional TypeSafe Jev decision client and response validation
└── policy.rs # Permission classification aligned with Bash Agent
plugins/bash-guard/
└── hooks/hooks.json # Directly invokes bash-guard claude hook
Licensed under the MIT License.