How To

Secrets Detection Automation: Why Leaked Credentials Keep Winning

Secrets detection automation only helps if you know what it actually catches. Part one of our GitGuardian secrets response series covers the realities that matter: why credentials keep landing in repos (env files, debug commits, notebooks, CI logs), why deleting a secret is not remediation (history, forks, and clones mean it is burned the moment it is pushed), the incident backlog nobody owns, validity checking as the triage superpower that separates a live AWS key from stale noise, and honeytokens as tripwires for perimeter breaches. Real ggshield commands, GitGuardian dashboard paths, and a sober look at why point-in-time scanning keeps losing.

·
gitguardiansecuritysecretssecretsdetectionggshielddevsecopscredentials
Cover Image for Secrets Detection Automation: Why Leaked Credentials Keep Winning

Secrets Detection Automation: Why Leaked Credentials Keep Winning

In 2023, an engineer committed an API key, noticed the mistake eleven minutes later, and force-pushed it out of history. The key is still valid today. Nobody rotated it, because as far as the commit log is concerned, it never existed.

That gap — between "I removed it from the branch" and "the credential is actually dead" — is where most secrets incidents live. Secrets detection automation exists to close it, but only if you understand what it is really catching and why the obvious fixes do not work. This guide walks through the realities that matter: why secrets keep landing in repositories, why deleting them is not remediation, why the incident backlog grows faster than anyone can clear it, and how validity checking and honeytokens turn a noisy alert stream into something you can act on.

This is part one of a three-part series. Part two is a hands-on secrets response walkthrough using GitGuardian's native tooling; part three covers continuous secrets response with an AI agent.

1. Why secrets keep landing in repositories

What it is. A hardcoded credential — an AWS access key, a database URL with an embedded password, a third-party API token — committed into source control where it should never be. GitGuardian's own research puts the scale in the millions of new leaks detected across public GitHub every year, and private repositories are worse, not better, because nobody assumes they need scanning.

Why it happens. Not through carelessness so much as through the shape of normal work. A .env file gets committed because someone edited .gitignore after the first commit. A debug session hardcodes a token "just to test," and the commit ships before anyone reverts it. Jupyter notebooks embed output cells with live tokens. CI logs print an environment variable during a failing build and get archived. Config-as-code sprawl means credentials live in more file types than any single reviewer can track.

How to detect it. Scan the full history of a repository — not just the working tree — with GitGuardian's CLI, ggshield:

pip install ggshield
ggshield auth login
ggshield secret scan repo /path/to/your/repo

That command walks every commit reachable from every branch, which is the only scan that tells you the truth about what has been exposed.

Typical impact. On a mid-sized codebase, a first full-history scan routinely turns up dozens to hundreds of distinct secrets, most of them years old and long forgotten.

2. Why deleting the secret is not remediation

What it is. The instinct after a leak is to delete the offending line, commit, and move on. On a shared repository that does almost nothing for your security posture.

Why it happens. People reason about Git the way they reason about a text file — remove the text, and it is gone. Git does not work that way. Once a commit is pushed, it exists in the reflog, in every clone, in every fork, in every developer's local cache, and in any CI runner that fetched it. A force-push rewrites your branch pointer; it does not reach into the laptops and build agents that already have the object. The secret is burned the moment it is pushed, full stop.

How to detect it. Confirm a secret still exists in history even after a "cleanup" commit:

git log --all --oneline -S 'AKIA' -- .

Anything that prints means the credential is still reachable in your history. The -S flag searches for the literal string across all commits. If it appears, deletion did not help you.

The only real fix. Revoke and rotate at the provider — invalidate the leaked credential and issue a new one. History rewriting (with git filter-repo or the GitHub secret-removal guidance) is hygiene you do after the key is dead, never instead of killing it.

3. The incident backlog nobody owns

What it is. Turn on scanning across an organization and you do not get ten alerts — you get a backlog. Hundreds, sometimes thousands, of open detections surface at once, each one an "incident" in the dashboard with no assignee and no clear disposition.

Why it happens. Detection is cheap and remediation is expensive. Every historical leak becomes a ticket, but nobody budgeted engineer-hours to work them. Without ownership the backlog becomes wallpaper: present, ignored, and growing every time a new repo is onboarded.

How to detect it. Look at the raw count and its trend in GitGuardian dashboard → Incidents → Secrets, filtered to Status: Triggered. If that number only ever climbs, you have a backlog problem, not a detection problem. The question is never "do we have leaks" — you do — it is "which of these can actually hurt us right now."

Typical impact. An unmanaged backlog means the one live, high-privilege key that matters is buried under hundreds of stale, harmless ones. Triage is the whole game.

4. Validity checking: the triage superpower

What it is. A leaked credential is only dangerous if it still works. Validity checking calls the credential's provider to ask exactly that — is this AWS key active, is this token still accepted — and tags each incident valid, invalid, or unknown. It is the single most useful signal for cutting a thousand-item backlog down to the handful that need action this hour.

Why it matters. A stale key that was rotated out two years ago is noise. A valid AWS key with broad IAM permissions sitting in a public fork is a breach in progress. Same detection type, completely different urgency — and only validity separates them.

How to detect it. In GitGuardian dashboard → Incidents → Secrets, apply the filter Validity: valid and sort by severity. Those are the incidents that justify waking someone up. Via ggshield you can surface validity inline while scanning:

ggshield secret scan repo /path/to/repo --show-secrets

Then confirm blast radius on a suspected-live AWS key by asking the provider who it belongs to:

AWS_ACCESS_KEY_ID=... AWS_SECRET_ACCESS_KEY=... aws sts get-caller-identity

If that returns an ARN, the key is live and you now know exactly which account it opens.

Typical impact. On most first scans the valid-and-dangerous set is a small fraction of the total backlog — often well under ten percent — which is precisely why validity-first triage turns an unworkable list into a short one.

5. Honeytokens: tripwires for the breaches you cannot see

What it is. A honeytoken is a deliberately planted decoy credential — a fake AWS key that grants no real access but fires an alert the instant anyone uses it. You place it somewhere an intruder would look: a config file, a CI variable, an internal wiki, a backup archive. Nothing legitimate ever touches it, so any activity is, by definition, unauthorized.

Why it matters. Detection so far has been about your own repositories. Honeytokens flip the model outward — they tell you when someone is already inside, poking around code or infrastructure they should not have. A honeytoken trip is not a hygiene finding; it is evidence of an active intruder, complete with the source IP and timestamp of whoever tried to use the bait.

How to detect it. Create one in GitGuardian dashboard → Honeytokens → Create honeytoken, then commit it into a plausible-looking file and wait. GitGuardian's Honeytoken docs cover placement strategy; the discipline is to seed them where a real attacker would rummage, not where your own tooling will trip them by accident.

Typical impact. Most honeytokens never fire — that is the point. The one that does hands you early warning of a perimeter breach that no repository scan could have caught, days or weeks before other signals would surface it.

Why point-in-time scanning keeps losing

Here is the uncomfortable part. A one-time scan gives you a snapshot, and secrets do not respect snapshots. New leaks arrive continuously.

Every debug commit, every misconfigured .gitignore, every notebook checked in with a live output cell, every CI pipeline that logs an environment variable — each is a fresh opportunity, and they happen at the rate your team commits code. Meanwhile the response side never speeds up: revoke the key at the provider, rotate the replacement, redeploy every service that used it, and verify nothing broke. That loop is human, slow, and unchanged by better detection.

So the backlog reopens the day after you clear it, and the validity of any given incident can flip the moment a developer re-uses an old credential. Scanning harder does not fix this. Making detection and triage continuous does — which is where the next article picks up.

Make secrets response continuous

Everything above can run without a human watching the dashboard. CloudThinker connects to GitGuardian through a read-only API token and continuously triages new incidents and honeytoken trips by validity and blast radius — surfacing the live, high-privilege leak the moment it appears, with every revoke-and-rotate action gated behind your approval.

Try CloudThinker free — 100 premium credits, no card required — or continue the series with the native-tooling response guide.