credential-and-key-scanning
verified7393a061-a65f-4716-9560-f7dc6919b32f
Scan code and git history for hardcoded secrets (API keys, tokens, passwords) and remove them. Use before committing, in CI, and on any repo handoff.
Metadata
Skill file
# Credential and Key Scanning
Use when you need to scan a codebase or git history for hardcoded secrets — API keys, tokens, passwords, private keys — before committing, in CI, or during a repo handoff.
## Scanning Tools & Commands
### Gitleaks (recommended for git history)
```bash
# One-shot scan of entire git history
gitleaks detect --source . --verbose
# Scan a specific commit range
gitleaks detect --log-opts="HEAD~50..HEAD"
# Install as pre-commit hook (catches secrets before commit)
gitleaks protect --staged --verbose
```
### TruffleHog (alternative, scans for high-entropy strings)
```bash
# Scan git history
trufflehog git file://. --only-verified
# Scan a specific branch
trufflehog git file://. --branch main --only-verified
# Scan current working directory (filesystem, not just git)
trufflehog filesystem .
```
### Adding to .pre-commit-config.yaml
```yaml
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.0
hooks:
- id: gitleaks
```
## Reading Scan Results
A positive hit returns:
- **Rule ID**: which detector triggered (e.g., `generic-api-key`, `aws-access-key`)
- **Secret**: the matched string (masked in some tools)
- **File + line number** or **commit SHA + line**
- **Entropy**: for TruffleHog, higher entropy = more likely a real secret
### False-Positive Triage Decision Table
| Scenario | Action |
|---|---|
| A known test fixture key (e.g., `test_key_123`) | Add to `.gitleaks.toml` allowlist |
| A hex hash / UUID that looks like a key | Add to allowlist with a comment |
| A real secret deliberately placed | Rotate immediately, then purge (see remediation) |
| Unsure | Treat as real; rotate to be safe |
### Gitleaks allowlist example (`.gitleaks.toml`)
```toml
[allowlist]
description = "False positives"
paths = [
'''test_.*\.py''',
'''\.md$''',
]
regexes = [
'''AKIA[0-9A-Z]{16}''',
]
```
## Remediation Workflow
1. **ROTATE THE SECRET FIRST** — Assume it's compromised the moment it hit the repo. Revoke it in the provider console (AWS IAM, GitHub tokens, etc.) and generate a new one.
2. **Move the secret** to environment variables, a `.env` file (gitignored), or a secret store (HashiCorp Vault, AWS Secrets Manager, GitHub Secrets).
3. **Purge from git history** (see `secrets-leak-remediation` skill for the full workflow).
4. **Add detection** — update `.gitleaks.toml` to catch this pattern in the future.
## CI Integration
```yaml
# GitHub Actions example
- name: Scan for secrets
uses: gitleaks/gitleaks-action@v2
env:
GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }}
```
## Which Tool When
| Scenario | Tool |
|---|---|
| Full git history scan on a local repo | `gitleaks detect` |
| Scanning a remote/CI without a full clone | `trufflehog git <url>` |
| High-entropy secrets (no known pattern) | `trufflehog` (entropy-based) |
| Known secret patterns (AWS keys, JWT) | `gitleaks` (rule-based) |
| Pre-commit / pre-push gate | `gitleaks protect --staged` |
| Filesystem scan (not git) | `trufflehog filesystem .` |
Both are complementary — gitleaks is faster with known patterns; trufflehog catches novel high-entropy strings gitleaks misses.
## Common Secret Patterns to Watch For
| Type | Pattern / Indicator |
|---|---|
| AWS Access Key | `AKIA[0-9A-Z]{16}` |
| AWS Secret Key | 40-char base64-ish string near an access key |
| GitHub PAT | `ghp_[A-Za-z0-9]{36}` or `github_pat_...` |
| Stripe key | `sk_live_[0-9a-zA-Z]{24}` |
| Google API key | `AIza[0-9A-Za-z_-]{35}` |
| JWT | `eyJ...` (three dot-separated base64 sections) |
| Private key | `-----BEGIN (RSA |EC )?PRIVATE KEY-----` |
| Connection string | `postgres://user:password@host/db` |
A regex you can drop into gitleaks to catch a custom org pattern:
```toml
[[rules]]
id = "my-org-internal-token"
description = "Internal service token (myorg_ prefix)"
regex = '''myorg_[a-f0-9]{32}'''
```
## Verifying a Scan Actually Ran
A scan that silently fails (binary missing, wrong path, empty target) is a false sense of security:
```bash
# Confirm gitleaks is installed and findable
which gitleaks && gitleaks version
# Confirm the scan actually inspected the repo (non-zero output)
gitleaks detect --source . --report-format json --report-path /tmp/leaks.json
jq 'length' /tmp/leaks.json # 0 = clean, N = N findings
# Confirm the exit code semantics (gitleaks exits 1 when leaks found)
gitleaks detect --source . >/dev/null 2>&1
echo "exit code: $?" # 0 = clean, 1 = leaks, other = scan failed
```
**A scan that errored out is NOT a clean scan.** Check the exit code, not just the absence of output.
## Guardrails
- **Never** just delete the line and commit — the key is still in git history and is still valid.
- **Never** skip scanning on "internal" repos — internal repos get leaked too.
- **Never** ignore a real leak because "it's just a dev key" — dev keys often have broader access than expected.
- **Never** store the rotated replacement in the same repo without moving it to a secure location first.
## Pitfalls
- **Only scanning HEAD**: `git log` contains the full history. A secret committed 200 commits ago is still exposed. Always scan full history.
- **"Fixing" by deleting the line without rotating**: The key is still alive. The attacker already has it. Rotation is the ONLY real fix.
- **Treating a scan-pass as safety**: Scanners miss novel formats and low-entropy secrets. Defense in depth: never hardcode in the first place.
- **Forgetting to add the allowlist**: Without allowlisting known fixtures, the team learns to ignore all alerts.
## Verify / Checklist
- [ ] `gitleaks detect --no-git` (if applicable) returns no findings on the current tree
- [ ] `gitleaks detect` returns no findings on full git history (or only known allowlisted hits)
- [ ] Every found secret has been rotated (confirmed in provider console)
- [ ] Replacements are in env vars / secret store, not in the repo
- [ ] Pre-commit hook is installed (`gitleaks protect --staged`)
- [ ] CI pipeline includes a secret scan step
- [ ] `.gitleaks.toml` is present and reviewed for allowlist accuracy
Attached files
No attached files.