claude-bash-approve

skill
Guvenlik Denetimi
Uyari
Health Uyari
  • No license — Repository has no license file
  • Description — Repository has a description
  • Active repo — Last push 0 days ago
  • Low visibility — Only 7 GitHub stars
Code Gecti
  • Code scan — Scanned 8 files during light audit, no dangerous patterns found
Permissions Gecti
  • Permissions — No dangerous permissions requested
Purpose
This tool is a Claude Code hook that automatically approves, denies, or prompts the user for Bash commands based on their safety. It parses commands into an AST to evaluate chained commands, wrappers, and control flows before deciding whether to let them execute.

Security Assessment
Risk: Low
The tool functions specifically by evaluating and executing shell commands via Claude's PreToolUse hook. It does not make external network requests, nor does it store or transmit your data. The automated code scan checked 8 files and found no dangerous patterns, hardcoded secrets, or malicious payloads. Its core mechanism is designed to strictly enhance your security by acting as a gatekeeper for terminal commands.

Quality Assessment
The project is currently active, with its most recent push happening today. However, it has very low community visibility, evidenced by only 7 GitHub stars. The most significant drawback is the complete lack of a license file. Without an open-source license, the software is technically proprietary, meaning you have no explicit legal permission to use, modify, or distribute the code.

Verdict
Use with caution — the code itself appears safe, but the lack of a formal software license presents a legal ambiguity for professional or commercial use.
SUMMARY

An alternative take on permissions for claude code.

README.md

claude-bash-approve

A Claude Code PreToolUse hook that auto-approves safe Bash commands and blocks dangerous ones. Written in Go for fast startup.

Install

In Claude Code:

/plugin install github:mariusvniekerk/claude-bash-approve

That's it. The hook registers automatically and the Go binary compiles on first use. Requires Go 1.25+.

How it works

When Claude Code is about to run a Bash command, this hook intercepts it and makes one of four decisions:

  • deny — command is blocked (with a reason shown to Claude)
  • ask — recognized command, user is prompted to confirm (terminal — no further hooks run) (e.g. git tag)
  • no opinion — hook has nothing to say, exits silently so the next hook in the chain can handle it (e.g. git push, gh pr create, or unrecognized commands)
  • allow — command runs immediately, no prompt
flowchart TD
    A["Parse command AST"] --> C{"All segments\nmatched?"}
    C -->|No| NOP["**no opinion**\nnext hook in chain"]
    C -->|Yes| priority
    subgraph priority["Decision priority"]
        D{"any segment\ndenied?"} -->|Yes| DENY["**deny**\nblock command"]
        D -->|No| E{"any segment\nask?"}
        E -->|Yes| ASK["**ask**\nprompt user"]
        E -->|No| F{"any segment\nno-opinion?"}
        F -->|No| OK["**allow**\nrun immediately"]
    end
    F -->|Yes| NOP

Commands are parsed into an AST (using mvdan/sh) so chained commands (&&, ||, ;, |), subshells, command substitutions ($(…)), and control flow (if, for, while) are all handled correctly — every segment must be safe for the whole command to be approved.

Wrappers + Commands

The hook uses a compositional model: a command is split into wrappers (prefixes like timeout 30, env, VAR=val) and a core command (like git status, pytest). Both are matched against regex patterns organized into categories.

Alternative installation

install.sh

git clone https://github.com/mariusvniekerk/claude-bash-approve.git
cd claude-bash-approve
./install.sh

Builds the binary, creates ~/.claude/settings.json if needed, and adds the hook. Pass --force to merge into an existing settings file (requires jq).

Manual setup

  1. Clone this repo:
git clone https://github.com/mariusvniekerk/claude-bash-approve.git
  1. Add the hook to your Claude Code settings (~/.claude/settings.json):
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/claude-bash-approve/hooks/bash-approve/run-hook.sh"
          }
        ]
      }
    ]
  }
}

Replace /path/to/ with the actual path to your clone.

  1. The hook auto-compiles on first run. The run-hook.sh shim rebuilds the Go binary whenever source files change, so there's no manual build step.

Configuration

Command categories are configured in hooks/bash-approve/categories.yaml. When this file is absent or empty, all commands are approved (with some exceptions noted below).

Enabled / Disabled

# Approve everything except git push
enabled:
  - all
disabled:
  - git push
# Only approve git and shell commands
enabled:
  - git
  - shell

disabled always overrides enabled — use it to carve out exceptions.

Default decisions by command

Most matched commands are auto-approved. Some have different defaults:

Decision Commands
deny (blocked, reason shown to Claude) git stash, git revert, git reset --hard, git checkout ., git clean -f, rm -r, go mod vendor, roborev tui
ask (terminal, user prompted) git tag
no-opinion (deferred to next hook) git push, jj git push, gh pr create, go mod init

To override a default, add the specific command name to enabled or disabled.

Available categories

Coarse groups (enable/disable entire ecosystems):

wrapper, git, jj, python, node, rust, make, shell, gh, go, kubectl, gcloud, bq, aws, acli, roborev, docker, ruby, brew, shellcheck, grpcurl

Fine-grained names (within each group):

Group Names
wrapper timeout, nice, env, env vars, .venv, bundle exec, rtk proxy, command, node_modules/.bin, absolute path
git git read op, git write op, git push, git tag, git destructive (git stash, git revert, git reset --hard, git checkout ., git clean -f)
jj jj read op, jj write op, jj git push
python pytest, python, ruff, uv, uvx
node npm, npx, node -e, bun, bunx, vitest
rust cargo, maturin
shell read-only, touch, mkdir, cp -n, ln -s, shell builtin, shell vars, process mgmt, eval, echo, cd, source, sleep, var assignment, shell destructive (rm -r)
go go, go mod vendor, go mod init, golangci-lint, ginkgo
gh gh read op, gh pr create, gh write op, gh api
kubectl kubectl read op, kubectl write op, kubectl port-forward, kubectl exec, kubectl cp
docker docker, docker compose, docker-compose
ruby rspec, rake, ruby, rails, bundle, gem, rubocop, solargraph, standardrb

See categories.yaml for the full reference with examples.

Telemetry

Every decision is logged to a local SQLite database (telemetry.db, next to the binary). This lets you review what the hook approved, denied, or passed through:

sqlite3 hooks/bash-approve/telemetry.db "SELECT ts, decision, command, reason FROM decisions ORDER BY ts DESC LIMIT 20"

Telemetry is best-effort — if the database can't be opened or written to, the hook continues normally.

Debugging

Test the hook directly by piping JSON to stdin:

echo '{"tool_name":"Bash","tool_input":{"command":"git status"}}' | \
  go run ./hooks/bash-approve/

Output is a JSON object with the decision:

{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow","permissionDecisionReason":"git read op"}}

No output (exit 0) means the hook has no opinion.

Running tests

cd hooks/bash-approve
go test -v ./...

Discovering new patterns

The project includes a Claude Code skill (.claude/skills/bash-approve-telemetry/) that queries the telemetry database to find commands that need new rules. Ask Claude to "check the telemetry for approval candidates" or invoke /bash-approve-telemetry.

Adding new commands

  1. Add a NewPattern(...) entry to allCommandPatterns or allWrapperPatterns in hooks/bash-approve/rules.go
  2. Choose the right decision:
    • allow (default) — auto-approve
    • WithDecision("deny") + WithDenyReason("...") — block with reason
    • WithDecision("ask") — terminal prompt to user
    • WithDecision("") — no opinion, defer to next hook
  3. Add test cases in main_test.go
  4. Update the category listing in categories.yaml if introducing a new group
  5. Run go test ./...

Yorumlar (0)

Sonuc bulunamadi