This guide covers how to detect secrets in your codebase using two complementary tools:
- Gitleaks - Fast pattern-based detection
- Trufflehog - Verification-based detection (tests if secrets are actually valid)
To set all of it up as described here, run this script in from the root of your repository: github.com/atacan/agentic-coding-files/setup-secret-scanning.sh
Installation
macOS (Homebrew)
brew install gitleaks trufflehogOther platforms
Gitleaks:
# Download from releases
https://github.com/gitleaks/gitleaks/releases
# Or use Go
go install github.com/gitleaks/gitleaks/v8@latestTrufflehog:
# Download from releases
https://github.com/trufflesecurity/trufflehog/releases
# Or use Go
go install github.com/trufflesecurity/trufflehog/v3@latestRunning Manual Scans
Full Repository Scan (All Commits, All Branches)
Run both tools back-to-back to scan every commit across every branch in your local repository:
gitleaks detect -v --log-opts="--all --full-history"
trufflehog git file://. --results=verified,unknown--log-opts="--all --full-history" tells Gitleaks to walk all refs (branches, tags) without history simplification. Trufflehog scans all branches by default; --results=verified,unknown reports both confirmed-active and unverifiable secrets (skip this flag to see everything including unverified).
To save reports for later review:
gitleaks detect -v --log-opts="--all --full-history" -f json -r gitleaks-report.json
trufflehog git file://. --results=verified,unknown --json > trufflehog-report.jsonGitleaks
Scan current directory (all git history):
gitleaks detect -vScan all branches:
gitleaks detect -v --log-opts="--all"Scan entire history across all branches:
gitleaks detect -v --log-opts="--all --full-history"Scan only staged changes (useful before committing):
gitleaks protect --staged -vScan a specific commit range:
gitleaks detect --log-opts="commit1..commit2"Output results to JSON:
gitleaks detect -v -f json -r results.jsonTrufflehog
Scan git repository:
trufflehog git file://.Scan only for verified (active) secrets:
trufflehog git file://. --only-verifiedScan a remote repository:
trufflehog git https://github.com/user/repo.gitScan filesystem (non-git):
trufflehog filesystem /path/to/directoryScan with JSON output:
trufflehog git file://. --jsonHandling False Positives
Gitleaks
Create a .gitleaksignore file in your repository root:
# Format: commit:file:rule:line
abc123def456:path/to/file.yaml:generic-api-key:42
# You can also use just the fingerprint from gitleaks output
abc123def456:config/example.json:generic-api-key:15
To find the fingerprint, run gitleaks and look for the Fingerprint: line in the output.
You can also add inline comments to ignore specific lines:
api_key: "example-key-for-documentation" # gitleaks:allowTrufflehog
Create a .trufflehog-ignore file:
# Ignore specific files
path/to/example/config.yaml
# Ignore patterns
**/test/fixtures/**Pre-commit Hook Setup
Option 1: Manual Script
Create .git/hooks/pre-commit:
#!/bin/sh
# Pre-commit hook to check for secrets
exit_code=0
# Run gitleaks
if command -v gitleaks &> /dev/null; then
echo "Running gitleaks..."
gitleaks protect --staged -v
if [ $? -ne 0 ]; then
echo "gitleaks detected secrets in staged changes."
exit_code=1
fi
fi
# Run trufflehog
if command -v trufflehog &> /dev/null; then
echo "Running trufflehog..."
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM)
if [ -n "$STAGED_FILES" ]; then
trufflehog filesystem . --include-paths=<(echo "$STAGED_FILES") --fail 2>&1
if [ $? -ne 0 ]; then
echo "trufflehog detected secrets in staged changes."
exit_code=1
fi
fi
fi
exit $exit_codeMake it executable:
chmod +x .git/hooks/pre-commitOption 2: Using pre-commit Framework
Install the pre-commit framework:
pip install pre-commit
# or
brew install pre-commitCreate .pre-commit-config.yaml:
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.0 # Use latest version
hooks:
- id: gitleaks
- repo: https://github.com/trufflesecurity/trufflehog
rev: v3.63.0 # Use latest version
hooks:
- id: trufflehog
entry: trufflehog filesystem --fail .
stages: [commit]Install the hooks:
pre-commit installCI/CD Integration
GitHub Actions
Create .github/workflows/secret-scan.yml:
name: Secret Scanning
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
gitleaks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Gitleaks
uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
trufflehog:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Trufflehog
uses: trufflesecurity/trufflehog@main
with:
extra_args: --only-verifiedGitLab CI
Add to .gitlab-ci.yml:
secret-scan:
stage: test
image: alpine:latest
before_script:
- apk add --no-cache git curl
- curl -sSfL https://github.com/gitleaks/gitleaks/releases/download/v8.18.0/gitleaks_8.18.0_linux_x64.tar.gz | tar -xz
script:
- ./gitleaks detect --source . -v
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCHAzure DevOps
Add to azure-pipelines.yml:
trigger:
- main
pool:
vmImage: ubuntu-latest
steps:
- checkout: self
fetchDepth: 0
- script: |
curl -sSfL https://github.com/gitleaks/gitleaks/releases/download/v8.18.0/gitleaks_8.18.0_linux_x64.tar.gz | tar -xz
./gitleaks detect --source . -v
displayName: 'Run Gitleaks'Best Practices
- Run both tools - Gitleaks catches patterns; Trufflehog verifies if secrets are active
- Scan entire history - Secrets in old commits are still exposed
- Use pre-commit hooks - Prevent secrets from being committed in the first place
- Add CI checks - Catch anything that slips through
- Rotate exposed secrets immediately - Even if removed from code, they exist in git history
- Use
.gitignore- Prevent secret files (.env, credentials) from being tracked - Use environment variables - Never hardcode secrets in source code
What To Do If Secrets Are Found
- Rotate the secret immediately - Generate a new key/password
- Revoke the old secret - Disable it in the service provider
- Remove from git history (optional but recommended):
# Using git-filter-repo (recommended) pip install git-filter-repo git filter-repo --invert-paths --path path/to/secret/file # Or using BFG Repo-Cleaner bfg --delete-files secret-file.txt - Force push (if history was rewritten):
git push --force-with-lease - Notify affected parties if the secret was used in production