permissions-auditor
Use when Claude Code settings.local.json has grown unwieldy, worktrees have drifted out of sync, or you suspect redundant/missing permission rules
Permissions Auditor
Audit and sync Claude Code settings.local.json for any project. Works as a standalone rule quality checker or across worktrees for drift detection and syncing.
Detects redundant rules, anti-patterns, malformed entries, and missing permissions — out of the box with zero configuration. Add a .permissions-auditor.yml file to your project for project-specific tool checks, category ordering, and custom deny gap rules.
When to Use
- Permission rules have accumulated organically and may have redundancies
- Worktrees have drifted to different permission sets
- After adding new tools (MCP servers, CLI tools) to check for missing allow rules
- After a security review to check for deny gaps
- Single checkout — audit still works for rule quality analysis
Commands
| Command | Usage |
|---|---|
| audit | /permissions-auditor audit — analyze rules quality (+ cross-worktree drift if worktrees exist) |
| sync | /permissions-auditor sync — copy canonical settings to all other worktrees (requires worktree setup) |
If no command is given, default to audit.
audit
Analyze the current settings file for quality issues. If worktrees exist, also compare across them.
Steps:
- Read and parse
.claude/settings.local.jsonfrom the current directory - Check for
.permissions-auditor.ymlin the project root:- If found, load project-specific tools, categories, and deny gap rules
- If not found, scan
CLAUDE.mdfor CLI tool references (look for tool names in code blocks, command examples, and setup instructions)
- Check if worktrees exist:
git worktree list --porcelain 2>/dev/null - If worktrees exist, read settings from all other worktrees
- Check for issues in the current settings (using all worktree data if available):
- Redundant rules: Specific subcommands covered by an existing wildcard (e.g.,
Bash(git status:*)is redundant ifBash(git:*)exists) - Duplicate rules: Exact duplicates in allow or deny lists
- Anti-patterns:
Bash(grep:*),Bash(find:*),Bash(cat:*)— Claude Code has dedicated Grep, Glob, Read tools - Malformed rules: Missing closing paren, wrong separators
- Missing rules: Check against tools discovered from
.permissions-auditor.ymlorCLAUDE.md. If worktrees exist, flag rules present in any other worktree but absent from canonical. - Deny gaps: Universal destructive operations not denied (force push, push to main/master,
rm -rf). If.permissions-auditor.ymlexists, also check project-specific deny gaps.
- Redundant rules: Specific subcommands covered by an existing wildcard (e.g.,
- Report rule counts, issues, and suggested improvements
Report sections:
- Single checkout: Rule counts -> Issues found (grouped by type)
- With worktrees: Rule counts -> Cross-worktree comparison table -> Issues found -> Suggested additions from other worktrees
Tool Discovery (no config file)
When no .permissions-auditor.yml is present, scan CLAUDE.md for tool references:
- Look for command patterns in code blocks:
poetry run ...,make ...,npm ...,go ..., etc. - Look for CLI tool names mentioned in setup instructions or common commands sections
- Use discovered tools to check for missing allow rules
- Skip category ordering (output rules in their existing order)
sync
Copy the current settings to all other worktrees. Requires a worktree setup.
Steps:
- Verify worktrees exist via
git worktree list --porcelain— if only one worktree, report "nothing to sync" and exit - Read canonical settings from current directory
- For each other worktree:
- Read its current
settings.local.json(if exists) - Show diff summary (added/removed rules)
- Ask user for confirmation before overwriting
- Read its current
- Write the canonical file to confirmed worktrees
- Verify all copies match via MD5 comparison
Design Principles
These principles guide rule consolidation:
- Prefer wildcards over subcommand lists:
Bash(git:*)not individualBash(git status:*),Bash(git diff:*), etc. - Don't allow what Claude has dedicated tools for: No
Bash(grep:*),Bash(find:*),Bash(cat:*)— use Grep, Glob, Read - Deny destructive operations explicitly: Force push, push to protected branches, production data mutations
- Use glob patterns in deny rules:
Bash(git*push*--force*)catchesgit push --forceandgit -C /path push --force - Group related rules by category (use
.permissions-auditor.ymlfor project-specific ordering) - Include MCP tools by full name:
mcp__server__tool_name, not abbreviated
Universal Deny Gap Checks
These destructive operations are always checked, regardless of configuration:
- Bash(git*push*--force*) # Force push
- Bash(git*push*origin*main*) # Push to main
- Bash(git*push*origin*master*) # Push to master
- Bash(rm*-rf*) # Recursive force delete
- Bash(git*reset*--hard*) # Hard reset (if not in allow list intentionally)
Configuration
Create a .permissions-auditor.yml file in your project root for project-specific checks. See examples/ in the plugin repo for samples.
Config file format:
# .permissions-auditor.yml
# Tools your project uses (checked for missing allow rules)
tools:
- name: <tool-command>
description: <what it does>
# Category ordering for organized settings (optional)
categories:
allow:
- name: "<category name>"
patterns: ["<prefix1>", "<prefix2>"]
deny:
- name: "<category name>"
patterns: ["<prefix1>", "<prefix2>"]
# Additional deny gap checks beyond universal protections
deny_gaps:
- pattern: "<glob pattern>"
reason: "<why this should be denied>"
If no config file is found, the auditor works in zero-config mode using CLAUDE.md scanning and universal checks only.
Category Ordering
When a .permissions-auditor.yml defines categories, rules in settings.local.json will be checked for proper grouping. Example ordering for a Python/dbt project:
allow:
1. Build tools — poetry, make
2. Database — database CLI tools, MCP database tools
3. Git — Bash(git:*)
4. GitHub CLI — Bash(gh:*)
5. CI/CD — CI runner tools
6. Cloud CLI — Bash(aws/gcloud/az:*)
7. Search/Knowledge — WebSearch, WebFetch
8. Shell utilities — Bash(ls/wc:*)
9. IDE tools — Bash(claude:*), skills
deny:
1. Destructive build — make clean, rm -rf
2. Destructive DB — DELETE, DROP, TRUNCATE
3. Destructive git — force push, push to main/master
This ordering is fully customizable via the config file. Without a config file, no ordering is enforced.
Common Mistakes
- Scoping Bash too narrowly then adding exceptions: Better to use
Bash(git:*)than listing 10 git subcommands - Forgetting deny rules: Allow rules alone aren't sufficient — explicitly deny destructive operations
- Syncing without auditing first: Always audit the canonical source before propagating it
- Including
Bash(cat:*)in allow: Use the Read tool instead; allowing cat bypasses Claude's file-reading UX - Skipping the config file: Zero-config mode works, but a config file catches project-specific gaps that CLAUDE.md scanning might miss