Skip to main content
Glama

πŸ›‘οΈ crv-leak-scan

Find the secrets you already leaked β€” before attackers do. Then let your AI fix them.

crv-leak-scan rips through a Git repository β€” working tree and commit history β€” hunting for Web3 RPC endpoints, crypto private keys, seed phrases, cloud tokens, AI keys, and database credentials. Keys that were "deleted" in a later commit are still in history. Attackers clone history. So does this tool.

Three ways to run it:

  • CLI β€” scan, mask, remediate-by-hand, exit codes for CI: npx crv-leak-scan

  • Pre-commit shield β€” block leaking commits locally: --install-hook

  • MCP server β€” an AI agent drives the whole detect β†’ remediate β†’ purge β†’ verify pipeline autonomously: --mcp

One binary. Two runtime dependencies (picocolors, the MCP SDK). Everything else is Node stdlib. Scans the last 100 commits of a modern repo in under two seconds.

npx crv-leak-scan

Why

Every leaked key follows the same lifecycle:

  1. A dev pastes a key into config.ts "just for local testing".

  2. It gets committed.

  3. Someone notices and deletes the line.

  4. Everyone believes it's gone.

It is not gone. git log remembers. Bots scrape every public commit on GitHub within minutes, specifically grepping for the prefixes below. crv-leak-scan is the counter-move: scan locally, rotate early, purge properly.

Related MCP server: opzyai

Quick start

# Scan working tree + last 100 commits of the current repo
npx crv-leak-scan

# Everything since day one, across all refs
npx crv-leak-scan --full-history

# Wire the pre-commit shield (blocks leaking commits locally)
npx crv-leak-scan --install-hook

# Check your setup: git, hook, wordlist integrity, detector self-test
npx crv-leak-scan --doctor

Real output β€” a key committed once and deleted later, found in history:

πŸ”΄ [CRITICAL] Alchemy Web3 RPC Endpoint
   File   : config.ts:1
   Origin : git history (commit 64db0602562a02ec707177c2b3d97a7d499ebc7f)
   Secret : https://eth-mainnet.g.alchemy.com/v2/kQ7z...****
   Fix    : Delete the key in the Alchemy dashboard (Apps β†’ API Keys), create a
            replacement, and review the app's usage stats for abuse. Purge the
            secret from history with `git filter-repo` (BFG also works), then
            force-push and have all clones re-clone.

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  Scanned   1 file Β· 2 commits Β· 0.1s
  Findings  2 (2 unique secrets)
  Severity  πŸ”΄ 2 critical
  By type   Alchemy Web3 RPC Endpoint Γ—1 Β· Infura Web3 RPC Endpoint Γ—1
────────────────────────────────────────────────────────────────
❌ FAIL β€” 2 leaks at or above "critical".
   1. Revoke/rotate every key above in its provider dashboard β€” assume abuse.
   2. Purge history: `git filter-repo --replace-text expressions.txt`, then force-push.
   3. Prevent: move real secrets to a gitignored `.env` and add `.crvignore` for fixtures.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Secrets are never printed. Every finding is masked (sk_live_...9a4f, https://mainnet.infura.io/v3/4a9f...****) β€” safe to paste in Slack, CI logs, or a standup screen.

What it detects

Class

Detector

Severity

Web3 RPC

Alchemy (*.g.alchemy.com/v2/…)

πŸ”΄ critical

Web3 RPC

Infura (/v3/<32-hex>)

πŸ”΄ critical

Web3 RPC

QuickNode (*.quiknode.pro/<token>)

πŸ”΄ critical

Web3 RPC

Ankr (rpc.ankr.com/<project>)

πŸ”΄ critical

Web3 RPC

Tatum (/v3/blockchain/node/…/<uuid>)

πŸ”΄ critical

Crypto

Ethereum private key, 0x-prefixed 64-hex

πŸ”΄ critical

Crypto

Ethereum private key, bare 64-hex (entropy + context gated)

πŸ”΄ critical

Crypto

BIP-39 seed phrase β€” 12 or 24 consecutive wordlist words

πŸ”΄ critical

Crypto

Solana private key (base58, 87–88 chars)

πŸ”΄ critical

Cloud/AI

OpenAI sk-proj-… and legacy sk-… keys

πŸ”΄ critical

Cloud/AI

Anthropic sk-ant-… keys

πŸ”΄ critical

Cloud/AI

Google / Gemini AIzaSy… keys

🟑 high

Cloud

AWS Access Key ID (AKIA/ASIA…)

🟑 high

Cloud

AWS Secret Access Key (40-char, keyword-gated)

πŸ”΄ critical

Payments

Stripe live secret keys (sk_live_, rk_live_)

πŸ”΄ critical

VCS

GitHub PATs (ghp_/gho_/ghu_/ghs_/ghr_, github_pat_)

πŸ”΄ critical

Database

Postgres / MySQL / MongoDB / Redis / AMQP URIs with passwords

🟑 high

False-positive elimination (the part most scanners get wrong)

Three layers, applied in order:

  1. Placeholder rejection β€” YOUR_API_KEY, CHANGEME, 0x000…000, deadbeef repeats, sequential constants, xxxxx runs, ${TEMPLATE} variables, and the canonical dev mnemonics (test test … junk, all-abandon) are silently ignored.

  2. Shannon entropy gate β€” formatless hex/base64 candidates must clear > 3.8 bits/char (H(X) = -Ξ£ P(x)Β·logβ‚‚ P(x)). A true random key sits near its alphabet's ceiling; aaaa… and 123456… sit at zero.

  3. Context gate β€” bare 64-char hex (indistinguishable from transaction hashes by shape) only fires near key-flavored keywords (private key, wallet, signer, …). 0x-prefixed keys fire on shape alone.

Full history, on your terms

Flag

Behavior

(default)

Working tree + last 100 commits β€” sub-2-second feel

--depth <n>

Last n commits

--full-history

Every commit, every ref β€” day one to HEAD

--no-history

Working tree only

--staged

Only what this commit would introduce (used by the hook)

Untracked and uncommitted files are always scanned. Deleted keys come back as findings with the exact commit SHA, ready to feed to git filter-repo.

The pre-commit shield

npx crv-leak-scan --install-hook

Writes a clearly-marked block into .git/hooks/pre-commit (respects core.hooksPath, never clobbers an existing hook, idempotent, reversible with --uninstall-hook). From then on:

  • clean commit β†’ scan runs in milliseconds, commit proceeds

  • leaking commit β†’ blocked before it ever reaches GitHub

βœ… CLEAN β€” no secrets found (staged changes (git diff --cached)).   β†’ commit lands
❌ FAIL β€” 1 leak … STRIPE_SECRET="sk_live_…"                        β†’ commit blocked

Emergency bypass: git commit --no-verify (then fix it, don't make a habit of it).

Silencing intentional fixtures

Tests contain fake keys. Two precision tools β€” silence one line or one path, never the whole tool:

// one line:
const mockRpc = "https://eth-mainnet.g.alchemy.com/v2/kQ7zR2vXw8mN4bC1dT6yH5uL0pEa9fJ3"; // crv-ignore
# .crvignore (gitignore-flavored: *, **, ?, !negation, trailing / = dir)
test/fixtures/
**/mock-secrets.ts
*.snap
!test/fixtures/keep-me.ts

The AI-driven remediation pipeline (MCP)

npx crv-leak-scan --mcp turns the scanner into a Model Context Protocol server. Point any MCP client at it:

{
  "mcpServers": {
    "crv-leak-scan": {
      "command": "npx",
      "args": ["-y", "crv-leak-scan", "--mcp"]
    }
  }
}

The connected agent gets five deterministic tools and one system prompt β€” the CRV OpSec Guardian β€” that instructs it to run the pipeline end to end, without human error:

Step

Tool

What it does

1 Β· Detect

crv_scan_staged / crv_scan_workspace

Regex + Shannon-entropy scan; returns {file, line, type, rawSecret, suggestedEnvName, inGitHistory} per leak

2 Β· Remediate

crv_auto_remediate

Three atomic actions: write the secret to .env, force .env into .gitignore, and refactor the source line to read the environment β€” process.env.X (TS/JS), os.getenv("X") (Python), std::env::var("X") (Rust), os.Getenv("X") (Go), ${X} (shell/YAML)

3 Β· Purge

crv_generate_purge_command

Builds the exact git filter-repo --replace-text plan; the expressions file is written inside .git/ so raw secrets never touch shell history or process lists

4 Β· Verify

crv_verify_clean

Re-audits workspace or staged diff β†’ {clean: true, status: "Ready to commit safely"}

β€”

crv_get_pipeline_instructions

Returns the full guardian instructions to the agent

The same guide ships as an MCP prompt (crv_pipeline_guide) and a resource (crv://pipeline-guide), and as server instructions on initialize β€” so the agent knows the playbook before its first tool call:

"You are the CRV OpSec Guardian… 1. First invoke crv_scan_staged. 2. If leaks are detected, DO NOT commit β€” call crv_auto_remediate to move secrets into .env and update .gitignore. 3. Purge history with crv_generate_purge_command when inGitHistory is set. 4. Call crv_verify_clean to confirm 100% security. 5. Report the fix."

Security boundary (read this)

Raw secrets flow only over the local stdio MCP channel β€” the agent needs them to relocate values into .env, and it could read the file anyway. Everything human-facing (terminal, --json, --sarif) stays masked, and remediation results never echo secret material back. .env is skipped by the worktree scan (it's the designated local store) but never by staged or history scans β€” committing a .env is precisely the leak this tool catches. And the prime rule the guardian enforces: moving a key to .env protects the repo, not the key β€” anything already pushed must be rotated.

CI/CD

GitHub Actions (scan everything, gate the merge)

name: leak-scan
on: [push, pull_request]
jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0        # full history β€” the whole point
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npx --yes crv-leak-scan@latest --full-history .

Exit code 1 fails the job when anything at/above --fail-on (default critical) is found.

Machine-readable output

npx crv-leak-scan --json  > leak-report.json   # pipe to Slack, Datadog, a bot…
npx crv-leak-scan --sarif > leak-report.sarif  # GitHub code scanning

--sarif emits SARIF 2.1.0 β€” upload it with github/codeql-action/upload-sarif and findings appear in the repository's Security tab with rule metadata and fingerprints.

Pre-commit (CI belt-and-suspenders)

      - run: npx --yes crv-leak-scan@latest --staged .

CLI reference

Usage: npx crv-leak-scan [path] [options]

Scan modes:   --staged | --depth <n> | --full-history | --no-history
Setup:        --install-hook | --uninstall-hook | --doctor | --mcp
Output:       --json | --sarif | --fail-on critical|high|medium | --quiet | --no-color
Other:        -v/--version Β· -h/--help

Exit codes:   0 clean Β· 1 leaks found Β· 2 usage/runtime error

Architecture

src/
β”œβ”€β”€ cli.ts        argument parsing, orchestration, exit codes, --mcp wiring
β”œβ”€β”€ scanner.ts    worktree walk Β· streaming git log -p parser Β· staged diff
β”œβ”€β”€ detectors.ts  17 regex detectors + BIP-39 scanner Β· masking Β· remediation
β”œβ”€β”€ entropy.ts    Shannon entropy
β”œβ”€β”€ wordlist.ts   embedded BIP-39 English wordlist (2048, verified)
β”œβ”€β”€ ignore.ts     .crvignore patterns + inline crv-ignore suppression
β”œβ”€β”€ hook.ts       pre-commit install/uninstall (core.hooksPath aware)
β”œβ”€β”€ remediate.ts  .env write Β· .gitignore guard Β· per-language env accessor
β”œβ”€β”€ purge.ts      git-filter-repo plan + safe expressions file
β”œβ”€β”€ mcp.ts        MCP server: 5 tools + guardian prompt (stdio transport)
β”œβ”€β”€ report.ts     terminal / JSON / SARIF renderers
β”œβ”€β”€ selftest.ts   per-detector canaries + placeholder battery
└── doctor.ts     --doctor diagnostics

The history pass streams git log -p line by line β€” flat memory on repos with gigabytes of history. Binary files are NUL-sniffed and skipped. The BIP-39 wordlist is structurally verified at build time (2048 words, unique, sorted β€” npm run wordlist regenerates it from the canonical source).

npm install && npm test    # 134 tests: detectors, boundaries, git history, hook, CLI, MCP e2e

Honest limitations

  • A scanner finds leaks; it cannot un-leak. Anything pushed to a public remote must be treated as compromised the moment it left your machine β€” rotate first, purge second.

  • Default mode reads the last 100 commits. That's a speed default, not a security guarantee β€” run --full-history in CI.

  • Merge-commit-only introductions, quoted exotic git paths, and base64-body secrets without recognizable shapes are known blind spots.


Built by CRV OpSec. Scan early, rotate ruthlessly, purge properly.

MIT β€” see LICENSE.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables AI agents to scan projects for leaked secrets and manage security incidents using GitGuardian's comprehensive API. It supports automated secret detection, honeytoken creation, and remediation workflows to secure codebases without context switching.
    40
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Local-first security check for AI coding agents β€” finds hardcoded secrets, exposed .env files, git-history leaks and vulnerable dependencies (OSV), entirely on your machine. Ask your agent "is this safe to ship?" and get a Launch Readiness score with a fix for every finding.
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI agents to self-govern by scanning code for hardcoded secrets, structural violations, and AI drift in real-time, providing fix packets for automatic remediation.
    28
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables AI coding agents to securely inject API keys and secrets into environment files, configs, or commands without the agent ever seeing the secret values.
    8 npm
    MIT