secret-scanner
Detects API keys, passwords, and secrets in code before they reach git. Use before commits, when working with credentials, or when user mentions "security check" or "secrets".
When & Why to Use This Skill
The Secret Scanner is a proactive security tool designed to prevent the accidental exposure of sensitive credentials in version control. It automatically detects API keys, passwords, JWT tokens, and private keys within your code before they reach Git repositories. By integrating regex-based scanning with actionable remediation advice—such as environment variable migration and secret rotation—this skill acts as a critical guardrail for developers to maintain high security standards and avoid costly data breaches.
Use Cases
- Pre-commit Security Audits: Automatically scan staged files during the 'git commit' process to block any hardcoded secrets from entering the repository history.
- Credential Remediation: Identify existing hardcoded strings (like OpenAI or AWS keys) and receive step-by-step guidance on moving them to .env files or secret management services.
- Legacy Code Cleanup: Perform manual deep scans across entire project directories to find and rotate old or forgotten credentials in legacy scripts.
- Security Best Practice Implementation: Use the tool to generate standardized .gitignore files and Pydantic-based configuration patterns to ensure long-term credential safety.
| name | secret-scanner |
|---|---|
| description | Detects API keys, passwords, and secrets in code before they reach git. Use before commits, when working with credentials, or when user mentions "security check" or "secrets". |
| allowed-tools | Bash(git:*, grep:*) |
| - matcher | "Bash(git commit*)" |
| - type | command |
| command | | |
Secret Scanner
What This Skill Does
Automatically scans code for hardcoded secrets BEFORE they reach git:
- API keys
- Passwords
- Access tokens
- Private keys
- Database credentials
- Any sensitive data
Detection Patterns
High-Risk Patterns (Always Block)
# ❌ Will be blocked
API_KEY = "sk_live_1234567890abcdef"
SECRET_KEY = "supersecretpassword123"
PASSWORD = "admin123"
AWS_SECRET = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
PRIVATE_KEY = "-----BEGIN RSA PRIVATE KEY-----"
DATABASE_URL = "postgresql://user:pass@localhost/db"
Safe Patterns (Will Pass)
# ✅ Safe - environment variables
API_KEY = os.getenv("API_KEY")
SECRET_KEY = settings.secret_key
PASSWORD = env.str("DATABASE_PASSWORD")
# ✅ Safe - configuration references
api_key: str = Field(..., env="API_KEY")
Regex Patterns Used
The scanner looks for:
# API keys and secrets
(API_KEY|SECRET|PASSWORD|TOKEN|PRIVATE_KEY)\s*=\s*["'][^"']{8,}["']
# JWT tokens
eyJ[A-Za-z0-9-_=]+\.eyJ[A-Za-z0-9-_=]+\.[A-Za-z0-9-_.+/=]*
# AWS keys
AKIA[0-9A-Z]{16}
# Database URLs with credentials
(postgres|mysql|mongodb):\/\/[^:]+:[^@]+@
# Private keys
-----BEGIN (RSA|DSA|EC) PRIVATE KEY-----
How to Handle Detected Secrets
Step 1: Remove from code
# Before (❌ blocked)
OPENAI_KEY = "sk-proj-1234567890abcdef"
# After (✅ passes)
OPENAI_KEY = os.getenv("OPENAI_KEY")
Step 2: Add to .env file
# .env (never committed!)
OPENAI_KEY=sk-proj-1234567890abcdef
DATABASE_URL=postgresql://user:pass@localhost/db
SECRET_KEY=your-secret-key-here
Step 3: Add to .env.example
# .env.example (can be committed)
OPENAI_KEY=sk-proj-xxx
DATABASE_URL=postgresql://user:pass@localhost/dbname
SECRET_KEY=generate-with-openssl-rand-hex-32
Step 4: Update .gitignore
# Environment variables
.env
.env.local
.env.production
# Secrets
secrets/
*.key
*.pem
*.cert
Pydantic Settings Integration
Use pydantic-settings for type-safe config:
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
"""Application settings loaded from environment."""
model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8",
case_sensitive=False,
)
# Database
database_url: str
# API Keys
openai_api_key: str
sentry_dsn: str | None = None
# Security
secret_key: str
algorithm: str = "HS256"
access_token_expire_minutes: int = 30
# Usage
settings = Settings()
# Now use settings.database_url instead of os.getenv()
Manual Scan Command
Run scanner manually:
# Scan all Python files
grep -rnE '(API_KEY|SECRET|PASSWORD|TOKEN)\s*=\s*["'\''][^"'\'']{8,}' app/
# Scan staged files only
git diff --cached --name-only | xargs grep -nHE 'API_KEY.*=.*["\047]'
# Check for common patterns
rg 'sk-[a-zA-Z0-9]{20,}' app/ # OpenAI keys
rg 'AKIA[0-9A-Z]{16}' app/ # AWS keys
rg 'ghp_[a-zA-Z0-9]{36}' app/ # GitHub tokens
Common Secret Types
OpenAI API Keys
# ❌ Bad
OPENAI_KEY = "sk-proj-1234567890"
# ✅ Good
OPENAI_KEY = os.getenv("OPENAI_KEY")
Database Credentials
# ❌ Bad
DATABASE_URL = "postgresql://admin:secretpass@localhost/mydb"
# ✅ Good
DATABASE_URL = settings.database_url
JWT Secrets
# ❌ Bad
SECRET_KEY = "my-super-secret-key-12345"
# ✅ Good
SECRET_KEY = os.getenv("SECRET_KEY")
# Generate with: openssl rand -hex 32
AWS Credentials
# ❌ Bad
AWS_ACCESS_KEY_ID = "AKIAIOSFODNN7EXAMPLE"
AWS_SECRET_ACCESS_KEY = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
# ✅ Good
# Use AWS CLI credentials or instance roles
import boto3
s3 = boto3.client('s3') # Uses ~/.aws/credentials
Secrets Already Committed?
If you accidentally committed secrets:
Step 1: Rotate the secret
# Generate new API key immediately!
# Old key is now compromised and must be revoked
Step 2: Remove from git history
# Remove file from all commits
git filter-branch --force --index-filter \
'git rm --cached --ignore-unmatch path/to/file.py' \
--prune-empty --tag-name-filter cat -- --all
# Force push (if remote)
git push origin --force --all
Step 3: Add to .gitignore
echo "config/secrets.py" >> .gitignore
git add .gitignore
git commit -m "security: prevent secrets in git"
Best Practices
1. Use Environment Variables
import os
from functools import lru_cache
@lru_cache
def get_settings():
return Settings()
settings = get_settings()
2. Separate Config Files
project/
├── config/
│ ├── settings.py # Settings class
│ ├── production.py # Prod config (no secrets!)
│ └── development.py # Dev config (no secrets!)
├── .env # Secrets (gitignored!)
└── .env.example # Template (committed)
3. Use Secret Management
# For production, use secret managers
from google.cloud import secretmanager
def get_secret(secret_id: str) -> str:
"""Fetch secret from Google Secret Manager."""
client = secretmanager.SecretManagerServiceClient()
name = f"projects/{PROJECT_ID}/secrets/{secret_id}/versions/latest"
response = client.access_secret_version(request={"name": name})
return response.payload.data.decode("UTF-8")
4. Docker Secrets
# docker-compose.yml
services:
app:
secrets:
- db_password
- api_key
secrets:
db_password:
file: ./secrets/db_password.txt
api_key:
file: ./secrets/api_key.txt
False Positives
If scanner blocks legitimate code:
Option 1: Use different variable name
# Instead of
API_KEY_EXAMPLE = "sk-test-example" # Blocked
# Use
EXAMPLE_API_KEY_FORMAT = "sk-test-xxx" # May pass
Option 2: Comment explanation
# Example key format for documentation (not real)
# Scanner: ignore-secret
API_KEY_FORMAT = "sk-live-xxxxxxxxxxxx"
Option 3: Move to documentation
<!-- README.md -->
API keys should follow format: `sk-live-xxxxxxxxxxxx`
Integration with Git Hooks
Add to .git/hooks/pre-commit:
#!/bin/bash
echo "🔍 Scanning for secrets..."
# Run secret scanner
if git diff --cached --name-only | xargs grep -nHE '(API_KEY|SECRET|PASSWORD|TOKEN)\s*=\s*["'\''][^"'\'']{8,}'; then
echo ""
echo "❌ Potential secrets detected in staged files!"
echo "Please remove hardcoded secrets and use environment variables."
echo ""
echo "Quick fix:"
echo " 1. Move secret to .env file"
echo " 2. Use os.getenv('SECRET_NAME') in code"
echo " 3. Stage changes and retry commit"
exit 1
fi
echo "✅ No secrets detected"
exit 0
Monitoring and Alerts
Set up monitoring for secret exposure:
# Use Sentry to track potential secret leaks
import sentry_sdk
def check_for_secrets_in_logs(log_message: str) -> bool:
"""Check if log message contains potential secrets."""
patterns = [
r'sk-[a-zA-Z0-9]{20,}', # OpenAI keys
r'AKIA[0-9A-Z]{16}', # AWS keys
r'ghp_[a-zA-Z0-9]{36}', # GitHub tokens
]
for pattern in patterns:
if re.search(pattern, log_message):
sentry_sdk.capture_message(
"Potential secret in logs",
level="warning",
)
return True
return False
Remember
Once a secret is committed, consider it compromised.
Always:
- ✅ Rotate compromised secrets immediately
- ✅ Use environment variables
- ✅ Keep .env in .gitignore
- ✅ Use secret managers in production
- ✅ Scan before every commit
Never:
- ❌ Commit secrets to git
- ❌ Share secrets in Slack/email
- ❌ Reuse secrets across environments
- ❌ Store secrets in code comments
- ❌ Log secret values