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

CommandUsage
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:

  1. Read and parse .claude/settings.local.json from the current directory
  2. Check for .permissions-auditor.yml in the project root:
    • If found, load project-specific tools, categories, and deny gap rules
    • If not found, scan CLAUDE.md for CLI tool references (look for tool names in code blocks, command examples, and setup instructions)
  3. Check if worktrees exist: git worktree list --porcelain 2>/dev/null
  4. If worktrees exist, read settings from all other worktrees
  5. 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 if Bash(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.yml or CLAUDE.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.yml exists, also check project-specific deny gaps.
  6. 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:

  1. Look for command patterns in code blocks: poetry run ..., make ..., npm ..., go ..., etc.
  2. Look for CLI tool names mentioned in setup instructions or common commands sections
  3. Use discovered tools to check for missing allow rules
  4. Skip category ordering (output rules in their existing order)

sync

Copy the current settings to all other worktrees. Requires a worktree setup.

Steps:

  1. Verify worktrees exist via git worktree list --porcelain — if only one worktree, report "nothing to sync" and exit
  2. Read canonical settings from current directory
  3. For each other worktree:
    • Read its current settings.local.json (if exists)
    • Show diff summary (added/removed rules)
    • Ask user for confirmation before overwriting
  4. Write the canonical file to confirmed worktrees
  5. Verify all copies match via MD5 comparison

Design Principles

These principles guide rule consolidation:

  1. Prefer wildcards over subcommand lists: Bash(git:*) not individual Bash(git status:*), Bash(git diff:*), etc.
  2. Don't allow what Claude has dedicated tools for: No Bash(grep:*), Bash(find:*), Bash(cat:*) — use Grep, Glob, Read
  3. Deny destructive operations explicitly: Force push, push to protected branches, production data mutations
  4. Use glob patterns in deny rules: Bash(git*push*--force*) catches git push --force and git -C /path push --force
  5. Group related rules by category (use .permissions-auditor.yml for project-specific ordering)
  6. 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