VS Code extension that finds secrets in git history, verifies live credentials, and helps remove leaks with safe rewrite workflows.
Website | Documentation | Quick Start | Screenshots | Development
- Multi-engine scanning: Runs Gitleaks, TruffleHog, and optional Nosey Parker, then merges findings into one attributed result set.
- Live credential checks: Uses TruffleHog verification when enabled, so live credentials stand out from static pattern matches.
- Safer cleanup flow: Previews history rewrites, separates local cleanup from force-push, and verifies refs after push.
- Report comparison: Exports scan results, imports them later, and marks previous findings as resolved, still present, or unverifiable.
# Install from VS Code Marketplace
code --install-extension nikolareljin.leak-lock
# Or install from a VSIX downloaded from the Releases page
code --install-extension leak-lock-*.vsix- Activity Bar: Click the 🛡️ shield icon
- Command Palette:
Ctrl+Shift+P→ "Open Leak Lock Scanner" - Status Bar: Click the shield icon
- Click "🔧 Install Dependencies" on first use
- Installs Docker images, BFG tool, and requirements
- One-time setup with progress tracking
- Auto-Detection: Git repositories selected automatically
- Manual Selection: Choose any directory to scan
- Review Results: Examine detected secrets in detailed table
- Select Secrets: Choose which ones to remove
- Generate Commands: Automatic BFG command generation
- Execute Cleanup: One-click git history rewriting
- Export JSON: Save all current findings and metadata to a
.jsonfile - Print / Save as PDF: Use the print-friendly view from scan results for PDF reports
- Share Findings: Attach exports to tickets, audits, or remediation docs
- Import JSON: Read a report back in and check each of its findings against the repository now, marked Resolved, Still present or Unverifiable, plus what is new since the report and the commit that introduced it
- Open VS Code settings for Leak Lock.
- Enable
leakLock.gitHistoryKeywordSearch.enabled. - Configure keywords in
leakLock.gitHistoryKeywordSearch.keywords. - Optionally tune:
leakLock.gitHistoryKeywordSearch.searchCommitMessagesleakLock.gitHistoryKeywordSearch.searchFileHistoryleakLock.gitHistoryKeywordSearch.searchFileNamesleakLock.gitHistoryKeywordSearch.maxMatchesPerKeywordleakLock.gitHistoryKeywordSearch.shortKeywordFileHistoryMaxCount
Note: leakLock.gitHistoryKeywordSearch.searchFileNames is disabled by default (opt-in) because it can increase scan time on large repositories.
Default keyword profile (designed for attribution-policy and secret hygiene):
- Agent/AI attribution terms:
agent,assistant,claude,codex,copilot,gemini,gpt,chatgpt,openai,anthropic,aider,cursor,windsurf,meldbot,openclaw,nanoclaw - Sensitive terms:
password,ldap,ldap_password,bind_password,bind_dn,token,access_token,auth_token,api_key,secret,client_secret,credentials,private_key,ssh_key,id_rsaThe keyword list can include arbitrary text terms and filename fragments, not only predefined security words.
Example use case:
- Detect commit messages that mention coding agents.
- Detect potentially sensitive terms in historical file changes.
- Detect historical filenames that include specific terms (for example
id_rsa,secrets, or custom naming conventions).
- Open from sidebar: click "🗑️ Remove files"
- Select repository (git root)
- Choose multiple files and/or directories
- Option A (fast): BFG, name-based grouping (single or per-item)
- Option B (safe): Git path-based, exact paths across branches
- Click "🔎 Preview matches" for path-based mode to see exact files across branches, remotes, and tags
- Remotes are fetched automatically to avoid missing references
- Prepare and review the generated script (copy it, or save it with "💾 Save as .sh" for bash — Linux, macOS, WSL, Git Bash — or "💾 Save as .ps1" for PowerShell; both are generated on every platform)
- Final step (red): confirm to run (BFG or Git) and rewrite history
git push --force --all only pushes refs/heads/*, so a branch that exists solely on the
remote keeps its leaked history. Every Leak Lock rewrite now materialises a local branch for
each remote branch first, pushes with --atomic, and verifies every remote branch and tag
afterwards. See docs/REMOVE_FILES.md.
- Preflight blocks the rewrite if local branches hold unpushed commits (they would be discarded)
- Push plan lists every ref that will be force-updated or created, before you run anything
- Verification reports any ref where the secret or file survived
- Per-finding checkboxes, a header select-all, and
Select all/Clear allbuttons - A live "N of M cleanable findings selected" counter
- Selections and custom replacement values persist across panel refreshes (previously, preparing a command re-checked everything)
Captured from a real scan of damn-vulnerable-repo, the public test fixture — cloned fresh, seeded, and scanned with Gitleaks, TruffleHog and Nosey Parker together, then rendered from the extension's own webview.
The run found 31 findings merged from 62 raw detections (Nosey Parker 23 · Gitleaks 27 · TruffleHog 12); the table shows six of them, sampled to span the three engines. Every credential in that repository is fake by construction — AWS's own published
AKIAIOSFODNN7EXAMPLE, Stripesk_test_keys, hostnames under the reserved.exampleTLD that can never resolve, and tokens whose "random" part is the alphabet in order. Nothing carries aVERIFIED LIVEbadge because there is nothing live to verify. See docs/TEST_FIXTURE.md.Regenerate the webview images with
tools/real-scan.jsandtools/render-screenshots.js(views:results,empty,sidebar,keywords,removeFiles,prepared,pushPlan,protected,verified). The two full-editor shots are taken from a live VS Code window, since the sidebar and activity bar are editor chrome the renderer cannot produce — and they come from a separate run against the same fixture, which is why the finding count differs from the figures above. Either way, scan a clone under/tmp/reposrather than your home directory — the paths are visible in the images.
Two surfaces: the Control Panel in the sidebar is where setup and scanning live, and the scanner in the main editor area is where findings are reviewed and cleaned up. The sidebar starts things; nothing there rewrites history on its own.
Visible here: the findings count broken down by severity, select-all and per-row
checkboxes, Export JSON and Print / Save as PDF, a search box over the results, the
refs-status line — Last fetched never (stale), with Refetch now beside it, because a
scan is only as complete as the refs it covered — and the banner that marks a finding as
living in a third-party dependency and therefore not selectable for cleanup.
One row per finding, with the file and line, the commit and branches it lives in, and a severity label — plus which engines found it and which missed it. The top two rows are real corroboration: Nosey Parker and Gitleaks both found the AWS key and the Azure connection string, each merged into a single row. Below them, Gitleaks found a Stripe key the other two missed, and TruffleHog found a credential inside a large blob that Nosey Parker and Gitleaks both missed — which is the whole reason for running more than one.
The first row also shows aggregation working: one secret, in 3 commits and still in the
working tree, as a single row rather than four. The node_modules row is marked
Dependency · not your code and cannot be selected for a history rewrite.
Collapsed to one line by default. Expanded, it reports the engines and versions that ran, the execution mode and the host it was sized for, the refs covered, and the settings in effect. Warnings are promoted into the collapsed summary, so collapsing hides volume and never a caveat.
Remove text no scanner flagged. Literal or regex, and a dry run reports the commits, files and branches a rule touches before anything is rewritten.
"No findings" means nothing without its scope, so the coverage panel sits directly beneath it.
The shield icon in the activity bar opens it. Everything starts from this sidebar:
dependency status (Docker and the Nosey Parker image, Java and BFG — all optional, and
installed on first run from the Details pane), the auto-detected target repository, the
scan trigger, and the entry points to keyword history search and Remove Files.
Opt-in, and separate from the credential detectors: commit messages, historical file contents and filenames are searched for terms you choose — useful for finding what a pattern-based scanner has no signature for.
Pick files or directories, then choose BFG (fast, name-based across all history) or path-based git (exact paths across branches, remotes and tags, with a match preview). Either way the rewrite sits behind a clearly marked final step.
Every destructive step sits behind something you have to read first.
1. 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.
2. The remote is still untouched. The local rewrite and the force-push are separate steps, and this gate stays on screen until you decide. It names how many refs the push will overwrite.
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 a single 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 afterwards.
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.
leak-lock/
├── extension.js # Main extension entry point
├── leakLockPanel.js # Main area panel provider
├── welcomeViewProvider.js # Activity bar welcome view
├── project-scan.js # Legacy compatibility
├── package.json # Extension manifest
├── media/
│ └── shield.svg # Extension icon
└── docs/ # Documentation files
- Main extension activation and command registration
- Dependency management and cleanup
- Status bar integration
- Main area webview panel provider
- Scanning workflow implementation
- Results display and remediation UI
- Activity bar sidebar integration
- Welcome interface and launch button
See also:
- docs/USER_GUIDE.md — full user guide
- docs/REMOVE_FILES.md — Remove Files flow details
- Node.js 22.13.0+
- VS Code 1.125.0+
- Docker (for testing scanning functionality)
# Clone repository
git clone https://github.com/nikolareljin/leak-lock.git
cd leak-lock
# Install dependencies
npm install
# Launch in development mode
code . # Press F5 to launch extension host# Run tests
npm test
# Manual testing
# 1. Press F5 to launch extension host
# 2. Click shield icon in activity bar
# 3. Test dependency installation
# 4. Test scanning workflowLeak Lock runs more than one detection engine and merges the results. They disagree more than you would expect, so the results table names which engine found each finding and which enabled engines did not.
- Project: https://github.com/gitleaks/gitleaks · MIT · actively maintained
- Install:
brew install gitleaks,apt install gitleaks, or a release binary. No Docker, no JVM. - Purpose: secret detection across full git history and the working tree
- Why it's the default: maintained, fast, and its ruleset still receives new detectors. It scans every ref (
--log-opts=--all) and, in a separate pass, the working tree — including untracked and ignored files. - Extra detail it provides: end line, column range, entropy, commit author and email, and a stable fingerprint (which is what makes baselines work)
- Settings:
leakLock.gitleaks.binaryPath,leakLock.gitleaks.configPath(custom TOML rules and allowlists),leakLock.gitleaks.baselinePath
- Project: https://github.com/trufflesecurity/trufflehog · AGPL-3.0 · actively maintained
- Install:
brew install trufflehogor a release binary. Leak Lock invokes it as an external process only — no bundling, no linking — so the extension remains MIT. - Purpose: the one thing no other engine here does — checking whether a discovered credential is still live, against 700+ providers
- Why it matters: a verified AWS key in a five-year-old commit is an active incident needing rotation and a history rewrite. An unverified high-entropy string is probably noise. Leak Lock ranks a verified finding above every rule-name heuristic and badges it
VERIFIED LIVE. - Privacy: verification makes read-only network calls to third-party providers using the discovered credential. It is therefore off by default — enable
leakLock.trufflehog.verifydeliberately. - Limits: reports no column range, entropy or fingerprint. Those are shown as not provided by this engine rather than left blank.
- Project: https://github.com/praetorian-inc/noseyparker · Apache-2.0
- Image:
ghcr.io/praetorian-inc/noseyparker:v0.24.0(pinned; requires Docker) - Status:
⚠️ archived read-only upstream on 2026-04-24.v0.24.0(May 2025) is its final release, so its ruleset can no longer receive detectors. On a test fixture it reported 2 findings where Gitleaks reported 7, missing an AWS key and a GitHub PAT in plain committed source. - Why it is still here: an excellent history walker and the best deduplication model of the three — it groups matches sharing a rule and capture groups into a single finding, which keeps large result sets reviewable.
- Settings:
leakLock.noseyParker.ruleset(default,default+assets,all),leakLock.noseyParker.suppressRedundant,leakLock.noseyParker.maxFileSizeMb,leakLock.noseyParker.image
leakLock.scan.executionMode defaults to auto, which sizes the plan to your machine —
core count, available memory (respecting container limits, since os.totalmem() reports
the host's memory inside a container), and current load:
- Capable host (≥ 6 cores, ≥ 8 GB, not saturated) — all engines in parallel,
capped at
cores − 2so the editor still has room - Modest host — all engines, one at a time. Slower, but nothing is skipped
- Constrained host (≤ 2 cores or < 4 GB) — Gitleaks only: a single static binary with no container runtime or JVM, and the only engine with a maintained ruleset
If host capacity causes an engine to be skipped, the coverage panel says so and names
it. Fewer engines means fewer findings, so a downgrade is never silent. Set
executionMode to parallel, sequential or single to decide for yourself.
leakLock.scan.engines sets which run, and in what order. Gitleaks and TruffleHog are
enabled by default — ["gitleaks", "trufflehog"]. Both are single binaries needing no
container runtime and no JVM, so a default install scans without Docker. Nosey Parker is
available but off by default: its upstream is archived and it runs only as a container
image. Add it with ["gitleaks", "trufflehog", "noseyparker"].
A missing engine binary disables that engine, never the whole scan, and the coverage panel says which engines ran and which did not, so an engine you have not installed is visible rather than silently absent.
Enabling TruffleHog does not by itself make any network call; verification is the separate
leakLock.trufflehog.verify setting, off by default.
Running more than one is the point: they have genuinely different rulesets, and the attribution line tells you when one of them is falling behind.
If an engine you have installed is reported as missing, it is almost always PATH — a
GUI-launched VS Code does not inherit your shell's. Leak Lock searches the usual install
locations (~/.local/bin, /usr/local/bin, /opt/homebrew/bin, …); for anything else,
set leakLock.trufflehog.binaryPath or leakLock.gitleaks.binaryPath.
A VS Code launched from Finder inherits no shell PATH at all on macOS, so a tool that
works in Terminal can still be reported as missing. Leak Lock searches for these tools by
absolute path, which means the installs below need no ~/.zshrc edit.
brew install --cask temurin # Java, for BFG — installs a system JDK
brew install git-filter-repo # the Git-only rewrite pathTwo traps worth naming, because both look like a broken install:
brew install openjdkis keg-only. Homebrew deliberately does not putjavaon yourPATH, because macOS ships its own stub. Leak Lock searches Homebrew'sopenjdkprefixes (/opt/homebrew/opt/openjdk*/bin,/usr/local/opt/openjdk*/bin),JAVA_HOMEand/usr/libexec/java_home, so it finds the JVM anyway — butjava -versionin a terminal keeps saying "not found" until you add the prefix yourself. Thetemurincask avoids the question entirely. To point Leak Lock at a specific JVM, setleakLock.java.path.pip install --user git-filter-repodoes not write to~/.local/binon the system Python. macOS's framework Python uses~/Library/Python/<version>/bin. Leak Lock asks Python where it actually wrote (sysconfig) rather than assuming, so either location is found; Homebrew's Python refuses--useroutright under PEP 668, which is whybrewis the recommended route on macOS.
Everything is optional: Java is needed only for BFG, and git-filter-repo only for the Git-only rewrite. Scanning needs neither.
- Purpose: Git history rewriting and cleanup
- Project: BFG Repo-Cleaner — https://rtyley.github.io/bfg-repo-cleaner/
- Tool: Java-based command line utility
- Why it’s good: Safer, faster alternative to
git filter-branchfor removing large files or sensitive data from history; robust, battle‑tested, and widely recommended. - Capabilities: Remove secrets from entire git history, delete files/folders by name
- Integration: Automated command generation and execution
- Note: Deletion matches by filename/folder name across history (not full path)
- Seamless integration: combines multi-engine detection with BFG/git removal in a single VS Code experience.
- Safer defaults: Previews, path‑based alternative, and confirmation steps reduce risk.
- Productivity: One panel to scan, review, prepare commands, and execute — no shell juggling.
- Cross‑platform: Gitleaks and TruffleHog run as native binaries with no container runtime; Docker is needed only for the optional Nosey Parker engine. Built‑in helpers keep it reliable on Windows, macOS, and Linux.
- Purpose: Exact path-based history rewriting across branches
- Command:
git filter-branch --index-filter 'git rm -r --cached --ignore-unmatch <path> ...' -- --all - Preview: Lists per-branch matches before running
- Integration: Alternative path-safe removal flow in main panel
leak-lock.openPanel- Open main scanner interfaceleak-lock.scanRepository- Start repository scanningleak-lock.fixSecrets- Open remediation interfaceleak-lock.openRemoveFiles- Open Remove Files flowleak-lock.cleanup- Clean up all dependencies
Required — one scan engine. Any single one of Gitleaks, TruffleHog or Nosey Parker is enough to scan. Setup is complete the moment one of them works; a second widens coverage but does not decide whether a scan can run. Git itself is required, as is credential-lens — which ships inside the extension, so there is nothing to install.
Optional — everything else. Their absence is named next to "Dependencies ready" rather than blocking anything:
| What it adds | Without it | |
|---|---|---|
| A second or third engine | Rules the others miss — they disagree more than you would expect | Fewer findings, and the coverage panel says which engines ran |
| Docker | Required only by Nosey Parker | Gitleaks and TruffleHog are native binaries |
| Nosey Parker image | An extra engine; upstream archived | Off by default |
| Java + BFG | An alternative history-rewrite engine | The git route is the default and needs no JVM |
| git-filter-repo | The Git-only cleanup mode | BFG, if Java is present |
When no engine at all is installed, Dependencies Setup opens by itself and names the choice. A missing engine disables that engine, not the scan.
The extension provides comprehensive cleanup functionality:
- Removes Nosey Parker Docker image
- Deletes BFG tool jar file
- Cleans up temporary files and directories
- Removes Docker volumes created by extension
Use command palette: Leak Lock: Clean Up Dependencies
We welcome contributions! Areas for improvement:
- 🔍 Additional secret detection patterns
- 🎨 UI/UX enhancements
- 📖 Documentation improvements
- 🧪 Test coverage expansion
Current version lives in VERSION, and package.json must match it. Current release text lives in RELEASE_NOTES.md. The release script uses that file for GitHub Releases and annotated git tags, and can insert it into CHANGELOG before publishing with --sync-changelog. Sync will not overwrite a changelog section that already exists unless --force is passed, since those entries are expanded by hand after they are generated. npm run check:release validates all three sources and runs on every pull request.
- Import previous scan reports and check which findings are resolved.
- Enforce repository identity before comparing reports.
- Show findings introduced since the imported report.
- Keep imported findings as historical records, not cleanup targets.
MIT License - see LICENSE file for details.
- 🌐 Website - Overview, screenshots and install instructions
- 📖 Documentation - Comprehensive guides
- 💬 Issues - Bug reports
- 📧 Contact: Create an issue for support
Made with ❤️ for secure development
Updated daily. Total and unique cloners over the last 14 days.











