leakferret

leakferret (Ruby gem)

MCP-native secret scanner — verified findings, agent-applied rewrites.

Gem Version MIT MCP Badge

leakferret finds, verifies, and rewrites a leaked secret

Ruby gem wrapper around the native leakferret binary. This gem ships no scanning logic of its own: it installs a tiny Ruby shim plus the prebuilt, statically-linked engine (written in Rust). All the work — scan, classify, verify, rewrite — happens in that single binary.

You get a precompiled platform gem with the binary bundled inside it, the same way nokogiri and sorbet-static ship their native code, for x86_64 Linux (glibc), macOS (x86_64 and arm64), and x86_64 Windows. The binary travels through RubyGems with the gem — no download, no network access, no Rust toolchain — and you can audit exactly what you will run with gem unpack leakferret. The gem has no fetch-and-run code at all. On any other platform the source gem installs and points you at a one-line fix (build from source, or set LEAKFERRET_BIN); it never downloads a binary.

What leakferret does

leakferret finds hardcoded secrets and API keys in your code and helps you remove them, in five stations:

  1. Scan — regex pre-filter over files; respects .gitignore and also reads dotfiles like .env.
  2. Catalog — a signed database of known-public example credentials (Stripe test keys, AKIAIOSFODNN7EXAMPLE, jwt.io samples) so documented examples are marked FIXTURE instead of false-alarming.
  3. Classify — a REAL / FIXTURE / UNKNOWN verdict, from offline heuristics or by asking the host editor/agent language model (no extra API key, no cost).
  4. Verify — a real but harmless API call to the provider (AWS SigV4, GitHub, GitLab, Stripe, OpenAI, Anthropic, Slack, Twilio, SendGrid, Mailgun, Datadog, Heroku, npm, PyPI, DigitalOcean) to confirm a key is live, plus a trufflehog fallback.
  5. Rewrite — swap a hardcoded literal for an environment-variable lookup (ENV.fetch in Ruby, process.env in JS, os.environ in Python), add a .env.example line, and print secret-manager seed commands.

Privacy invariant: the full secret value never leaves your machine. Only a redacted first-4-plus-last-4 preview (e.g. AKIA...4XYZ) is ever written to a report, log, network message, or model prompt. Verification calls go straight from your machine to the provider — leakferret has no servers.

Install

gem install leakferret

RubyGems and Bundler select the right precompiled gem for your platform automatically, so the binary is already there after gem install — no first-run download. Inspect it before you trust it:

gem unpack leakferret        # the bundled binary is at lib/leakferret/bin/

Released gems carry GitHub build provenance (SLSA), so you can verify each gem was built from the tagged source in CI. On a platform without a prebuilt binary (e.g. aarch64-linux or Alpine/musl), the source gem still installs — so bundle install resolves — and tells you to build from source (cargo install leakferret-cli) or set LEAKFERRET_BIN. It never downloads.

Add it to a Gemfile for project-local use:

gem 'leakferret'

Requires Ruby >= 3.1.

CLI

The gem installs a leakferret executable that simply execs the binary, so every subcommand and flag works exactly as upstream:

leakferret scan .
leakferret verify . --only-verified
leakferret rewrite . --apply --backend doppler
leakferret baseline init
leakferret catalog info
leakferret mcp                 # MCP server on stdio

leakferret scan --git walks commit history. Output formats are pretty (colored terminal), json, and sarif (for GitHub Code Scanning).

Ruby API

require 'leakferret'

# Regex pre-filter only.
findings = Leakferret.scan('.')

# + provider-verified (live HTTP to GitHub / Stripe / AWS / ...).
findings = Leakferret.verify('.', mode: 'only-verified')

# + propose rewrites for REAL findings.
findings = Leakferret.rewrite('.', backend: 'doppler')

# Apply rewrites in place.
Leakferret.rewrite('.', apply: true)

Each Finding is a hash with path, line, column, pattern, severity (critical/high/medium/low), verdict (real/fixture/unknown), match_redacted, confidence, verification, and fingerprint.

Rewrite a leak

rewrite turns a hardcoded secret into an env-var lookup and helps you move it into a secret manager:

leakferret rewrite . --dry-run-diff               # preview the change, touch nothing
leakferret rewrite . --apply                      # write `ENV.fetch("KEY")` in place + add to .env.example
leakferret rewrite . --apply --backend doppler    # also print seed commands for your manager

--backend accepts env (default), vault, doppler, aws-secrets-manager, infisical. By default it only rewrites findings confirmed REAL/live; add --include-unknown to also fix unconfirmed candidates.

Use it in CI

leakferret is one binary with clear exit codes (0 = clean, 1 = findings), so it drops into any CI. The recommended pattern: baseline once, then verify on every build so you only fail on new secrets.

# One-time, on a repo that may already have findings:
leakferret baseline init            # fingerprints current findings (HMAC, never the raw secret)
git add .leakferret-baseline.json   # commit it — the per-repo salt is auto-gitignored

After that, verify ignores anything in the baseline and fails only on new leaks.

GitHub Actions — use the dedicated action (uploads SARIF to Code Scanning):

- uses: leakferrethq/leakferret-action@v1
  with: { path: ., fail-on: any }

CircleCI:

jobs:
  secrets:
    docker: [{ image: cimg/ruby:3.3 }]
    steps:
      - checkout
      - run: gem install leakferret
      - run: leakferret verify . --format sarif > leakferret.sarif
      - store_artifacts: { path: leakferret.sarif }

GitLab CI / Argo Workflows / Jenkins / anything else — identical recipe:

gem install leakferret
leakferret verify .                 # exits 1 on any REAL finding -> fails the job

Useful flags: --only-verified (fail only on provider-confirmed live keys), --verify-mode ever-verified (with a baseline, fail on anything that ever verified live), --format sarif|json.

Use it with AI agents (MCP)

leakferret is also an MCP server, so a coding agent (Cursor, Claude, Continue) can scan, verify, and rewrite before it commits. Add it to your editor's MCP config:

{
  "mcpServers": {
    "leakferret": { "command": "leakferret", "args": ["mcp"] }
  }
}

In Cursor: Settings → MCP → Add. In Claude Desktop: the mcpServers block of claude_desktop_config.json. Tools exposed: scan_repository, classify_candidates, verify_finding, propose_rewrite, baseline_diff.

Running leakferret mcp directly in a terminal looks like it hangs — that's correct. It's a stdio JSON-RPC server waiting for your editor to connect, not a command you run by hand.

Using a local binary

Every leakferret wrapper honors the LEAKFERRET_BIN environment variable. Point it at a binary on disk and the wrapper runs that instead of the downloaded copy:

export LEAKFERRET_BIN=/opt/leakferret/leakferret
leakferret scan .

For air-gapped or offline installs, set LEAKFERRET_SKIP_DOWNLOAD=1 to skip the release download and position the binary yourself.

Block commits locally (pre-commit hook)

Catch 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 offline; --fail-on any exits non-zero on any non-fixture finding (documented examples like AKIAIOSFODNN7EXAMPLE are still ignored). Pair with leakferret baseline init to block only on new secrets, or commit the hook to .githooks/ and run git config core.hooksPath .githooks to share it with a team.

License

MIT for this gem and the bundled binary. The fixture catalog data is CC-BY-SA-4.0 — see leakferret-catalog.


Part of leakferret · leakferret.com · maintained by Maria Khan <missusk@protonmail.com>.