IOnclad  /  Features  /  Git History
Risk Breakdown · Git History Critical

Deleting a secret doesn't delete it. Git keeps every version.

You committed a key, caught it, and removed it in the next commit. The file looks clean now. But git is a time machine, and the old value is still sitting in history, readable by anyone with clone access who runs one command. The working tree is not the repository. The repository remembers everything.

What git history actually stores

Every commit you've ever made is kept as an immutable snapshot. Deleting a line in a new commit adds a new snapshot on top. It does not touch the old one. So a secret that appeared in commit 200 and got "removed" in commit 201 still lives in commit 200, forever, until you rewrite history and force every clone to update.

git log -p --all reads it Every branch Every tag Every stash Every fork and clone CI caches

Why it's catastrophic

The danger isn't that the secret is hard to find. It's that it's trivial to find, and almost nobody looks before they make the repo public.

1. One command exposes the whole history

Anyone who can clone the repo runs git log -p --all and reads every line ever added or removed, across every branch. They don't need an exploit or special access. A teammate, a contractor, a new hire, or anyone you later grant access to, all see the secret that you thought was gone.

2. Going public makes private history public

The most common version of this: you build privately, drop a key in early, "remove" it, then flip the repo to public for launch. Bots scrape new public repositories within minutes, and they read history, not just the current files. The key you deleted three months ago gets harvested the moment the repo goes live.

3. Removing is not rotating

This is the trap that gets nearly everyone. Editing the file out in a later commit feels like a fix. It changes nothing about the credential itself. The value is still valid, still in history, and still works for whoever finds it.

The trap

"I deleted it in a later commit" is not remediation. If a secret was ever committed, assume it is compromised, rotate it, and then purge it from history. The old value lives in every clone, fork, and CI cache until you do both.

4. Stashes are the blind spot

A git stash with a secret in it never shows up in normal review. It's not on any branch, it's not in the log you usually read, and it survives until someone runs git stash clear. Most people forget stashes exist. The secret sits there quietly.

The shape of a real incident

It almost always plays out like this:

# commit 200, three months ago, building privately
+ DATABASE_URL="postgres://admin:[email protected]:5432/app"

# commit 201, next morning, "cleaned up"
+ DATABASE_URL=process.env.DATABASE_URL   // looks fixed ✓

# launch day: repo flipped to public
# attacker runs one command on the fresh clone:
$ git log -p --all | grep -i "postgres://"
# …and commit 200 hands over the live database password.
How IOnclad catches it

IOnclad has a dedicated git-history scanner. It runs 100% offline against the .git directory on your machine, so nothing is uploaded anywhere:

It finds known secret patterns and high-entropy values across history. It's a strong safety net, not a guarantee, so still rotate anything it flags.

How to fix it

Find every secret before someone else does.

Scan your code and full git history in under a minute. Free verdict, no signup, nothing leaves your machine.