Security configuration scanner for Claude Code
Project description
Clauditor
Security configuration scanner for Claude Code.
Clauditor audits your Claude Code settings and repository configuration to detect security misconfigurations.
Table of Contents
- Features
- Installation
- Usage
- Configuration Scopes
- How Findings Work
- Enforcing a Minimum Scope
- Check Format
- Built-in Checks
- Generating a Settings File
- Adding a Custom Check
- Architecture
- License
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-codeflag --base-levelflag 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_valuechecks → sets the key to its required valueconfig_containschecks → builds/merges the required list entries (e.g. multiple checks writing topermissions.denyare merged automatically)config_setchecks → skipped (e.g.forceLoginOrgUUIDrequires your org-specific UUID; reported separately)file_content/file_existschecks → 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
- Create a new YAML file in
checks/following the format above. - Assign the next available
CC###ID. - Run
clauditor list-checksto verify it loads correctly. - Run
clauditor scanto 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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3c37b1e858aebde93416e7efe2357f75fcd05d79f78e63a5a115e80b06e36553
|
|
| MD5 |
8cfea4997fbca4fcbd96dda446c1747e
|
|
| BLAKE2b-256 |
3a10416844473aba18f42e0b8fddd3fd6ef84b19718165635cdf82ea283c1dac
|
Provenance
The following attestation bundles were made for clauditor-0.1.0.tar.gz:
Publisher:
python-publish.yml on gabrielsoltz/clauditor
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
clauditor-0.1.0.tar.gz -
Subject digest:
3c37b1e858aebde93416e7efe2357f75fcd05d79f78e63a5a115e80b06e36553 - Sigstore transparency entry: 1088906004
- Sigstore integration time:
-
Permalink:
gabrielsoltz/clauditor@b896d1298a7e7055ea19128bb35d8354408a6945 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/gabrielsoltz
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@b896d1298a7e7055ea19128bb35d8354408a6945 -
Trigger Event:
release
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
24f2825d3cb159d80fa792b9cbb23f4f4790c1e5729de589d26069bd99a93594
|
|
| MD5 |
232097801c4718454fe66d8b10b1dc04
|
|
| BLAKE2b-256 |
e6965787d4c86335de7085d6ef489f92263a58da0b0c7464c46d84f2a3f0ecaa
|
Provenance
The following attestation bundles were made for clauditor-0.1.0-py3-none-any.whl:
Publisher:
python-publish.yml on gabrielsoltz/clauditor
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
clauditor-0.1.0-py3-none-any.whl -
Subject digest:
24f2825d3cb159d80fa792b9cbb23f4f4790c1e5729de589d26069bd99a93594 - Sigstore transparency entry: 1088906039
- Sigstore integration time:
-
Permalink:
gabrielsoltz/clauditor@b896d1298a7e7055ea19128bb35d8354408a6945 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/gabrielsoltz
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@b896d1298a7e7055ea19128bb35d8354408a6945 -
Trigger Event:
release
-
Statement type: