Documentation
leakferret is one Rust binary that is engine, CLI, and MCP server. It finds hardcoded secrets, confirms which are live, and rewrites them to read from an environment variable. Everything below reflects v0.1.4.
Install
Every package runs the same prebuilt binary. The Ruby gem bundles it inside precompiled platform gems, so gem install ships the binary through RubyGems with no download (audit it with gem unpack leakferret). The other wrappers download it from GitHub Releases on first use and verify it against a pinned SHA256.
# npm (CLI) npm i -g @leakferret/cli # Ruby gem gem install leakferret # Go go install github.com/leakferrethq/leakferret-go/cmd/leakferret@latest # Rust, from source cargo install leakferret-cli # Or grab a binary from the releases page and put it on $PATH # https://github.com/leakferrethq/leakferret/releases
LEAKFERRET_BIN — point it at a local binary to use your own (offline or development use).Quick start
# 1. Fast regex scan of the working tree $ leakferret scan . # 2. Scan + classify + verify which keys are actually live $ leakferret verify . # 3. See the env-var rewrite as a diff, without touching files $ leakferret rewrite . --dry-run-diff # 4. Apply the rewrites in place $ leakferret rewrite . --apply
scan respects .gitignore and also reads dotfiles such as .env. Add --git to walk commit history instead of the working tree.
Block commits locally (pre-commit hook)
Stop a secret before it is ever committed. From your repo root:
$ cat > .git/hooks/pre-commit <<'HOOK' #!/bin/sh # Offline secret scan (no network). Blocks the commit on any finding. leakferret verify . --verify-mode none --fail-on any || { echo "leakferret blocked this commit. Bypass: git commit --no-verify" exit 1 } HOOK $ chmod +x .git/hooks/pre-commit
--verify-mode none keeps it fully offline; --fail-on any exits non-zero on any non-fixture finding (documented examples like AKIAIOSFODNN7EXAMPLE are still ignored). Pair with leakferret baseline init so the hook only blocks on new secrets.
CLI reference
Global options apply to every command: -q/--quiet, -v/-vv/-vvv (verbosity), -h/--help, -V/--version.
leakferret scan [PATH]
Regex pre-filter only — no classifier, no verifier. Fastest mode.
| Flag | Meaning |
|---|---|
--git | Scan git history (HEAD's reachable commits) instead of the working tree. |
--all | With --git: scan every branch and tag, not just HEAD — catches secrets on un-merged branches. |
--since <REV> / --until <REV> | History window for --git (e.g. HEAD~10, a tag, a SHA). |
--max-depth <N> | Cap on commits walked (safety valve for huge histories). |
-f, --format | pretty (default), json, sarif. |
--show-fixtures | Include FIXTURE-classified findings in output. |
--exclude [<GLOB>…] | Extra glob excludes, on top of .gitignore. |
--only [<PATH>…] | Limit to specific files (pre-commit-hook mode). |
--only-verified | Emit only findings the verifier confirms live (forces the verifier to run). |
--fail-on <none|any|real|verified> | Exit non-zero when findings at this level are present. any blocks on any non-fixture finding — use it with --verify-mode none for an offline pre-commit gate. Omitted keeps the historical exit codes. |
leakferret verify [PATH]
Scan + offline-heuristic classify + provider verification. Inherits all scan flags, plus:
| Flag | Meaning |
|---|---|
--verify-mode | none, best-effort (default), only-verified, ever-verified. |
--verifier-timeout-secs <N> | Per-verifier timeout (default 10). |
--update-baseline | Record the current findings into the baseline (writes the baseline / history / salt). Off by default — a plain verify never writes to your repo. |
leakferret rewrite [PATH]
Scan + classify, then propose ENV-fetch rewrites for REAL findings. Inherits all scan flags, plus:
| Flag | Meaning |
|---|---|
--apply | Apply rewrites in place (default is dry-run). |
--dry-run-diff | Print the unified diff per file without writing. |
--check | CI mode: like --dry-run-diff but exits 1 if any rewrites are pending. |
--backend | Seed-command target: env (default), vault, doppler, aws-secrets-manager, infisical. |
leakferret baseline <init|show|ignore>
Manage .leakferret-baseline.json — one-way HMAC fingerprints of acknowledged findings, so CI fails only on new leaks. init creates it (and gitignores the secret salt), show prints it, ignore --fingerprint <fp> acknowledges a finding. scan/verify are read-only and never write these files — you record with verify --update-baseline.
leakferret catalog <info|list|test|refresh>
info prints catalog metadata; list dumps entries as JSON; test <value> prints the verdict for a value; refresh fetches a fresh catalog from the CDN and verifies its Ed25519 signature before persisting it.
leakferret mcp
Start the MCP server on stdio. See Editors & agents.
Output formats
| Format | Use |
|---|---|
pretty | Colored terminal output (default). |
json | Structured findings for scripting and pipelines. |
sarif | For GitHub Code Scanning; the Action uploads it for you. |
Allowlisting false positives
Suppress a single finding with an inline comment pragma, kept next to the value it concerns:
API_KEY = "AKIA…" # leakferret:allow aws_access_key reason="tutorial fixture"
- A pragma on the same line, or the line immediately above, suppresses that finding.
- With a
pattern_idit only suppresses that pattern; without one it suppresses all patterns on the line. #,//, and/* */comment markers all work.
Editors & coding agents (MCP)
MCP (Model Context Protocol) lets a coding agent call leakferret's pipeline itself — so it self-checks before committing. The server exposes scan_repository, classify_candidates, verify_finding, propose_rewrite, and baseline_diff, plus a classify prompt.
The shared config block (works for Claude Desktop, Cursor, and Continue):
{
"mcpServers": {
"leakferret": {
"command": "npx",
"args": ["@leakferret/mcp"]
}
}
}
If you installed the native binary, point at it directly instead:
{
"mcpServers": {
"leakferret": { "command": "leakferret", "args": ["mcp"] }
}
}
Claude Code
$ claude mcp add leakferret -- npx -y @leakferret/mcpThen ask Claude to scan before it commits. Use claude mcp list to confirm it registered.
Cursor
Create .cursor/mcp.json in the project (or the global one) with the shared block above. Cursor shows the server under Settings → MCP.
Continue
Add the shared block to ~/.continue/config.json (the mcpServers key). Restart the IDE panel to load it.
Claude Desktop
Edit claude_desktop_config.json and add the shared block, then restart the app:
| OS | Path |
|---|---|
| macOS | ~/Library/Application Support/Claude/claude_desktop_config.json |
| Windows | %APPDATA%\Claude\claude_desktop_config.json |
VS Code extension
Install leakferret from the VS Code Marketplace (or Open VSX for VSCodium/Cursor). It bundles the binary and surfaces findings inline.
CI
GitHub Actions
# .github/workflows/secrets.yml name: secrets on: [push, pull_request] jobs: scan: runs-on: ubuntu-latest permissions: security-events: write # for SARIF upload steps: - uses: actions/checkout@v4 - uses: leakferrethq/leakferret-action@v1 with: verify-mode: only-verified fail-on: any
Inputs: version (default latest), path, verify-mode, format (default sarif), fail-on (low|medium|high|critical|any), baseline-path, upload-sarif, category, github-token. Outputs: findings-count, verified-count, sarif-path.
Any other CI
Install the binary and run a command — exit code 1 means action needed:
$ leakferret verify . --verify-mode only-verified --format json $ leakferret rewrite . --check # exits 1 if rewrites are pending
Baselines
A baseline records the findings you already know about, so CI fails only on new leaks. verify is read-only — it never writes to your repo — so you record a snapshot explicitly:
# one-time: create the baseline (gitignores the secret salt for you) $ leakferret baseline init # record current findings, then commit the baseline $ leakferret verify . --update-baseline $ leakferret baseline ignore --fingerprint <fp> # acknowledge one finding
Commit .leakferret-baseline.json and .leakferret-history.jsonl; .leakferret-salt stays gitignored — it must remain secret. In CI, a plain leakferret verify . then fails on any finding not already in the baseline.
How it works
- Scan — regex pre-filter over files (respects
.gitignore, reads dotfiles, optionally walks history). - Catalog — candidates checked against a signed database of known-public examples; matches are marked FIXTURE.
- Classify — verdict per candidate: REAL, FIXTURE, or UNKNOWN. Runs offline by default.
- Verify — one harmless API call confirms a key is LIVE. The call goes straight to the provider.
- Rewrite — swaps the literal for an env-var lookup, appends to
.env.example, prints secret-manager seed commands.
Verifiers
Around 25 providers are verified natively: AWS (SigV4), GitHub, GitLab, Stripe, OpenAI, Anthropic, Slack, Twilio, SendGrid, Mailgun, Datadog, Heroku, npm, PyPI, DigitalOcean, Hugging Face, Groq, Replicate, Notion, Postman, Figma, Linear, Square, Shopify, Databricks. leakferret detects 60+ secret types in all.
trufflehog binary if it's on your PATH (or pointed at by LEAKFERRET_TRUFFLEHOG_BIN). Without it, those findings stay UNKNOWN rather than failing. trufflehog is an optional, user-installed AGPL-3.0 tool invoked as a separate process — not bundled, modified, or redistributed by leakferret.Each verification is a single least-privilege call (e.g. AWS STS GetCallerIdentity). Set --verifier-timeout-secs to bound slow networks. --verify-mode ever-verified fails on keys that were ever live, even if revoked now.
Fixture catalog
A signed database of documented-public example credentials (Stripe test keys, AKIAIOSFODNN7EXAMPLE, jwt.io samples, RFC examples). It is bundled into the binary, so a fresh install already suppresses these. Refresh and signature-verify an update with leakferret catalog refresh; inspect with leakferret catalog info.
Privacy & security
The full secret value lives only on disk. It is never serialized, logged, or sent in any report, log, network message, or model prompt. Only a redacted first-4 + last-4 preview (e.g. AKIA…4XYZ) ever leaves the process — a dedicated test enforces this. Verification calls go straight from your machine to the provider; leakferret has no servers and collects nothing. Baselines store one-way HMAC fingerprints, never the raw secret.
Platforms & caveats
Prebuilt binaries for v0.1.0:
x86_64-unknown-linux-gnux86_64-apple-darwin,aarch64-apple-darwinx86_64-pc-windows-msvc,aarch64-pc-windows-msvc
cargo install leakferret-cli from source, or set LEAKFERRET_BIN.- Classification is offline by default; there is no built-in LLM call in the CLI.
- The
trufflehogfallback is optional and only used if the binary is present. - Git-history scanning (
--git) and--only-verifiedare mutually exclusive for now.
Troubleshooting
| Symptom | Fix |
|---|---|
| Install can't download the binary | Set LEAKFERRET_BIN to a local binary, or install via cargo install leakferret-cli. |
| A documented example key is flagged | Run leakferret catalog refresh, or add a leakferret:allow pragma. |
| A key shows UNKNOWN instead of LIVE | No native verifier for that provider and no trufflehog on PATH, or the verifier timed out. |
| CI doesn't fail on a known leak | Check --verify-mode / fail-on, and that the finding isn't in the baseline. |