Curious Pandas

Deleted is not gone

A secret committed once lives on in history, in every clone and in every fork. Leak a fake key, delete it, read it straight back - and learn the one fix that actually works.

12 min+35 XP for finishing

It’s late, the payment test won’t pass, and the quickest way to try the real API is to paste the key into config.env. It works. Everything gets committed together. Ten minutes later someone spots the key, deletes the line, and commits again. “Fixed.”

It isn’t. Remember what a commit is: a snapshot of every file, named by the hash of its contents. The fix made a new snapshot without the key. It didn’t touch the old one, and it can’t: change a commit and its name changes, along with every name after it.

Visual guideWhere a deleted key still lives
Where a deleted key still livesThree commits in a row: the shop starts, a payment key is added, the key is removed. The removal is a new commit whose parent is the commit with the key. Every clone that fetched the middle commit has a copy of the key.start the shopno keyadd payment keykey in config.envremove keyHEADevery copyclones, forks, CIparentparentfetched byWhere a deleted key still livesThree commits in a row: the shop starts, a payment key is added, the key is removed. The removal is a new commit whose parent is the commit with the key. Every clone that fetched the middle commit has a copy of the key.start the shopno keyadd payment keykey in config.envremove keyHEADevery copyclones, forks, CIparentparentfetched by
  1. The shop starts clean.
  2. The key is committed. That snapshot is now permanent, named by a hash of exactly these bytes.
  3. The "fix" is a new snapshot. Its parent is still the one with the key - history only ever grows.
  4. Anyone who fetched in between - a teammate, a fork, a CI runner - has their own copy. Nothing you do to your repository reaches theirs.

Leak it, delete it, read it back

The key below is fake, and shaped so that no real scanner will mistake it for a real one. That’s the only difference from the real thing.

The Friday fixreal git hashes
Working tree
    Staging area
      Last commit
        Commit graph

        No commits yet.

        git show <commit>:<file> prints a file as it was in any commit. HEAD~1 means “one commit before HEAD”. Anyone with a copy of the repository can do this, for every commit ever made.

        Check yourself+10 XP

        A key was pushed in yesterday's commit. Today you delete it and push again. Who can still read it?

        The one fix that works

        If a secret was pushed, treat it as stolen. Go to whoever issued it, revoke it, and issue a new one. A revoked key is worthless wherever it ended up. GitHub’s own guide puts this first, and notes that once the secret is revoked or rotated, that “may be sufficient to solve your problem.”

        You can rewrite history to take the old commit out (tools like git filter-repo do this). But GitHub’s guide is plain about what that cannot reach: other people’s clones, forks that contain the commit, cached views of the commit by its hash, and pull requests that reference it. Rewriting tidies your copy. Rotating protects you.

        Check yourself+10 XP

        You rewrite history so the leaked key is in no commit at all, and force-push. Is the key safe now?

        Stop it before it leaves

        The cheapest leak is the one that never gets pushed:

        • Keep secrets out of the working tree. Read them from the environment or a secrets manager. List local config files like .env in .gitignore so git add . can’t sweep them up.
        • Scan before you push. gitleaks checks a repository’s whole history for known secret formats, and can run as a pre-commit hook and in CI.
        • Let the server refuse. GitHub’s push protection blocks a push that contains a secret in a format it recognises, before it reaches the repository. For your own account it’s on by default when you push to public repositories.
        Check yourself+10 XP

        Which habit stops this leak from happening in the first place?

        Do it for real · your machine+50 XP bonus

        Everything above ran in your browser. Now do it on a real cluster withthe tilt platform. You need Docker, kind and Tilt, about 15 GB of free disk and 8 GB of spare RAM. Setup guide · Can my machine run it?

        Find out whether a string ever entered a repository’s history. -S lists the commits that added or removed it, and -p shows the change:

        git log -p -S "PAYMENT_KEY" --all

        Then scan a repository of your own with gitleaks:

        gitleaks git -v .

        gitleaks git reads the history, not just today’s files. (Older guides say gitleaks detect; it still works but was deprecated in v8.19.)

        Self-reported. We can't see your machine, and we don't want to. This is a bonus, never required.

        Sources

        Finished reading?

        Missions and quizzes above earn their own XP. This marks the lesson done.