Skip to main content

Security configuration scanner for Claude Code

Project description

Clauditor

Security configuration scanner for Claude Code.

Tests Security Lint PyPI Python License

Clauditor audits your Claude Code settings and repository configuration to detect security misconfigurations.


Table of Contents


Features

  • Scans all Claude Code configuration scopes: user, project, local, and managed
  • Checks repository-level files (CODEOWNERS, CLAUDE.md, etc.)
  • Checks are defined as YAML files — easy to read, extend, and contribute
  • Each check maps to a concrete threat, severity, and remediation
  • Scan a local path, a remote git URL, or just the current directory
  • Rich terminal output with optional verbose remediation steps
  • CI-friendly --exit-code flag
  • --base-level flag to enforce a minimum required scope

Installation

pip install clauditor

Or from source:

git clone https://github.com/gabrielsoltz/clauditor
cd clauditor
python3.13 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"

Usage

# Scan current directory (project + user scope settings)
clauditor scan

# Scan a specific local repository
clauditor scan --path /path/to/repo

# Clone and scan a remote repository
clauditor scan --url https://github.com/org/repo

# Scan only user scope (~/.claude/settings.json)
clauditor scan --user-only

# Require settings to be enforced at project level or above
clauditor scan --base-level project

# Require enterprise-wide enforcement (managed only)
clauditor scan --base-level managed

# Filter by severity
clauditor scan --severity CRITICAL,HIGH

# Filter by scope
clauditor scan --scope user,project

# Show remediation steps for failed checks
clauditor scan -v

# Exit with code 1 if any failures found (for CI)
clauditor scan --exit-code

# List all available checks
clauditor list-checks

# Generate a user settings file covering user-scoped checks (default)
clauditor generate

# Generate a project-level settings file (.claude/settings.json)
clauditor generate --scope project

# Generate only for critical checks
clauditor generate --severity CRITICAL

# Generate for specific checks
clauditor generate --checks CC002,CC010,CC011

# Write directly to a file
clauditor generate --scope managed -o managed-settings.json

Configuration Scopes

Claude Code reads settings from multiple locations. Each location is a scope. Understanding scopes is key to understanding Clauditor's output.

The four Claude Code scopes

Column Scope File Who it applies to
M managed System path (platform-specific) Everyone on the machine; deployed by IT
L local .claude/settings.local.json You, in this repo only; gitignored
P project .claude/settings.json All collaborators; committed to git
U user ~/.claude/settings.json You, across all projects

Scope precedence

When the same setting exists in multiple scopes, Claude Code applies the highest-precedence scope:

managed  >  local  >  project  >  user
(highest)                        (lowest)

managed is set by an administrator and cannot be overridden by anyone. local takes precedence over project, which means a developer can use .claude/settings.local.json to override what the team committed in .claude/settings.json.

Repository scope (Clauditor extension)

Column Scope What it checks
R repository VCS governance files: CODEOWNERS, workflow configs, etc.

repository is not a Claude Code scope — it's Clauditor's own concept for checks that look at repository governance files rather than Claude Code JSON settings. It has no precedence relationship with the config scopes above.


How Findings Work

Per-scope icons in the output table

Each scope column shows one icon:

Icon Meaning
Setting is correctly configured at this scope
Setting is present but has the wrong value
Covered — a higher-precedence scope already passes, so this scope is irrelevant
Skipped — the settings file for this scope was not found or is empty
· N/A — this check does not apply to this scope

How the effective (overall) status is decided

The effective status in the Status column is determined by the highest-precedence scope that is not skipped:

  • If managed=PASS → effective is PASS, regardless of lower scopes. All lower scopes show (covered).
  • If managed=FAIL → effective is FAIL, regardless of lower scopes. A wrong value at the top level locks everyone.
  • If managed=–, local=–, project=PASS → effective is PASS, user shows (covered).
  • If all config scopes are (not configured anywhere) → effective is FAIL. A missing setting is not a passing setting.

Scenario examples

Scenario A — Enforced via managed settings (best)

M L P U Status
✔ PASS

Setting is in managed. Everyone on the machine is protected. Lower scopes are irrelevant.

Scenario B — Enforced via project settings (team-level)

M L P U Status
✔ PASS

Setting is in .claude/settings.json (committed to git). All collaborators are protected. User scope is covered.

Scenario C — Only the individual has it set

M L P U Status
✔ PASS

Setting is only in ~/.claude/settings.json. Your own machine is protected, but teammates are not. This is PASS by default, but see --base-level below.

Scenario D — Nobody has it set

M L P U Status
✘ FAIL

The setting is not configured anywhere. This is always FAIL — a missing setting provides no protection.

Scenario E — Wrong value at managed level

M L P U Status
✘ FAIL

Managed has the wrong value. Because managed takes highest precedence, it overrides the correct project/user values. Effective status is FAIL.


--base-level: Enforcing a minimum scope

By default, Clauditor marks a check as PASS if the setting is correctly configured at any scope. However, you may want to require enforcement at a specific level.

# Require the setting to exist at project scope or above (for team-wide enforcement)
clauditor scan --base-level project

# Require enterprise-wide enforcement through managed settings only
clauditor scan --base-level managed

With --base-level project, Scenario C above becomes FAIL — the setting is only personal and doesn't protect the team.

--base-level What passes
user (default) Any scope: user, project, local, or managed
project Must be in project, local, or managed (not just user)
local Must be in local or managed (not just project/user)
managed Only managed qualifies

Check Format

Checks live in the checks/ directory as YAML files. Example structure:

id: CC001
name: CODEOWNERS Enforcement for Claude Code Paths
description: >
  Ensures that /.claude/ and /CLAUDE.md have CODEOWNERS entries
  requiring security team review.

scope:
  - repository

severity: HIGH  # CRITICAL | HIGH | MEDIUM | LOW | INFO

threat: >
  Without CODEOWNERS enforcement, contributors can silently modify
  Claude Code settings, hooks, or instructions without security review...

category: access_control

check_type: file_content  # config_value | config_contains | config_set | file_content | file_exists

check_config:
  search_paths:
    - CODEOWNERS
    - .github/CODEOWNERS
  required_entries:
    - pattern: "/.claude/"
      owner: "@security-team"
    - pattern: "/CLAUDE.md"
      owner: "@security-team"

remediation: >
  Add to CODEOWNERS:
    /.claude/ @security-team
    /CLAUDE.md @security-team

fix_available: true

references:
  - https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners
  - https://code.claude.com/docs/en/settings

Check Types

check_type check_config keys Description
config_value key, expected_value Verifies a key/value in a JSON settings file
config_contains key, required_values Verifies a list key contains all required values
config_set key Verifies a key is present and non-empty (any truthy value)
file_content search_paths, required_entries Verifies required lines exist in a file
file_exists paths, any_of Verifies file(s) exist in the repository

Built-in Checks

ID Name Severity Scope Threat Mitigated
CC001 CODEOWNERS Enforcement for Claude Code Paths HIGH repository Supply chain attacks via unreviewed config changes
CC002 Disable Bypass Permissions Mode CRITICAL user, project, local, managed Unrestricted tool execution via --dangerously-skip-permissions
CC003 Enforce Managed Permission Rules Only LOW managed User/project permission rules bypassing IT policy
CC004 Deny Sensitive File Operations LOW managed Credential theft via .env, secrets/**, credential files
CC005 Disable Auto-Approval of Project MCP Servers LOW managed Supply chain attacks via malicious .mcp.json
CC006 Enforce Managed Hooks Only LOW managed Arbitrary code execution via project/user hooks
CC007 Force SSO Login Method MEDIUM managed Unmanaged personal accounts bypassing corporate identity
CC008 Require SSO Organization UUID MEDIUM managed Cross-tenant auth or unbound SSO enforcement
CC009 Require Approval for Network-Fetching Tools LOW managed Unlogged outbound requests via curl/wget
CC010 Enable Bash Sandboxing LOW user, project, local, managed Unrestricted shell access bypassing permission limits
CC011 Restrict Sandbox Filesystem Write Paths MEDIUM user, project, local, managed Writes to /etc, /usr, ~/.ssh, ~/.aws enabling persistence
CC012 Restrict Sandbox Filesystem Read Paths MEDIUM user, project, local, managed Exfiltration of SSH keys, cloud credentials, .env secrets

Generating a Settings File

clauditor generate produces a ready-to-deploy JSON settings file containing all the values needed to remediate the applicable checks.

clauditor generate                          # All checks → managed settings
clauditor generate --scope project         # Project-level (.claude/settings.json)
clauditor generate --severity CRITICAL     # Only critical checks
clauditor generate --checks CC002,CC010    # Specific checks only
clauditor generate -o managed-settings.json

Scope controls which checks are included:

--scope Checks included
user (default) user-scoped checks only
project project + user checks
local local + project + user checks
managed All config checks: managed + local + project + user

What gets generated:

  • config_value checks → sets the key to its required value
  • config_contains checks → builds/merges the required list entries (e.g. multiple checks writing to permissions.deny are merged automatically)
  • config_set checks → skipped (e.g. forceLoginOrgUUID requires your org-specific UUID; reported separately)
  • file_content / file_exists checks → skipped (repository governance files, not settings values)

Example output (clauditor generate --scope managed):

{
  "disableBypassPermissionsMode": "disable",
  "allowManagedPermissionRulesOnly": true,
  "permissions": {
    "deny": [
      "Read(.env)",
      "Read(**/.env)",
      "Read(secrets/**)",
      "Write(secrets/**)",
      "Read(**/credentials)",
      "Bash(curl:*)",
      "Bash(wget:*)"
    ]
  },
  "enableAllProjectMcpServers": false,
  "allowManagedHooksOnly": true,
  "forceLoginMethod": "claudeai",
  "sandbox": {
    "enabled": true,
    "filesystem": {
      "denyWrite": ["/etc", "/usr", "~/.ssh", "~/.aws"],
      "denyRead": ["~/.ssh", "~/.aws/credentials", ".env", "**/.env"]
    }
  }
}

Adding a Custom Check

  1. Create a new YAML file in checks/ following the format above.
  2. Assign the next available CC### ID.
  3. Run clauditor list-checks to verify it loads correctly.
  4. Run clauditor scan to see results.

No code changes required.


Architecture

clauditor/
├── cli.py              # Typer CLI entry point
├── scanner.py          # Orchestrates checks against providers
├── loader.py           # YAML check loader with Pydantic validation
├── aggregator.py       # Scope precedence + base_level logic
├── generator.py        # Settings file generator
├── models/
│   ├── check.py        # Check, Scope, Severity, CheckType models
│   └── finding.py      # Finding, FindingStatus models
├── providers/
│   ├── base.py         # BaseProvider interface
│   ├── config_provider.py   # User, Project, Local, Managed providers
│   └── repository_provider.py  # Repository file provider + git clone
├── checkers/
│   ├── config_value.py     # Logic for config_value checks
│   ├── config_contains.py  # Logic for config_contains checks
│   ├── config_set.py       # Logic for config_set checks
│   ├── file_content.py     # Logic for file_content checks
│   └── file_exists.py      # Logic for file_exists checks
└── output/
    └── console.py       # Rich terminal output

checks/                  # YAML check definitions

License

Apache 2.0

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

clauditor-0.1.0.tar.gz (39.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

clauditor-0.1.0-py3-none-any.whl (34.1 kB view details)

Uploaded Python 3

File details

Details for the file clauditor-0.1.0.tar.gz.

File metadata

  • Download URL: clauditor-0.1.0.tar.gz
  • Upload date:
  • Size: 39.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for clauditor-0.1.0.tar.gz
Algorithm Hash digest
SHA256 3c37b1e858aebde93416e7efe2357f75fcd05d79f78e63a5a115e80b06e36553
MD5 8cfea4997fbca4fcbd96dda446c1747e
BLAKE2b-256 3a10416844473aba18f42e0b8fddd3fd6ef84b19718165635cdf82ea283c1dac

See more details on using hashes here.

Provenance

The following attestation bundles were made for clauditor-0.1.0.tar.gz:

Publisher: python-publish.yml on gabrielsoltz/clauditor

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file clauditor-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: clauditor-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 34.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for clauditor-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 24f2825d3cb159d80fa792b9cbb23f4f4790c1e5729de589d26069bd99a93594
MD5 232097801c4718454fe66d8b10b1dc04
BLAKE2b-256 e6965787d4c86335de7085d6ef489f92263a58da0b0c7464c46d84f2a3f0ecaa

See more details on using hashes here.

Provenance

The following attestation bundles were made for clauditor-0.1.0-py3-none-any.whl:

Publisher: python-publish.yml on gabrielsoltz/clauditor

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page