secret-scanner
Scans the full git history of a repository for leaked secrets, API keys, tokens, and credentials. Triggered when a user asks to audit commits for exposed credentials, run a pre-publish security check, or scan git history for sensitive data. Produces a severity-ranked findings table with remediation commands. Read-only — never modifies git history automatically.
Secret Scanner
Scan the entire git history of a repository for leaked secrets, credentials, and sensitive tokens.
When to Trigger
- User asks to scan for secrets or leaked keys
- User wants to check git history for exposed credentials
- User requests a pre-publish or open-source readiness security check
- User asks to audit commits before making a private repo public
Scan Methodology
Step 1: Extract All Historical Diffs
Do NOT scan only the working tree. Secrets may exist in deleted files or amended commits.
# Get all diffs across entire history (all branches, all commits)
git log -p --all --diff-filter=ACMR --no-color
For targeted scanning of specific branches:
git log -p <branch> --no-color
For scanning only added files (initial introductions of secrets):
git log --all --diff-filter=A --name-only --pretty=format:"%H %ai"
Step 2: Scan Each Diff for Secret Patterns
Apply regex patterns against every + line (additions) in every diff hunk.
Secret Patterns
CRITICAL Severity
| Provider | Pattern |
|---|---|
| AWS Key ID | AKIA[0-9A-Z]{16} |
| AWS Secret | aws_secret_access_key\s*[:=]\s*['"]?[A-Za-z0-9/+=]{40} |
| GitHub PAT | ghp_[0-9a-zA-Z]{36} |
| GitHub OAuth | gho_[0-9a-zA-Z]{36} |
| GitHub Fine | github_pat_[0-9a-zA-Z_]{82} |
| Stripe Live | sk_live_[0-9a-zA-Z]{24,} |
| Stripe Restricted | rk_live_[0-9a-zA-Z]{24,} |
| Private Key | -----BEGIN (RSA|EC|DSA )?PRIVATE KEY----- |
HIGH Severity
| Provider | Pattern |
|---|---|
| Sentry Auth | sntryu_[0-9a-f]{64} |
| Slack Token | xox[bpors]-[0-9a-zA-Z-]{10,} |
| Vercel Token | [A-Za-z0-9]{24} (in context of VERCEL_TOKEN or header) |
| SendGrid | SG\.[0-9A-Za-z_-]{22}\.[0-9A-Za-z_-]{43} |
| Twilio | SK[0-9a-fA-F]{32} |
| Supabase Key | eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9\.[A-Za-z0-9_-]+ |
MEDIUM Severity
| Type | Pattern |
|---|---|
| Generic password | password\s*[:=]\s*['"][^'"]{8,}['"] |
| Generic secret | secret\s*[:=]\s*['"][^'"]{8,}['"] |
| Generic token | token\s*[:=]\s*['"][^'"]{16,}['"] |
| Connection string | (mongodb\+srv|postgres|mysql):\/\/[^\s'"]+ |
| Base64 blob | [A-Za-z0-9+/=]{40,} (contextual — only flag when near key/token/secret keywords) |
Output Format
Present findings as a markdown table sorted by severity:
| # | Severity | Commit | Date | File | Pattern | Snippet (masked) |
|---|----------|------------|------------|-----------------------|-----------------|------------------|
| 1 | CRITICAL | a1b2c3d | 2025-03-15 | src/config.ts | AWS Key ID | AKIA****XXXX |
| 2 | CRITICAL | e4f5g6h | 2025-02-01 | .env | Stripe Live Key | sk_live_**** |
| 3 | HIGH | i7j8k9l | 2025-01-20 | lib/sentry.js | Sentry Auth | sntryu_**** |
| 4 | MEDIUM | m0n1o2p | 2024-12-10 | docker-compose.yml | Generic password| ******** |
Always mask the middle portion of any found secret. Never display full credentials in output.
Remediation Guidance
For each finding, provide:
1. Credential Rotation (IMMEDIATE)
CRITICAL: Rotate ALL found credentials immediately.
- AWS: IAM Console > Security Credentials > Create New Access Key > Deactivate Old
- GitHub: Settings > Developer Settings > Personal Access Tokens > Regenerate
- Stripe: Dashboard > Developers > API Keys > Roll Key
- Sentry: Settings > Auth Tokens > Revoke & Create New
2. Remove from Git History
Use git filter-repo (NOT git filter-branch which is deprecated):
# Install if needed
pip install git-filter-repo
# Remove a specific file from all history
git filter-repo --invert-paths --path <file-path>
# Replace a specific string across all history
git filter-repo --replace-text <(echo 'AKIA1234567890ABCDEF==>REDACTED')
3. Force Push (Required After History Rewrite)
# WARNING: This rewrites shared history. Coordinate with all collaborators.
git push --force --all
git push --force --tags
Warn the user explicitly:
- Force-push is required after
filter-repo— this rewrites ALL commit SHAs - All collaborators must re-clone or
git fetch --all && git reset --hard origin/<branch> - GitHub/GitLab cached views may still show old commits for ~24 hours
4. Prevent Future Leaks
Recommend adding a .gitignore entry and a pre-commit hook:
# .gitignore additions
.env
.env.local
.env.*.local
*.pem
*.key
# Pre-commit hook (save as .git/hooks/pre-commit)
#!/bin/bash
if git diff --cached | grep -qE 'AKIA|sk_live_|ghp_|-----BEGIN.*PRIVATE KEY'; then
echo "ERROR: Potential secret detected in staged changes. Aborting commit."
exit 1
fi
Safety Rules
- Read-only scan — never modify git history, files, or configuration automatically
- Always show findings first — let the user decide what to remediate
- Mask secrets in output — never display full credentials, even in tool output
- No network calls — scan is entirely local, no data leaves the machine
- Confirm before filter-repo — if the user asks to remediate, confirm the exact commands before execution