cc-allow
Permission control for Claude Code tools. Evaluates bash commands, file operations (Read/Write/Edit), search tools (Glob/Grep), and WebFetch URLs against configurable rules to allow, deny, or defer to Claude Code's permission system.
Features
- AST-based parsing - Uses
mvdan.cc/sh/v3/syntax for accurate bash parsing
- Tool-centric configuration - TOML config organized by tool type (
[bash], [read], [write], [edit], [glob], [grep], [webfetch])
- Specificity-based matching - More specific rules win, regardless of order
- Layered configs - Global defaults with project-level overrides
- Pipe security - Block dangerous patterns like
curl | bash
- Redirect control - Prevent writes to sensitive paths
- File rule integration - Bash commands respect file access rules (e.g.,
cat /etc/passwd denied if /etc/** is in Read deny list)
- Smart argument detection - Pattern-first commands (grep, sed, awk, jq) automatically skip pattern arguments during file rule checking, avoiding false positives
Disclaimer
Bash is inherently dangerous. This tool isn't a substitute for your human judgement. Whatever you let your LLM run is your responsibility.
Installation
As a Claude Code Plugin
-
Add the marketplace:
/plugin marketplace add dannycoates/cc-allow
-
Install the plugin:
/plugin install cc-allow@dannycoates-cc-allow
The plugin automatically downloads binaries on first session start.
Manual Installation
Download from releases or build from source:
go install github.com/dannycoates/cc-allow/cmd/cc-allow@latest
Usage
# Evaluate a bash command (default)
echo 'rm -rf /' | cc-allow
# Exit code: 2 (deny)
# Evaluate file tool permissions
echo '/etc/passwd' | cc-allow --read
echo '/project/src/main.go' | cc-allow --write
echo '/home/user/.bashrc' | cc-allow --edit
# Evaluate search tool permissions
echo '/etc' | cc-allow --glob
echo '/home/user/project' | cc-allow --grep
# Evaluate WebFetch URL permissions
echo 'https://example.com' | cc-allow --fetch
# With explicit config
echo 'ls -la' | cc-allow --config ./my-rules.toml
# Hook mode for Claude Code (reads JSON from stdin)
cc-allow --hook
# Validate and inspect config
cc-allow --fmt
Managing Rules with /allow-rules
The /allow-rules slash command provides a conversational interface for managing cc-allow rules. Tell it what you want in plain English and it figures out the right config changes.
Scope detection
The command determines where to write rules based on your phrasing:
| Scope | Keywords | Config file |
|---|
| Session (default) | "for now", "temporarily", or no scope mentioned | .config/cc-allow/sessions/<id>.toml |
| Project | "always", "permanently", "this project" | .config/cc-allow.toml |
| Global | "globally", "everywhere", "all projects" | ~/.config/cc-allow.toml |
Examples
# Session-scoped (default) — allow docker for this session
/allow-rules allow docker
# Session-scoped — temporary override
/allow-rules let me use curl for now
# Project-scoped — permanent rule
/allow-rules always allow npm install in this project
# Global — applies everywhere
/allow-rules globally deny rm -rf
# Deny rules
/allow-rules block curl | bash
# File tool rules
/allow-rules allow reading /var/log/**
# Complex rules
/allow-rules allow git push but ask for --force
The command reads the appropriate config, makes the change, validates with --fmt, and tests with a matching command to confirm.
Exit Codes
| Code | Action | Meaning |
|---|
| 0 | allow | Command explicitly allowed |
| 1 | ask | Defer to Claude Code's permission system |
| 2 | deny | Command explicitly denied |
| 3 | error | Configuration or parse error |
Configuration
Config Locations
Configs are loaded from multiple locations (loosest to strictest):
~/.config/cc-allow.toml - Global defaults
<project>/.config/cc-allow.toml - Project rules (in source control)
<project>/.config/cc-allow.local.toml - Local overrides (gitignored)
<project>/.config/cc-allow/sessions/<id>.toml - Session-scoped (auto-cleaned)
--config <path> - Explicit config
Rules are merged across configs: deny always wins, allow beats ask, ask means "no opinion."
Quick Start
version = "2.0"
[bash]
default = "ask"
dynamic_commands = "deny" # block $VAR or $(cmd) as command names
[bash.allow]
commands = ["ls", "cat", "grep", "git", "go", "npm"]
[bash.deny]
commands = ["sudo", "rm", "dd"]
message = "Dangerous command blocked"
Rules with Specificity
More specific rules win regardless of order:
# Allow rm in general (specificity: 100)
[[bash.allow.rm]]