Eighteen months ago a developer on a deadline pasted a cloud access key into a configuration file to get a deployment working. A week later a reviewer spotted it, and the developer deleted the line. The pull request was merged, everyone moved on, and today the repository’s latest code is clean. The key is still in the repository. It is in the commit history, where anyone who can clone the repository can read it in seconds, and it is quite possibly still valid.

In short

  • Deleting a secret from the code does not remove it from git. Every clone carries the full history. A scanner that only reads the latest files will never see it.
  • Finding it is the easy half. The real fix is to revoke and rotate the credential. Rewriting history is secondary and often impractical.
  • “Rotated” should be verified, not asserted. Comparing the live secret with the leaked one, without anyone seeing either, turns a status update into evidence.
  • Secrets are the shortest path to a breach. A valid cloud key needs no exploit. It is the identity.

The situation

The scenario is illustrative, but in InsightFortify’s demo estate the shape is real: an AWS Access Key ID in config/settings.py of acme/payments-api, found by a full-history secret scan and scored P1. Secrets like this leak for ordinary reasons: a local test that became a commit, a configuration file copied from a teammate, an example that was not quite an example, or a debug session pushed by accident.

Industry
data

Industry research on public code repositories reports millions of hard-coded secrets exposed every year, and a rising trend. GitGuardian’s 2024 report counted 12.8 million new secrets in public GitHub commits during 2023, and its research has repeatedly found that many leaked credentials remain valid long after they are exposed. Private repositories are not immune: they are cloned to laptops, build agents and contractors’ machines, and they are themselves targets.

Sources: GitGuardian, The State of Secrets Sprawl 2024; Verizon Data Breach Investigations Report, 2024, on the use of stolen credentials.

Why existing tooling misses it

  • Pre-commit hooks only protect the future. They are worth having, but they say nothing about the years of history already in the repository, and developers can skip them.
  • Latest-tree scans see only today’s files. Many scanners, and many CI steps, read the current checkout. A secret added and removed in two commits never appears there.
  • A finding is not a fix. Even when a secret is found, the ticket often reads “remove the key from the file”. The developer deletes the line, the ticket closes, and the credential stays live.
  • “Rotated” is a word, not evidence. Security asks whether the key was rotated, someone says yes, and nobody checks.
TODAY Scan the latest code, close the ticket when the line is gone

The key that was deleted last year is invisible. The key found today is “fixed” by deleting the line, which leaves the credential valid and still in history.

VERIFIED ROTATION Scan every commit, close the finding when the credential is proven rotated

The full history is scanned. Each finding names the commit and author, the remediation is “revoke and rotate”, and rotation is checked against the secrets manager, with the result kept in the audit trail.

How InsightFortify handles it

Finding it, across the whole history

Repository scans run Gitleaks over every commit, not just the latest tree. This is a per-organisation setting, on by default, because it is slower on very large repositories. Organisations can add their own token formats, such as internal API keys or service tokens, to the built-in rules, and CI pipelines fetch the same combined configuration from the API, so the worker and the pipeline never disagree about what a secret looks like.

Handling it safely

The secret itself never reaches the database. The finding keeps a redacted snippet with only the first and last two characters visible, and two hashes: one to deduplicate, so the same key in the same file is one finding however many commits carry it, and one of the value alone, used only for the rotation check. Each finding records the commit and author it was found in.

Scoring it

A secret in production code is scored high. One in a test fixture is marked down, by less than a vulnerability in test code would be, because a real key in a test file is still a real key. Repository context still applies: a secret in an internet-facing, card-handling service scores above one in an internal tool.

A P1 finding detail page in InsightFortify showing the score breakdown, the evidence and the remediation guidance
A finding’s detail page. Score factors, the redacted evidence and the remediation, which for a secret always starts with revoking and rotating it.

Proving it is rotated

This is the step most programmes skip. Once the owner has rotated the credential and stored the new one in the secrets manager, an analyst or the assignee runs a rotation check on the finding, naming where the live credential is kept, for example an AWS Secrets Manager reference. InsightFortify reads the current value, hashes it, and compares it with the hash of the leaked value. If they differ, the leaked secret is no longer the live one. If they match, the response is blunt: the leaked value is still the live secret, so revoke and rotate it now. Either way the result is recorded on the finding’s history and in the audit trail. Nobody, including the analyst, ever sees either value, and only secret locations under prefixes the deployment allows can be checked, so the feature cannot be used to probe other secrets.

sequenceDiagram autonumber participant G as Gitleaks full history participant F as InsightFortify participant SA as Security analyst participant O as Service owner participant SM as Secrets manager participant AWS as Cloud provider G->>F: Leak in config/settings.py, commit and author F->>F: Redact, hash, score P1 F-->>SA: New P1 secret, SLA 7 days SA->>O: Assign - revoke and rotate, not just delete O->>AWS: Deactivate the old key, issue a new one O->>SM: Store the new key SA->>F: Rotation check against the secret reference F->>SM: Read the current value F->>F: Compare hashes - values differ F-->>SA: Rotated, recorded in history and audit trail O->>F: Optional history rewrite, then close

InsightFortify never revokes keys itself. It tells you whether revocation actually happened.

Closing a leaked secretIn this order: the credential first, the repository second
1
Triage and assignsecurity analyst

Confirm the finding is a real credential, not a placeholder. Assign it to the owner of the system the credential belongs to, who may not be the author of the commit.

2
Check for usesecurity analyst + cloud team

Before rotating, check the provider’s access logs for the key: was it used from an unexpected place? If so, this is an incident, not a hygiene task.

3
Revoke and rotateservice owner

Deactivate the leaked credential at the provider, issue a new one, and store it in the secrets manager. Update the consumers.

4
Verify rotationsecurity analyst or assignee

Run the rotation check against the secret reference. A “not rotated” result goes straight back to the owner.

5
Clean the repositoryservice owner

Remove the secret from the code and load it from the secrets manager. Rewriting history is optional once the credential is dead, and often disruptive.

6
Prevent recurrenceAppSec

Add any internal token formats to the organisation’s secret patterns, so CI and the worker catch the next one before merge.

Decision flow

flowchart LR A["Secret found in history"] --> B{"Real credential?"} B -->|"No, placeholder"| D["Dismiss with a reason"] B -->|"Yes"| C{"Used from an
unexpected place?"} C -->|"Yes"| I["Treat as an incident
hand to incident response"] C -->|"No or unknown"| R["Revoke and rotate"] I --> R R --> V{"Rotation check"} V -->|"Still live"| R V -->|"Rotated"| K["Clean code, close finding"] style I fill:#fee2e2,stroke:#ef4444 style K fill:#ccfbf1,stroke:#0d9488

The first full-history scan

The first time full-history scanning runs across an estate, expect a backlog. Years of commits contain years of mistakes, and many of the credentials in them were rotated long ago for unrelated reasons. That is not a reason to avoid the scan. It is a reason to work the results in a sensible order.

  1. Production cloud and payment credentials first. Cloud access keys, database passwords for production systems, payment-provider keys. Check provider logs, then rotate and verify.
  2. Third-party service tokens next. Source control, CI, messaging and monitoring tokens. These are often long-lived and broadly scoped.
  3. Internal and non-production credentials. Still worth rotating, on the normal SLA.
  4. Test fixtures and placeholders last. Confirm they are fake and dismiss with a reason, so the decision is recorded and the same string does not come back as a new finding.

Because the same leaked key in the same file is one finding however many commits carry it, the backlog is usually much smaller than the raw scanner output suggests. And because the CI pipeline fetches the same rules from InsightFortify, including the organisation’s own token formats, the inflow of new leaks can be stopped at the pull request while the historical backlog is worked down.

A practical note for management: many leaked credentials belong to systems owned by teams other than the one whose repository they were found in. A platform team’s key in an application repository is the platform team’s problem to rotate. Assigning by credential owner rather than by repository avoids weeks of tickets bouncing between teams.

What it means for each audience

For the CISO

A valid cloud key in a repository is not a vulnerability that might be exploited. It is access that has already been handed out. Treat secret findings as the shortest route to a breach and hold them to the tightest SLA.

Verified rotation lets you say “every leaked credential found this quarter is dead” and show the evidence.

For the AppSec lead

Full-history scanning will surface a backlog the first time it runs. Work it by priority: production cloud and payment credentials first, test fixtures last.

Assign to the owner of the credential, not the author of the commit. They are often different people.

For the board

The question to ask is not “did we find secrets?” but “how many found secrets are still valid?” The right answer is zero, and it should be provable.

Leaked credentials also matter for cyber insurance and for regulators’ view of basic hygiene.

Outcome metrics to track

Verified rotationsshare of closed secret findings with a successful rotation check
Time to revokefrom detection to a rotation check that says “rotated”
History coverageshare of repositories scanned with full history at least once
New leaks per monthshould fall as custom patterns and CI checks catch them before merge

Pitfalls

  • Closing on deletion. The most common failure. Make “rotation verified” part of your definition of done for a secret finding.
  • Rewriting history first. Force-pushing a rewritten history disrupts every clone and does nothing about copies already taken. Kill the credential first.
  • Ignoring test files entirely. They are scored lower, not ignored. Real keys end up in tests surprisingly often.
  • Expecting the scanner to know if a key works. InsightFortify does not test leaked credentials against the provider. It verifies rotation against your secrets manager, and the provider’s own logs answer whether the key was used.

Deleting the line is not the fix. Killing the credential is.

Scan every commit, assign to the credential’s owner, and close the finding only when rotation has been verified.

Keep reading