Leak Lock
VS Code & OpenVSX extension · scans with Gitleaks, TruffleHog & Nosey Parker · rewrites with BFG or git

Find the secrets in your git history — then actually remove them.

Leak Lock scans a repository for credentials, files, and keywords using several detection engines at once, shows every finding — and which engine found it — and rewrites them out of every branch and tag, including the refs that only exist on the remote. It also removes any text you specify, not just what a scanner flagged. The cleanup is a script you can read, run from the panel, and verify — bash on macOS and Linux, PowerShell on Windows.

Six steps, from finding to verified clean

The whole tool is one loop: scan, review, fix, and confirm. Nothing runs until you have looked at the plan.

Find secrets with more than one engine

Gitleaks, TruffleHog and Nosey Parker can run together over your working tree and full git history, and their results are merged into one list. They disagree more than you would expect — on a test repository one engine found seven secrets where another found two — so every finding names the engines that found it and the ones that missed it. TruffleHog can go further and check whether a key still works. On top of that: a path scan for files that shouldn't be there, and an opt-in keyword pass over commit messages, historical contents, and filenames for terms you choose — like id_rsa or an internal project name.

Sized to your machine

Leak Lock checks how much machine it has before it commits to a plan — cores, memory (including the real limit inside a container, not the host's), and current load. A capable machine runs the engines in parallel, leaving cores for the editor. A modest one runs them one at a time. A genuinely constrained one runs Gitleaks alone — a single binary with no container runtime. If an engine gets skipped for capacity, the coverage panel says so and names it: fewer engines means fewer findings, and that is not something to hide. One setting overrides the lot.

Review every finding — and what was actually scanned

Findings land in a table in the main panel — each credential with its file and line, each history hit with the branch, commit, and date, and a live credential flagged VERIFIED LIVE. Alongside them sits a coverage panel: which engines ran, how many refs were scanned, which branches exist only on the remote, and whether the scan finished. A clean result is only worth as much as the scope behind it. Export the list — and the coverage — to JSON or PDF.

See what a secret actually is

A rule matching tells you a string looks dangerous. It does not tell you whether that key is a 4096-bit RSA key with no passphrase or a JWT that expired last year — and those call for very different urgency. Findings that are SSH or PEM private keys, certificates, JWTs or GCP service-account files are badged with their type, and open to show the algorithm, fingerprint, whether the key is passphrase-protected, any validity window, and the claims the artifact carries. A secret the scanner cut short is identified by reading the whole file — fifty characters of a private key is not a key. All of it runs locally, and the report never contains the key body or a JWT signature. Names inside a credential are labelled as evidence, because a key comment is what the artifact says about itself, not proof of who owns it.

Follow a finding back to the commit that introduced it

Hover a file path for the absolute one; a finding from history names the commit its path belongs to, since that file may no longer exist on disk. Click the commit hash to open that file, at that commit, on GitHub, GitLab or Bitbucket, anchored to the line. The repository itself is asked which path is real, so the link is not a guess — and a self-hosted or unrecognised host shows the hash as plain text rather than a URL that would 404.

Replace strings, remove files — or redact anything you choose

Three remediation modes: replace a detected secret with a placeholder everywhere it appears, delete a file or path outright, or write your own source text → replace with rule for content no scanner flags — an internal hostname, a private repository name, a customer identifier. Manual rules match literally or by regex, and a dry run shows exactly which commits, files and branches each one touches before anything is rewritten.

Get a script for the cleanup

Every remediation is prepared as a complete, readable script — headed # Generated by Leak Lock — that runs the rewrite across all refs. Save it, read it, hand it to a reviewer, or run it yourself outside the editor. No hidden magic. You get bash on macOS and Linux and PowerShell on Windows, each doing the same work the extension would do itself: the replacement rules go to a temp file locked to your account rather than being inlined, and the file is removed even if the rewrite fails partway.

Run it, then verify

Press the button to run the rewrite from the panel, or run the script by hand. Either way it force-pushes atomically, re-fetches, and checks every remote ref — reporting any that still carry the secret by name. A cleanup it can't verify isn't finished.

What it looks like in the editor

Two surfaces: a Control Panel docked in the sidebar for setup and scanning, and a full-width scanner in the main editor area where findings are reviewed and cleaned up.

The Leak Lock scanner showing six findings from a real three-engine scan. Each row lists the file, line number, the matched secret, a replacement value, which engines found it and which missed it, the branch, commit hash and date, and a severity label. The first two rows were each found by both Nosey Parker and Gitleaks and missed by TruffleHog; one row was found by Gitleaks alone, another by TruffleHog alone inside a large blob that the other two missed. One row is marked as living in a third-party dependency and cannot be selected for cleanup. Above the table are the scan coverage summary and the manual redaction rules editor; below it are buttons to prepare and run a BFG-based or git-only cleanup.
Review every finding, then generate the cleanup One row per secret, with the file and line, the commit and branch it lives in, a severity label — and which engines found it, and which missed it. The top two rows were corroborated by two engines each; below them, one was found by Gitleaks alone and one by TruffleHog alone — which is the whole reason for running more than one. The first row is a single secret across 3 commits and the working tree, merged into one row rather than four. Tick what you want cleaned, set the replacement, then prepare a BFG or git-only script — nothing is rewritten until you have read it.
The Scan coverage panel expanded, listing each engine with its version and finding count, the execution mode and the host it was chosen for, the number of local branches, remote branches, tags and stash entries scanned, whether refs were refreshed, a collapsed list of branches that exist only on the remote, and the ruleset, file-size limit, timeout and dependency handling in effect.
What was actually scanned A clean result is only worth what was examined, so every scan says so: which engines ran and at which versions, how many refs were covered, and whether anything was skipped. Collapsed to one line by default — but a warning is never hidden by collapsing it.
The Manual redaction rules editor, with fields for source text, a literal or regex match mode and a replacement value, and a table of two rules: a hostname matched literally, with a dry-run result underneath reporting the five commits and two files it touches and the branches they are on, and a customer identifier matched by regular expression.
Remove anything, not just what a scanner flagged Type the text yourself — an internal hostname, a private repository name, a customer identifier. Literal or regex, and a dry run shows exactly which commits, files and branches each rule touches before anything is rewritten.
The no-findings view: a shield icon, the heading No Security Issues Found, four green confirmations that no API keys, passwords, private keys or database credentials were detected, and below them the scan coverage panel listing the three engines that ran and the refs they covered.
A clean result you can check "No findings" means nothing on its own, so the coverage panel sits right underneath it — which engines ran, how many refs they covered, and whether the scan finished.
The Control Panel sidebar, with a Dependencies Setup section showing Docker Engine, Nosey Parker image, Java runtime and BFG all installed, a Target Directory section with a detected git repository, and a Start Scan button.
Control Panel Dependency status, the auto-detected target repository, and the scan trigger. One scan engine is the whole requirement — one click installs Gitleaks and TruffleHog as native binaries, no Docker and no JVM. Docker, the Nosey Parker image and BFG are optional, and their absence is noted beside "Dependencies ready" rather than blocking a scan.
The Git History Search panel, with toggles for enabling git history scanning, searching commit messages and searching file history, a maximum matches per keyword field, and a list of twenty keywords each with a remove button.
Keyword history search An opt-in pass over commit messages, historical file contents and filenames for terms you choose, beyond what the credential detectors catch.
The Remove Unwanted Files view, with the selected repository, a file and directory picker, options to prepare a single combined BFG command or one per item, a path-based git alternative with a preview-matches button, and red confirmation panels for the final history rewrite.
Guided file removal Pick paths to delete, preview the exact branches, remotes and tags they match, then confirm the rewrite behind a clearly marked final step.

And then the part that actually changes your history

Every destructive step sits behind something you have to read first. This is the whole sequence, captured from a real cleanup of the damn-vulnerable-repo fixture.

The prepared git-only cleanup script shown in full before anything runs. It begins with a bash shebang and portability notes, sets strict error handling, changes into the repository, then fetches and prunes refs, checks for unpushed commits, and materialises every remote branch locally.
1. Read the script before it runs Prepare generates the exact commands and shows them. Nothing has run yet — you can copy the script and run it yourself instead, and it performs the same safety checks either way.
A confirmation panel stating that local history has been rewritten and the secret removed, but nothing has been pushed yet and the remote is unchanged. It explains that confirming will force-push every branch and tag in a single atomic push, and offers a red confirm button beside a cancel button that keeps the remote unchanged.
2. The remote is still untouched The local rewrite and the force-push are separate steps. This gate stays on screen until you decide — and it names how many refs the push will overwrite.
An explanation shown when the remote refuses the push because the main branch is protected. It states that nothing was pushed, the remote is unchanged and the secret is still on it, that the other rejected refs are not separate problems because the push is atomic, and lists numbered steps to allow force pushes temporarily and then restore the protection.
If your main branch is protected — and it should be The push is atomic, so one protected branch rejects every ref and git reports nine failures for one cause. Leak Lock names the branch actually blocking it, says plainly that nothing was pushed and the secret is still on the remote, and gives the steps to finish — including turning the protection back on.
A green confirmation reading: Verified clean on every remote ref.
3. Verified, not assumed After the push, every remote branch and tag is re-fetched and re-checked. If nothing could be examined, it says so rather than reporting a clean result it never tested.

What the script actually does

The generated script runs the same ordered sequence whether you press the button or run it yourself. In brief:

  1. Fetch & prune — plan against current refs, not a stale local view.
  2. Check for unpushed commits — stop and name them rather than discard them.
  3. Recreate every remote-only branch locally — the step that closes the leak: push --force --all only touches refs/heads/*, so remote-only branches would otherwise keep the old history.
  4. Rewrite across all refs — BFG, filter-branch, or filter-repo, over every branch and tag.
  5. Drop backup refs, expire the reflog, repack — so the old objects are actually gone.
  6. Atomic force-push, then re-fetch and verify — all refs in one transaction; then confirm each is clean on the server.

The replacement rules live in one owner-only temporary file that the script's exit trap deletes. Verification re-reads that same file, so each secret appears exactly once in the script rather than being repeated in every check — one list, one place, and no drift between what was rewritten and what is verified. The script targets bash 3.2 and runs on Linux, macOS, and Windows under Git Bash or WSL, checking its required tools up front rather than failing halfway through a rewrite.

Rewriting history changes every commit hash — teammates re-clone afterward, and the leaked credential should be rotated regardless. It's a destructive operation, run with review. If TruffleHog flagged a key as VERIFIED LIVE, rotate it first: rewriting history does not revoke a working credential.

Install in your editor

Leak Lock ships to both the VS Code Marketplace and Open VSX, so it installs in VS Code and every OpenVSX-based editor. The extension id is nikolareljin.leak-lock on both.

VS Code

VS Code Marketplace

Extensions view (Ctrl/Cmd+Shift+X) → search “Leak Lock” → Install. Or from the terminal:

code --install-extension \
  nikolareljin.leak-lock
Open the listing
Cursor · Windsurf · Antigravity · Kiro · VSCodium

Open VSX

OpenVSX editors can't use the MS Marketplace. Search “Leak Lock” in their Extensions view, or install from the CLI (swap in your editor's binary):

cursor  --install-extension nikolareljin.leak-lock
windsurf --install-extension nikolareljin.leak-lock
codium  --install-extension nikolareljin.leak-lock
Open on Open VSX
Offline / any editor

From a VSIX

Grab the packaged .vsix from Releases, then Extensions: Install from VSIX… in the Command Palette — works in any VS Code-compatible editor.

Browse releases

Requires an editor on VS Code 1.96.0+. Docker must be installed and running — the extension installs the rest of its tooling for you (next section).

Four steps in the Control Panel

Open the panel from the 🛡️ shield icon in the Activity Bar, or run Open Leak Lock Scanner from the Command Palette. Everything below happens in the left-hand Control Panel, top to bottom.

Install dependencies

First run only, and the requirement is one scan engine — any single one of them is enough to scan. Gitleaks and TruffleHog are enabled by default and install as single binaries: no Docker, no JVM. Everything else is optional and says so rather than blocking you — Docker and the Nosey Parker image add a third engine whose upstream is archived, and Java adds BFG as an alternative to the git rewrite route that is now the default. An engine you have not installed is reported in the coverage panel and skipped; it never fails the scan.

Scan for issues

Point it at a repository (git folders are auto-detected) and run the credential scan. Results fill the table in the main area for review.

Scan strings & files

Add the opt-in keyword pass for strings, filenames, and historical contents that the credential detectors don't cover. Use it for your own naming conventions and known-bad filenames.

Remove files & directories

The Remove Files button opens the guided removal UI. Choose paths to delete or strings to replace, preview the exact matches across every ref, then generate and run the cleanup.

Add your own redaction rules

Under the results, a source text → replace with editor removes content no scanner flags — internal hostnames, private repository names, customer identifiers. Literal or regex, validated as you type, previewed against real history, and cleaned through the same verified pipeline as a detected secret.

It rewrites history. Plan for that.

! Every commit hash changes

Everyone on the repo re-clones or hard-resets after a rewrite. Agree on a time before you run it.

! Rotate the credential first

Anything that reached a remote is already compromised. Rotate it; cleaning history is the second step, not the first.

! Unpushed work blocks the rewrite

If a local branch is ahead of its remote, the run stops and lists those commits instead of discarding them. Push first.

Nothing runs without review

Every cleanup is a script you can read and a plan naming each ref that will be force-updated. You approve it.