CLI tool to detect leaked secrets and API keys in codebases and public GitHub repos, including git history and staged changes
Project description
leakscan
A fast, lightweight CLI tool that finds leaked API keys and secrets in your code, git history, and public GitHub profiles. Pure Python, zero config, installs in seconds.
Why leakscan?
Most secret scanners are either bloated (Docker required, YAML hell) or miss the things that matter (git history, staged changes, live verification). leakscan is different:
- One command install:
pip install leakscan. No Docker, no config files, no setup. - 107 secret patterns covering every major provider (AWS, GCP, GitHub, Stripe, OpenAI, Supabase, Vercel, Datadog, MongoDB, PostgreSQL, Kubernetes, and 90+ more).
- Git history scanning: Finds secrets that were committed and then deleted. Most scanners miss this entirely.
- Staged scanning: The
--stagedflag scans only what you're about to commit. Pre-commit hooks run in milliseconds, not minutes. - Live verification: Makes safe, read-only API calls to check whether a leaked key is still active, across 30+ services.
- Know your blast radius: Every finding explains what an attacker could do with it, how serious that is, and exactly where to rotate it.
- Git blame and secret age: See who committed a secret, when, on which branch, and how likely it is to have already been scraped by bots.
- Fast on big repos: Multi-threaded scanning with a live progress bar, chunked file reading that keeps memory flat, and a benchmark summary at the end.
- .leakscanignore: A gitignore-style ignore file with negation support, plus
leakscan ignoreandleakscan inithelper commands. - History cleanup helper:
leakscan purgegenerates thegit filter-repo/ BFG Repo-Cleaner commands to scrub a secret from history, for you to review and run. - Slack / Discord alerts:
--notify slack,--notify discord, or--notify bothposts a scan summary straight to your team's channel. - Custom rules: Drop a
.leakscan.yamlin your repo and define your own patterns.
Quick comparison
| Feature | leakscan | gitleaks | trufflehog |
|---|---|---|---|
| Install | pip / brew / uvx / curl | Binary download | Binary / Docker |
| Config required | No | Yes (TOML) | No |
| Git history | Yes | Yes | Yes |
| Staged-only scan | Yes | No | No |
| Live verification | Yes (30+ services) | No | Limited |
| Custom patterns (YAML) | Yes | Yes | No |
| GitHub profile scan | Yes | No | Yes |
| Gist scanning | Yes | No | No |
| SARIF output | Yes | Yes | Yes |
| Pure Python | Yes | Go | Go |
| Pre-commit hook | Built-in | Manual | Manual |
| Multi-threaded scanning | Yes | Yes | Yes |
| Blast-radius remediation guidance | Yes | No | No |
| Git blame / secret age | Yes | No | No |
| History cleanup command generator | Yes (purge) |
No | No |
| Slack / Discord alerts | Yes | No | No |
Installation
Homebrew (macOS / Linux)
brew tap Vasishta03/leakscan
brew install leakscan
pipx (isolated, recommended for CLI tools)
pipx install leakscan
uvx / uv
uvx leakscan --help # run without installing
uv tool install leakscan # permanent install
pip
pip install leakscan
curl one-liner
curl -fsSL https://raw.githubusercontent.com/Vasishta03/secret-scanner/main/install.sh | sh
From source
git clone https://github.com/Vasishta03/secret-scanner
cd secret-scanner
pip install -e .
Usage
Scan a local project
leakscan scan ./myproject
leakscan scan . --severity HIGH
Understanding a finding
By default, every finding is printed as a full block: what was found, what an attacker could do with it, how serious that is, where to rotate it, and (inside a git repo) who committed it and how long it has been exposed.
[CRITICAL] AWS Access Key ID
File: config.py:3
Match: AKIAIOSFODNN7EXAMPLE
If leaked: Combined with its secret key, an attacker can call any AWS API the
underlying IAM identity is permitted to use, including launching compute,
reading S3 buckets, and modifying IAM policies.
Blast radius: Full cloud account compromise
Rotate at: https://console.aws.amazon.com/iam/home#/security_credentials
Git blame: dev@example.com - commit a1b2c3d4e5f6 (45d ago, branch main)
"add aws credentials"
Secret age: 45d - scraping risk: CRITICAL
This secret has been in git history for over 30 days. HIGH probability it has
already been scraped by automated bots that continuously crawl public
repositories for credentials.
Use --brief for the old compact one-line-per-finding table, and --no-blame
to skip the git blame and secret-age lookups (faster on large repos):
leakscan scan . --brief
leakscan scan . --no-blame
Scan only staged changes (pre-commit)
leakscan scan . --staged
This is what the built-in pre-commit hook uses. It only checks the diff you're about to commit, so it finishes instantly even on large repos.
Scan git history
Deleted a secret and pushed? It's still in your history. Find it:
leakscan scan . --history
leakscan scan . --history --depth 500 --since 2024-01-01
leakscan scan . --history --branch main
Verify leaked secrets are still live
leakscan scan . --verify
Makes safe read-only API calls to check if detected tokens are active. Supports 30+ services, including GitHub, GitLab, Stripe, OpenAI, Anthropic, HuggingFace, SendGrid, Slack, npm, Replicate, Telegram, Google API, Sentry, Vercel, Cloudflare, Supabase, Notion, Linear, Mailgun, Postmark, Railway, Cohere, Groq, Pinecone, Datadog, Twilio, and Azure AD.
Scan a GitHub repo by URL
leakscan scan https://github.com/owner/repo
leakscan scan https://github.com/owner/repo --history --verify
Scan a GitHub user's entire profile
leakscan scan --github username
leakscan scan --github username --include-gists
leakscan scan --github username --history --token $GITHUB_TOKEN
Output formats
leakscan scan . --format json --output results.json
leakscan scan . --format csv --output findings.csv
leakscan scan . --format sarif --output results.sarif
leakscan scan --github user --format disclosure --output report.md
Redact secrets in output
leakscan scan . --redact
leakscan scan . --format json --redact --output safe-results.json
Baseline mode (CI-friendly)
Save current findings as known, then only alert on new ones:
leakscan scan . --save-baseline .secrets.baseline
leakscan scan . --baseline .secrets.baseline
Performance on large repos
Scanning is multi-threaded and streams files in fixed-size chunks, so memory usage stays flat even on repos with very large files:
leakscan scan . --threads 16
leakscan scan . --chunk-size 2000000
leakscan scan . --max-file-size 100
A live progress bar tracks files as they're scanned, and a benchmark summary is printed at the end:
Scanned 4213 file(s) in 1.84s (2289.7 files/sec)
--threads defaults to your CPU count, --chunk-size defaults to 1,000,000
bytes, and --max-file-size defaults to 50 MB (larger files are skipped with
a warning instead of being read into memory). Binary files are detected and
skipped automatically. Use --no-progress to disable the progress bar, for
example in CI logs.
Custom configuration
Create .leakscan.yaml in your project root:
custom_patterns:
- name: "Internal Service Token"
regex: "intk_[a-zA-Z0-9]{32}"
severity: HIGH
description: "Internal microservice auth token"
- name: "Company OAuth Secret"
regex: "myco_secret_[a-zA-Z0-9]{40}"
severity: CRITICAL
description: "OAuth client secret for internal apps"
exclude_paths:
- "vendor/**"
- "*.min.js"
- "testdata/**"
entropy_threshold: 4.0
severity: HIGH
Or use [tool.leakscan] in your existing pyproject.toml:
[tool.leakscan]
severity = "HIGH"
entropy_threshold = 4.0
exclude_paths = ["vendor/**", "docs/**"]
Pre-commit hook
leakscan install-hook
Installs a git pre-commit hook that runs leakscan scan . --staged --severity HIGH --brief --no-blame. Blocks commits containing secrets. Uses your .secrets.baseline automatically if present.
To suppress a specific line, add any of these comments:
# nosec# gitleaks:allow# secretscanner:allow
.leakscanignore
Create .leakscanignore in your project root to skip paths, using
gitignore-style syntax. The legacy .secretignore filename is still read for
backward compatibility.
tests/fixtures/**
vendor/**
*.example
docs/
node_modules/**
# Negation: re-include a path an earlier pattern ignored
!important.env
Supports full ** glob syntax, trailing-slash directory patterns (docs/
matches docs/ at any depth), and !pattern negation.
leakscan init # create .leakscanignore with sensible defaults
leakscan ignore "*.test.js" # append a pattern to .leakscanignore
leakscan scan . --show-ignored # list files skipped because of .leakscanignore
Cleaning up git history
If a scan finds a secret that was committed in the past, leakscan purge
generates (but does not run) the commands to remove it with git filter-repo
or BFG Repo-Cleaner:
leakscan purge --file config/secrets.yaml
leakscan purge --commit a1b2c3d4
leakscan purge --all --method filter-repo
--file removes a file from every commit, --commit shows what a commit
changed so you can target it, and --all walks you through redacting every
leaked value found by leakscan scan . --format json. Every report ends with
a force-push warning (use --force-with-lease, never plain --force) and a
reminder that rewriting history does not undo a leak, so rotate the credential
too.
Notifications (Slack / Discord)
Send a summary of a scan to Slack and/or Discord:
leakscan scan . --notify slack --slack-webhook https://hooks.slack.com/services/...
leakscan scan . --notify discord --discord-webhook https://discord.com/api/webhooks/...
leakscan scan . --notify both
leakscan scan . --notify slack --notify-clean
--notify accepts slack, discord, or both. To avoid passing webhook
URLs every time, save them once in ~/.leakscanrc:
SLACK_WEBHOOK_URL="https://hooks.slack.com/services/..."
DISCORD_WEBHOOK_URL="https://discord.com/api/webhooks/..."
By default, a notification is only sent when secrets are found. Pass
--notify-clean to also post a message when a scan comes back clean.
GitHub Actions
- name: Install leakscan
run: pip install leakscan
- name: Scan for secrets
run: leakscan scan . --severity HIGH --no-entropy --format sarif --output results.sarif
- name: Upload SARIF to GitHub Security
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarif
Detected secret types
107 patterns in total, each with its own blast-radius explanation and rotation link.
CRITICAL
Private keys (RSA, EC, PGP, OpenSSH, PKCS#8, DSA), AWS access keys and secret keys, GCP service accounts, Azure storage connection strings and Azure AD client secrets, Age encryption keys, MongoDB/PostgreSQL/MySQL connection strings, Kubernetes service account tokens, Firebase Admin SDK keys, WireGuard private keys
HIGH
GitHub tokens (PAT, OAuth, App, Refresh), GitLab tokens, Stripe live keys, OpenAI keys, Anthropic keys, HuggingFace tokens, Telegram bot tokens, Discord bot tokens and webhooks, Slack tokens and webhooks, SendGrid, Mailgun, npm tokens, PyPI tokens, Shopify tokens, DigitalOcean tokens, Dropbox tokens, Notion keys, Linear keys, Terraform Cloud tokens, Vault tokens, New Relic keys, Mapbox tokens, Square tokens, Twitter bearer tokens, Mailchimp keys, Supabase keys, Vercel tokens, Cloudflare keys and API tokens, Datadog keys, PlanetScale tokens, Postman keys, Grafana tokens, Sentry tokens, Doppler tokens, Infisical tokens, Flutterwave keys, Coinbase tokens, Twitch secrets, Replicate tokens, Redis connection strings, Docker config auth, Expo access tokens, Fly.io API tokens, PagerDuty API keys, Elastic Cloud API keys, Cohere keys, Groq keys, Pinecone keys, Atlassian API tokens, PayPal client secrets, Razorpay key IDs, Postmark server tokens, Railway API tokens
MEDIUM
Generic API keys, generic secrets, hardcoded passwords, Bearer tokens, JWT tokens, database URLs with credentials, basic auth in URLs, Stripe test keys, Firebase server keys, Google API keys, Slack app tokens, private key file paths, Sentry DSNs, PEM certificates
LOW
High-entropy strings (Shannon entropy detection for values in .env, YAML, config files)
How it works
scanner/
cli.py Click-based CLI with 30+ options and ignore/init/purge commands
engine.py Multi-threaded, chunked file scanner; git history parser; staged diff scanner
patterns.py 107 regex patterns with severity classification
config.py .leakscan.yaml and pyproject.toml config loader
entropy.py Shannon entropy scorer for quoted and unquoted values
verifier.py Live API verification for 30+ services
remediation.py Per-pattern blast-radius, consequence, and rotation-link data
blame.py Git blame lookups, secret age, and scraping-risk classification
baseline.py Fingerprint-based baseline save/load/compare
reporter.py Terminal (block and brief), JSON, CSV, SARIF 2.1.0, disclosure report output
ignorefile.py .leakscanignore parser (gitignore syntax, ** globs, negation)
purge.py git filter-repo / BFG command generator for history cleanup
notify.py Slack and Discord webhook notifications, ~/.leakscanrc storage
github/
fetcher.py GitHub API client: repos, gists, commit diffs, rate limit handling
Contributing
git clone https://github.com/Vasishta03/secret-scanner
cd secret-scanner
pip install -e ".[dev]"
pytest tests/ -v
To add a new pattern: edit scanner/patterns.py, add a corresponding test in tests/test_scanner.py.
To add a new verifier: edit scanner/verifier.py, add the extractor regex and verification logic.
Changelog
See CHANGELOG.md for release notes.
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file leakscan-0.4.0.tar.gz.
File metadata
- Download URL: leakscan-0.4.0.tar.gz
- Upload date:
- Size: 54.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
76206ce0808257d89730479b6129763d51a53ae48d3de69c278991a7d6208af9
|
|
| MD5 |
79c59830606d08a1fad2549f323a46b5
|
|
| BLAKE2b-256 |
dfde6c79511297dcdbb94d55a0d47b6a2ade3f8a77df0f4237499e4a7f2bd512
|
Provenance
The following attestation bundles were made for leakscan-0.4.0.tar.gz:
Publisher:
publish.yml on Vasishta03/secret-scanner
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
leakscan-0.4.0.tar.gz -
Subject digest:
76206ce0808257d89730479b6129763d51a53ae48d3de69c278991a7d6208af9 - Sigstore transparency entry: 1785548539
- Sigstore integration time:
-
Permalink:
Vasishta03/secret-scanner@d9f0b076e0ccaa5700c8f4bb68a7ce95cdce932d -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/Vasishta03
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d9f0b076e0ccaa5700c8f4bb68a7ce95cdce932d -
Trigger Event:
push
-
Statement type:
File details
Details for the file leakscan-0.4.0-py3-none-any.whl.
File metadata
- Download URL: leakscan-0.4.0-py3-none-any.whl
- Upload date:
- Size: 52.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3fd2ca7ebf92e26fcdf19ba2e7b03a2a7622146858b52d308aab3c9b6cae347c
|
|
| MD5 |
f10e2e332f6a9d8439dbd2567cde4054
|
|
| BLAKE2b-256 |
ef780801173edaec4c215d34b0f9ce46e78b3ef03e507f1bed9c80336d8aa59e
|
Provenance
The following attestation bundles were made for leakscan-0.4.0-py3-none-any.whl:
Publisher:
publish.yml on Vasishta03/secret-scanner
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
leakscan-0.4.0-py3-none-any.whl -
Subject digest:
3fd2ca7ebf92e26fcdf19ba2e7b03a2a7622146858b52d308aab3c9b6cae347c - Sigstore transparency entry: 1785548614
- Sigstore integration time:
-
Permalink:
Vasishta03/secret-scanner@d9f0b076e0ccaa5700c8f4bb68a7ce95cdce932d -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/Vasishta03
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d9f0b076e0ccaa5700c8f4bb68a7ce95cdce932d -
Trigger Event:
push
-
Statement type: