Secret scanning with Gitleaks and TruffleHog, and what to do the moment one is found

Finding a credential in git history is the easy part. The hard parts are verifying it is live, rotating it without an outage, and stopping the next one — in that order, and usually within the hour.

Every repository we have scanned in the last three years had at least one secret in its history. Not always a live one, but always one. The pattern is consistent: a test credential committed in 2021 "temporarily", a .env added before .gitignore was, a Terraform variable file with a database password, a long-lived cloud access key in a CI script that predates the OIDC integration.

Two tools cover this well, and they answer different questions.

Gitleaks for history, TruffleHog for verification

Gitleaks is fast and regex-driven. It walks the full commit history and reports matches with the commit, author and file. It is the right tool for the first pass over a repository and for the pre-commit and CI hook, because it runs in seconds.

gitleaks detect --source . --redact --report-format sarif --report-path leaks.sarif

TruffleHog does something Gitleaks does not: it verifies. For hundreds of credential types it calls the provider's API to check whether the key still works.

trufflehog git file://. --only-verified --json

That distinction is the whole triage. A hundred unverified matches is a weekend of work; three verified live credentials is an incident you handle this afternoon. Run Gitleaks broadly, and run TruffleHog with --only-verified to decide what is urgent.

TruffleHog also scans beyond git: S3 buckets, Docker images, CI logs, Jira, Slack. CI logs in particular are worth a pass — build systems print environment variables far more often than anyone expects, and those logs are usually readable by everyone in the organisation.

Rotate first, clean history later, and in that order

The instinct on finding a secret in git is to rewrite history and remove it. That is the wrong first move and it wastes the hours that matter.

Assume the secret is compromised the moment it is committed. Every clone, every fork, every CI cache and every developer laptop has it. History rewriting does not reach any of those, and if the repository was ever public it does not reach the archives either.

So the order is:

  1. Rotate the credential. Create the new one, deploy it, verify the service works, then revoke the old one. This is the only step that actually removes the risk.
  2. Check what was done with it. The cloud audit log — CloudTrail, Cloud Audit Logs, Entra sign-in logs — for the key's identity, from the commit date to now. Unexpected regions, unusual API calls, access from unfamiliar addresses. This is the question the incident report has to answer, and the log retention window is why it sometimes cannot be answered at all.
  3. Then decide about history. For a private repository with a rotated secret, rewriting history is usually not worth the disruption of every developer re-cloning. For a public repository, rewrite and ask the platform to expire the cached views of the old commits.

Stopping the next one

Detection at commit time is much cheaper than detection at scan time:

  • A pre-commit hook running Gitleaks, installed via the repository's hook configuration so it arrives with a clone. It is bypassable, and that is fine — it catches accidents, not malice.
  • The server-side scan as a required check, so a bypassed hook is still caught before merge.
  • Push protection at the platform level, where available, which blocks the push entirely.

But the structural fix is not to have long-lived secrets at all. The reason cloud access keys keep appearing in repositories is that they exist. Replace CI credentials with OIDC federation to a role, replace static database passwords with IAM authentication or a secrets manager with dynamic credentials, and issue what remains with a short expiry. A secret that lives fifteen minutes is not worth committing and is not worth much if it is.

Keep a short, deliberate allowlist for test fixtures and example values. Make it explicit, in .gitleaks.toml, with a comment for each entry. An allowlist that grows silently is how a real credential ends up ignored.

What the auditor asks, and what actually matters

Both ISO 27001 and SOC 2 ask about secret management, and a scanner in the pipeline plus a documented rotation procedure answers the control.

The question worth asking internally is different: if a key leaked today, how long until we knew, and how long until it was dead? If the answer is more than a day, the work is in detection and rotation speed, not in the scanner.

ConsultorIA

Want this done on your cloud?

A ten-day read-only assessment is free, and Skyline lets you see your estate on a map before you write to us.

Related articles