Hardcoded API keys, database passwords, and cloud credentials leak into git history more often than most engineering teams admit — a rushed commit, a .env file that slipped past .gitignore, a debug print statement left in during an incident. Once a secret lands in a commit, it lives in git history forever unless someone actively rewrites it, and forks, clones, and CI logs can all carry copies you'll never find by searching the current working tree.
This guide walks through setting up secrets scanning git repository workflows from scratch: choosing tools, wiring up local pre-commit checks, catching what already leaked into history, and enforcing scans in CI so a hardcoded secret never reaches main again. By the end you'll have a layered setup — local, historical, and pipeline-level — using gitleaks and trufflehog, the two most widely adopted open-source scanners. You'll also come away with a repeatable process for triaging findings and rotating exposed credentials, not just detecting them.
Step 1: Pick a Secrets Scanning Git Repository Strategy
Before installing anything, decide what you're actually protecting against, because gitleaks and trufflehog solve slightly different problems:
- Gitleaks is fast, regex- and entropy-based, and works well as a pre-commit gate and CI check. It's the go-to for catching new secrets before they're committed.
- TruffleHog goes further by verifying findings against live APIs (it can confirm whether an AWS key or Slack token is actually active) and is stronger at deep historical scans across an entire commit history.
Most mature setups use both: gitleaks for fast, everyday gating, and trufflehog for periodic deep audits and verification of what's real versus a false positive. That combination is what we'll build here.
Step 2: Run a Gitleaks Tutorial Scan Locally
Start with a baseline scan of your repository to see what's already there. Install gitleaks via your package manager or download a binary release:
# macOS
brew install gitleaks
# Linux (or via Docker, no local install needed)
docker run -v $(pwd):/repo zricethezav/gitleaks:latest detect \
--source="/repo" --verbose
Run a full-history scan from the repo root:
gitleaks detect --source . --report-path gitleaks-report.json --verbose
This walks every commit, not just the current tree, and reports the file, line, commit hash, and matched rule for each finding. If this is your first gitleaks tutorial run on an existing repo, expect noise — old test fixtures, sample .env.example files, and placeholder tokens often trigger matches. Triage each hit before assuming it's a real exposure.
Create a .gitleaks.toml in the repo root to tune rules and suppress known false positives:
title = "gitleaks config"
[extend]
useDefault = true
[[rules.allowlist]]
description = "example env files"
paths = ['''\.env\.example$''', '''fixtures/.*''']
Step 3: Add a Pre-Commit Hook to Prevent Hardcoded Secrets in Git
Detecting secrets after they're committed is better than nothing, but the real win is stopping them before they enter history at all. Use the pre-commit framework to wire gitleaks into every developer's local workflow:
# .pre-commit-config.yaml
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.4
hooks:
- id: gitleaks
Install the hook framework and register it:
pip install pre-commit
pre-commit install
Now every git commit runs gitleaks against staged changes first. A developer trying to commit a stray AWS key gets blocked locally, with the exact line flagged, instead of finding out during a code review or — worse — after it's already been pushed. This single step is the highest-leverage way to prevent hardcoded secrets git commits from ever happening in the first place, because it shifts the check to the moment the secret is typed, not weeks later.
Step 4: Follow a TruffleHog Setup Guide for Deep History Audits
Pre-commit hooks only protect commits made after the hook exists — they say nothing about years of prior history, secrets committed before anyone adopted scanning, or repos inherited from an acquisition or a departed contractor. This is where trufflehog earns its place.
Install it and run a verified scan across full git history:
# Install via Go
go install github.com/trufflesecurity/trufflehog/v3@latest
# Or via Docker
docker run -it -v "$PWD:/repo" trufflesecurity/trufflehog:latest \
git file:///repo --only-verified
The --only-verified flag is the key part of any practical trufflehog setup guide: instead of dumping every regex match, trufflehog actively tests credentials against their originating service (AWS STS, GitHub's API, Slack, Stripe, and dozens more) and reports only the ones that are demonstrably live. That cuts triage time dramatically on large, older repositories where a naive scan might return hundreds of matches.
Run this as a scheduled job — weekly for active repos, monthly for archived ones — since new detection rules and verification modules ship regularly and can surface secrets an earlier scan missed.
Step 5: Enforce Scanning in CI
Local hooks can be bypassed with --no-verify, and not every contributor will have pre-commit installed. CI enforcement is what makes the policy actually binding. A minimal GitHub Actions job:
name: secrets-scan
on: [pull_request, push]
jobs:
gitleaks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: gitleaks/gitleaks-action@v2
env:
GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }}
Set fetch-depth: 0 so the action can see full history on pull requests, not just the last commit. Configure the job to fail the build (not just warn) on any unallowlisted finding, and require it as a passing status check before merge in your branch protection rules.
Step 6: Handle Findings and Rotate Exposed Secrets
A scanner is only useful if findings lead to action. When a real secret is confirmed:
- Rotate it immediately at the source — the cloud provider, SaaS vendor, or database — before doing anything else. Removing the line from the repo does not invalidate the credential.
- Purge it from history if it's sensitive enough to warrant a rewrite, using
git filter-repoor the BFG Repo-Cleaner, then force-push and have every collaborator re-clone. - Add the pattern to your allowlist only after confirming it's genuinely a non-issue (test fixture, expired sandbox key) — never allowlist to silence a finding you haven't investigated.
- Log the incident so recurring patterns (a specific team, a specific service integration) can be addressed at the root cause, such as moving that credential to a secrets manager.
Verification and Troubleshooting
- Confirm hooks are actually running:
pre-commit run --all-filesshould execute gitleaks against every tracked file, not just staged ones. If nothing happens, check that.pre-commit-config.yamlis committed andpre-commit installwas run inside the correct repo. - CI passes locally but fails in the pipeline: this is almost always a shallow-clone issue. Verify
fetch-depth: 0(or an equivalent full-history checkout) is set, since a shallow clone hides most of the commit history gitleaks and trufflehog need to scan. - Too many false positives: refine
.gitleaks.tomlallowlists by path or regex rather than disabling rules entirely, and prefer trufflehog's--only-verifiedmode for historical scans to cut noise on older repos. - Trufflehog reports "unverified" for a real credential: some providers don't expose a lightweight verification endpoint; treat unverified findings as "needs manual review," not "safe to ignore."
- Secrets still leaking despite hooks: check for
--no-verifyusage in commit history and consider making the CI check a required, non-bypassable status check rather than relying solely on local enforcement.
How Safeguard Helps
Gitleaks and trufflehog are excellent building blocks, but stitching them into pre-commit hooks, scheduled history audits, and CI gates across dozens of repositories — and then keeping the resulting findings triaged, rotated, and tracked — becomes its own operational burden as an organization scales. Safeguard consolidates that workflow: it continuously scans every repository in your software supply chain for hardcoded secrets, correlates findings with verified exposure risk, and routes confirmed leaks straight into remediation workflows with rotation tracking, so nothing sits in a spreadsheet waiting for someone to notice. Combined with Safeguard's broader supply chain visibility — dependency provenance, build integrity, and CI/CD posture — secrets scanning becomes one signal in a unified security picture rather than a disconnected script that only the security team remembers to run.