Secret Scanning Triage: Not Every Leaked Key Is an Incident

Detector type tells you what a secret is. Validation tells you what to do about it.
Secret scanning triage by validation: sort leaked credentials by whether they still work, rotate live keys first, and keep verdicts current after rotation.
August 24, 2026

Run a secret scanner across a few hundred repositories and the first result is a number that makes everyone uncomfortable. Thousands of findings. API keys in test fixtures, tokens in old commits, database URLs in example configs, and somewhere in the pile, a handful of credentials that still work.

Secret scanning triage is the job of finding that handful fast. It is not the same job as vulnerability triage. A vulnerability is a question of exploitability over time. A leaked credential is a yes or no question that you can often answer directly: does this key still authenticate?

This post walks through a triage model built on that question, what to do with each outcome, and how to keep the answer current.

Why severity is the wrong first sort

Most scanners label findings by detector type. An AWS access key is critical, a generic high-entropy string is low. That sort order looks sensible and fails in practice.

  • An AWS key that was rotated two years ago is harmless, whatever its label says.
  • A "low confidence" generic string can be a live production database password.
  • A private key has no provider endpoint to test, but it is real key material and stays dangerous until it is replaced.
  • A canary token is supposed to be found. Treating it as an incident wastes an afternoon.

The detector type tells you what kind of secret it is. It does not tell you what to do. Validation does.

Validation: the question that decides the action

Validation means testing whether a detected credential is real and live, using a method that cannot cause harm. There are two layers worth separating.

Structural validation

Many modern token formats carry a checksum. GitHub tokens, for example, include one, which lets a scanner reject a look-alike string without any network call. Structural checks throw out fabricated and truncated tokens at scan speed, and they never transmit the candidate secret anywhere.

Live validation

For a credential that survives the structural check, a read-only call to the provider answers the question. A "who am I" call against a cloud API, an identity endpoint on a SaaS platform, or a connection test against a database. The call must be read-only and must not change state. Loopback and localhost connection strings are not worth testing and should be left alone.

Some credentials cannot be tested live. Raw private keys, HMAC-signed JWTs and some connection-string formats have no safe endpoint to ask. A good triage model records that honestly instead of guessing.

A five-bucket triage model

Every validation outcome should fold into exactly one action. Five buckets cover every case we have seen.

  1. Rotate now. The credential authenticated at the provider. This is an incident. Rotate it, revoke sessions it created, and check the provider's audit log for use.
  2. Rotate. Real key material that cannot be probed, such as a private key. Treat it as live and replace it on a normal change schedule.
  3. Triage. No verdict was reached. No validator applies, the provider's answer was inconclusive, or the check was skipped. A person decides, using detection confidence to order the queue.
  4. No action. The credential was tested and is inactive, or the material is structurally invalid. Record it and move on.
  5. Expected. A canary token planted on purpose. Investigate only if it appears somewhere it should not.

Two rules keep the model honest. A missing or unrecognized status lands in Triage, never in No action. And detection confidence only orders work inside Triage. It never downgrades a confirmed or presumed live credential.

History matters as much as the current code

Removing a secret from the latest commit does not remove it from the repository. It is still in history for anyone who clones, still in forks, and often still in CI logs and build caches.

A scanner that only reads the current checkout misses exactly the secrets people think they already fixed. Scan every branch and every commit, then record where each secret is visible:

  • Visible in the default branch. Exposed in current code.
  • Not visible in the default branch. Exposed in history only. Still a risk, and still needs rotation if live.
  • Visibility unknown. Presence at the branch tip could not be established. This is not evidence the secret was removed.

Roll duplicates up. The same key committed in fifty places is one credential to rotate, not fifty findings to close.

Rotation is the fix. Deletion is not.

The OWASP Secrets Management Cheat Sheet is direct about this: once a secret is exposed, rotate it. Rewriting git history can reduce future exposure, but it does not undo past exposure, and it breaks every clone and open pull request.

A practical response for a Rotate now finding:

  1. Revoke or rotate the credential at the provider.
  2. Update the consuming service to read the new value from a secrets manager, not from the repository.
  3. Check the provider's audit log for use between the commit date and the rotation.
  4. Remove the value from the code so it stops re-appearing in scans.
  5. Re-validate the old credential and confirm it reads inactive.

Step 5 is the one teams skip. Without it, nobody knows whether the rotation actually happened.

What a validation result should show

"Active" on its own is not enough to act on. The engineer rotating the key needs to know which key, which account, and what it can reach. A useful validation result carries the provider's answer, parsed where possible.

  • Identity. The account, user, team or project the credential authenticated as.
  • Scope. Where the provider reports it, what the credential can do. A read-only token and an org-admin token are different incidents.
  • Location. The repository, file, commit and line, plus every other place the same value appears.
  • Environment. Whether the value was seen in production, staging or development configuration.
  • Proof. The raw provider response, so a reviewer can confirm the verdict without re-running the test.

This is also what makes the result defensible later. When an auditor asks how you decided a finding needed no action, "the provider said the key was invalid on this date" is an answer.

Keep verdicts current

A validation result is a fact about the moment it was tested. Keys get rotated, tokens expire, and a credential marked inactive last quarter may have been re-issued with the same value. A triage list that is never re-tested drifts away from reality.

Three triggers keep it close:

  • On push. A push to the default branch re-tests the credentials in that repository.
  • On a schedule. Live credentials re-tested often, everything else less often, so idle repositories are not forgotten.
  • On demand. A re-check on one finding, used right after a rotation to confirm it worked.

Prevent the next one

Triage handles what already leaked. The cheaper win is stopping the next commit.

  • At the keyboard. A pre-commit hook or an agent skill that checks for secrets as code is written, including code a coding agent writes.
  • At the pull request. A check on the lines a PR adds. Be clear about what blocking buys you: it keeps the secret off the default branch, but the credential is already exposed the moment the branch is pushed. Blocking is fast notice, not containment.
  • Across the repository. A check on the repository's whole secret inventory, including history, so a pre-existing live credential shows up in front of the next person who opens a PR.

Pair each check with ownership. A live credential without an owner and a deadline stays live.

How Heeler triages secrets

Heeler Secrets scans every branch and the full commit history on GitHub, GitLab, Azure DevOps and Bitbucket, then validates each finding.

  • Checksum-aware structural validation rejects fabricated tokens with no network call.
  • A read-only live check runs against the provider for cloud, SaaS and AI credentials, and a connection test runs for database strings. Localhost URIs are not tested.
  • Every underlying status folds into one of five action buckets: Rotate Now (Confirmed Live), Rotate (Presumed Live), Triage (No Verdict), No Action (Confirmed Inactive) and Expected (Canary Token). The same buckets drive filters, guardrails, workflow conditions and the MCP tools.
  • Findings roll up across commits and files, with occurrence counts and the environments each secret was seen in.
  • A push to the default branch re-tests every credential in the repository, and a Re-check action tests one credential on demand.
  • Secrets guardrails on PR Guardrails gate the lines a PR adds, or the repository's whole inventory including history.

The full model is in the Secrets documentation, and the response runbook is Contain a leaked secret. For the PR side, see our post on Secrets PR Guardrails.

A triage checklist

  1. Sort by action, not by detector type. Work Rotate now first.
  2. Rotate at the provider, then re-validate and confirm the old value reads inactive.
  3. Treat real key material that cannot be probed as live until it is replaced.
  4. Work the no-verdict queue by detection confidence, high first.
  5. Scan history, not just the current checkout, and roll duplicates into one finding.
  6. Give every live credential an owner and a deadline, through tickets or workflows.
  7. Add a pre-commit or agent-side check so the next secret never lands.

FAQ

What is secret scanning triage?

Secret scanning triage is deciding what to do with each detected credential. The most reliable basis is validation: testing, safely and read-only, whether the credential still works.

Is a leaked but inactive key still a problem?

Not as an incident. An inactive credential cannot be used, so it needs no rotation. Remove it from the code so it stops appearing in scans, and keep the record.

Does blocking a pull request contain a leaked secret?

No. The secret is exposed as soon as the branch is pushed, even if the PR never merges. Blocking keeps it off the default branch. Rotation is what contains it.

What about secrets that cannot be validated live?

Real key material such as a private key should be treated as live until it is rotated. Credentials with no safe validator should go to a manual review queue, never to "no action" by default.

See your live credentials first

Heeler validates every detected secret and sorts the list by the action it needs, live credentials first. Get a demo.

What’s new on Heeler
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Related resources

See All Resources