Skip to content

Secrets Scanning

Automated secrets detection to prevent credential leaks using Gitleaks. This scan fails CI when a secret is detected.

FieldValue
Workflow.github/workflows/secrets-scan.yml
Configuration.gitleaks.toml
BehaviorFails CI if secrets detected
TriggersEvery push, all pull requests
  • New commits (on push)
  • All commits in the PR branch
  • Files, comments, diffs

API keys, passwords, tokens (GitHub, AWS, etc.), private keys, database connection strings, and 100+ built-in secret patterns. Example shapes:

# AWS Access Key
AKIAIOSFODNN7EXAMPLE
# GitHub Personal Access Token
ghp_1234567890abcdefghijklmnopqrstuvwxyz
# Private SSH Key
-----BEGIN OPENSSH PRIVATE KEY-----
# Generic API Key
api_key=sk_live_1234567890abcdef
# Database URL
postgres://user:password@localhost:5432/db
Error: gitleaks detected secrets in commits
Finding: api_key="sk_live_..."
File: src/config.rs
Line: 42
Commit: abc1234
Terminal window
# Install gitleaks
brew install gitleaks
# or
curl -sSfL https://github.com/gitleaks/gitleaks/releases/download/v8.18.2/gitleaks_8.18.2_linux_x64.tar.gz | tar -xz
# Scan current changes
gitleaks detect --source . --verbose
# Scan entire history
gitleaks detect --source . --log-opts="--all"
# Scan a specific commit range
gitleaks detect --source . --log-opts="--since=HEAD~1"

Verify: a clean tree reports no leaks found.

Block secrets before they are committed:

.git/hooks/pre-commit
#!/bin/sh
gitleaks protect --verbose --redact --staged
Terminal window
chmod +x .git/hooks/pre-commit

Verify: staging a test secret aborts the commit.

Edit .gitleaks.toml:

[extend]
useDefault = true # Use built-in rules
[allowlist]
paths = [
'''test/fixtures/secrets.txt''', # Ignore test files
]
regexes = [
'''EXAMPLE_.*''', # Ignore example placeholders
]

Add project-specific patterns:

[[rules]]
id = "custom-api-key"
description = "Project API Key"
regex = '''project_key_[0-9a-f]{32}'''

Verify: gitleaks detect --source . honors the new rules and allowlists.

  1. Add it to the allowlist in .gitleaks.toml:

    [allowlist]
    regexes = [
    '''YOUR_PLACEHOLDER_TOKEN''',
    ]
    paths = [
    '''docs/examples/''',
    ]
  2. Or mark the line inline:

    secret = "not-a-real-secret" # gitleaks:allow

Verify: re-run gitleaks detect and confirm the finding is gone.

Critical — if a real secret is detected:

  1. Rotate immediately — assume the secret is compromised.

  2. Remove it from history:

    Terminal window
    # Use BFG Repo-Cleaner
    bfg --replace-text secrets.txt repo.git
  3. Force push (destructive):

    Terminal window
    git push --force

Verify: gitleaks detect --source . --log-opts="--all" no longer finds the secret.

Slow scans — scan only staged changes:

Terminal window
gitleaks protect --staged

Ignoring files:

[allowlist]
paths = [
'''\.lock$''', # Lock files
'''vendor/''', # Vendored code
'''test/fixtures/''', # Test data
]

A leaked credential is one of the fastest paths from a public repository to a compromised system, and once a secret reaches git history it persists in every clone and fork — rotation, not deletion, is the only real fix. Failing CI on detection makes the leak un-mergeable rather than merely flagged, and the pre-commit hook pushes the check earlier still, stopping the secret before it ever enters history where cleanup becomes destructive and incomplete.