A Claude Code plugin that detects and blocks potential secrets before git commits.
# Add the marketplace
/plugin marketplace add /path/to/claude-plugins
# Install the plugin
/plugin install security-hooks@plugins-by-jamesThat's it. Pre-built binaries are included for all platforms. The hook automatically scans all git commit operations for secrets.
For development: Clone the repo and run make build to compile locally.
- What Are Security Hooks?
- How It Works
- Secret Patterns Detected
- Smart Filtering
- Output Format
- Hook Configuration
- Exit Codes
- Usage Examples
- Running Tests
- Environment Variables
- Remediation Guidance
- Best Practices
- Limitations
- Debugging
- License
Security hooks are PreToolUse hooks that run before git operations to scan staged files for secrets, API keys, credentials, and sensitive data. They act as a final security gate before code is committed.
Key Features:
- Fail-closed design (errors block commits for safety)
- TOCTOU-safe (reads from git staging area, not disk)
- Zero external dependencies (Go stdlib only)
- 26 pre-compiled regex patterns for fast detection
- Hardcoded .env value detection with word boundaries
- Compiled binary for instant startup
Scans for 26 common secret patterns using pre-compiled regex.
- Parses
.envfile for environment variable values - Searches staged files for exact matches using word boundaries
- Catches secrets that are copy-pasted from .env into source code
1. Receive Bash tool call with "git commit" command
2. Get CLAUDE_PROJECT_DIR (or cwd)
3. Parse .env file (if exists)
4. Get staged files: git diff --cached --name-only
5. For each staged file:
- Skip binary files (30+ extensions)
- Skip .env files
- Skip files >10MB or <10 bytes
- Read content from git staging area
- Check against 26 secret patterns
- Check against hardcoded .env values
6. If secrets found: block commit with detailed report (exit 2)
7. On errors: block commit (exit 2, fail-closed)
| Pattern | Example Format |
|---|---|
| AWS Access Key ID | AKIA[0-9A-Z]{16} |
| AWS Secret Access Key | aws_secret_access_key = ... |
| Google API Key | AIza[0-9A-Za-z-_]{35} |
| Pattern | Example Format |
|---|---|
| OpenAI API Key | sk-[a-zA-Z0-9]{20,} |
| OpenAI Project Key | sk-proj-[a-zA-Z0-9]{20,} |
| Anthropic API Key | sk-ant-[a-zA-Z0-9-]{20,} |
| Pattern | Example Format |
|---|---|
| GitHub PAT | ghp_[a-zA-Z0-9]{36} |
| GitHub OAuth Token | gho_[a-zA-Z0-9]{36} |
| GitHub User Token | ghu_[a-zA-Z0-9]{36} |
| GitHub Server Token | ghs_[a-zA-Z0-9]{36} |
| GitHub Refresh Token | ghr_[a-zA-Z0-9]{36} |
| npm Access Token | npm_[a-zA-Z0-9]{36} |
| PyPI API Token | pypi-[a-zA-Z0-9]{43,} |
| Pattern | Example Format |
|---|---|
| Slack Token | xox[baprs]-[a-zA-Z0-9-]{10,} |
| Discord Bot Token | [MN][A-Za-z\d]{23,}.[A-Za-z\d_-]{6}.[A-Za-z\d_-]{27} |
| Twilio API Key | SK[a-fA-F0-9]{32} |
| SendGrid API Key | SG.[a-zA-Z0-9_-]{20,}.[a-zA-Z0-9_-]{20,} |
| Mailgun API Key | key-[a-zA-Z0-9]{32} |
| Pattern | Example Format |
|---|---|
| Stripe Secret Key | sk_live_[a-zA-Z0-9]{24,} |
| Stripe Restricted Key | rk_live_[a-zA-Z0-9]{24,} |
| Pattern | Example Format |
|---|---|
| PostgreSQL | postgres(ql)?://user:pass@host |
| MySQL | mysql://user:pass@host |
| MongoDB | mongodb(+srv)?://user:pass@host |
| Pattern | Description |
|---|---|
| Private Keys | -----BEGIN (RSA|DSA|EC|OPENSSH) PRIVATE KEY----- |
| Bearer Tokens | bearer [a-zA-Z0-9_-.]{20,} |
| Generic Secrets | (secret|token)\s*[:=]\s*[value]{20,} |
Images: .png, .jpg, .jpeg, .gif, .ico, .svg
Archives: .zip, .tar, .gz, .7z
Executables: .exe, .dll, .so, .bin
Compiled: .wasm, .pyc, .class
Fonts: .woff, .woff2, .ttf, .eot
Media: .mp3, .mp4, .mov
Databases: .db, .sqlite, .sqlite3, .dat
Other: .pdf, .lock, .min.js, .min.css
.env, .env.local, .env.production, .env.development, .env.test, .env.staging, .env.example
Plus any file matching .env.* pattern.
Non-Secret Values Ignored:
- Booleans:
true,false,yes,no,on,off - Environments:
development,production,staging,test - Common URLs:
localhost,127.0.0.1,0.0.0.0 - Encodings:
utf-8,utf8,none,null
Value Filtering:
- Minimum 8 characters
- Numeric-only values skipped
SECURITY WARNING: Potential secrets detected in staged files!
Pattern-based detections:
- src/api.js:42 - Found potential GitHub Personal Access Token
- config/settings.ts:15 - Found potential OpenAI API key
Hardcoded .env values detected:
- src/config.py:8 - Found hardcoded value from .env key 'DATABASE_PASSWORD'
Please remove secrets before committing.
Use environment variables at runtime instead of hardcoding values.
Consider using a secrets manager for sensitive credentials.
Location: hooks/hooks.json
{
"description": "Security hooks for detecting secrets before git commit",
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/bin/check-secrets",
"timeout": 30
}
]
}
]
}
}| Code | Meaning | Action |
|---|---|---|
| 0 | No secrets / Non-commit command | Commit proceeds |
| 2 | Secrets detected | Commit blocked with deny JSON |
| 2 | Error (parse, git failure, timeout) | Commit blocked (fail-closed) |
Clean commit:
git commit -m "Add feature"
# -> check-secrets scans staged files
# -> No secrets found
# -> Commit proceedsBlocked commit:
git commit -m "Add API integration"
# -> check-secrets scans staged files
# -> Found: sk-abc123... in src/api.py:42
# -> Commit blocked with detailed report (exit 2)# Build and test
make install
# Run tests only
make test
# Run tests directly with verbose output
cd scripts && go test -v -race -cover ./...
# Run a specific test
cd scripts && go test -v -run Test_CheckFileForSecrets ./...Test Coverage: 87+ tests across 6 test files covering:
- Pattern compilation and constant verification
.envparsing and filtering edge cases- Binary/env file detection
- All 26 secret patterns + false positive prevention
- Git commit command detection
- Full integration tests via compiled binary
| Variable | Description |
|---|---|
CLAUDE_PROJECT_DIR |
Project root for .env file lookup |
CLAUDE_PLUGIN_ROOT |
Plugin installation directory |
- Remove the hardcoded secret from the file
- Use environment variables:
apiKey := os.Getenv("API_KEY")
- Ensure .env is gitignored
- Use a secrets manager for production
- Rotate exposed secrets if accidentally committed
- Never hardcode secrets
- Use separate credentials per environment
- Document required environment variables
- Inject secrets at deploy time
- Scan commit history with
git-secrets - Audit secret access
- Cannot catch obfuscated secrets
- Pattern-based only (unknown formats won't match)
- Context-blind (secrets in comments still flagged)
- Requires .env file for hardcoded value detection
- Files >10MB skipped
- Requires Go 1.22+ to build
- Verify hook config: Check
hooks/hooks.json - Test manually:
echo '{"tool_name": "Bash", "tool_input": {"command": "git commit -m test"}}' | \ ./bin/check-secrets
- Check stderr for error messages
- Enable debug mode:
claude --debug - Rebuild after changes:
make build
MIT