I built a local secret scanner
I keep finding API keys in repositories that were supposed to be private. Sometimes the key is in the current code. More often it is in a commit from months ago, deleted later, and still reachable through git log. So I b
I keep finding API keys in repositories that were supposed to be private. Sometimes the key is in the current code. More often it is in a commit from months ago, deleted later, and still reachable through git log. So I built SecretSentinel: a small Python CLI that scans your working tree, your staged changes and your git history for hard-coded credentials.
Repository: github
Package: pypi
Install and run
pip install secretsentinel
secretsentinel scan # working tree (tracked and untracked, respecting .gitignore)
secretsentinel scan --staged # only lines you are about to commit
secretsentinel scan --history # every added line in the commit history
The exit code is 1 when something is found and 0 otherwise, so a CI job fails on a leak. Reports can be a console table, JSON, or SARIF 2.1.0 for GitHub code scanning:
secretsentinel scan --format sarif --output results.sarif
Why history matters
A secret deleted in the latest commit is not gone.
--history
checks every added line across all refs, and reports the commit where the secret appeared:
secrets.py:1:8 (commit 3f9a1c2b4d5e): [critical] aws-access-key-id: AKIA...VB (20 chars)
Rotating the key is still the real fix. The scan tells you where to look.
Design choices
- Local only. The scanner makes no network calls. It never checks whether a secret is still valid.
- No full secrets in output. Reports show a short preview and the length. JSON and SARIF hold a SHA-256 digest instead of the value.
- Rules are YAML. Each rule has an ID, a regex, a severity, a CWE, and an optional entropy threshold. The regexβs first capture group is the candidate secret, so entropy is measured on the secret and not on the text around it.
- Allowlist by digest. An accepted finding can be excluded by its SHA-256 digest, so the allowlist file never contains the secret.
- Ignore file. .secretsentinelignore skips paths, such as test fixtures.
A pre-commit hook is included, so the scan can block a commit before the secret reaches the repository:
repos:
- repo: local
hooks:
- id: secretsentinel
name: SecretSentinel
entry: secretsentinel scan --staged
language: system
pass_filenames: false
types: [text]
What it does not do yet
This is v0.1, and it is pre-alpha. Be aware of these limits:
- Detection is regex and entropy based. Expect false positives, and tune the rules and the allowlist for your project.
- Git history is loaded in memory. On very large repositories, use --max-commits.
- It does not validate keys against the provider. That is intentional, to keep the tool offline, but it means you still have to judge which findings are live.
- The built-in rules cover common providers: AWS, GitHub, Google, Slack, Stripe, private keys, and generic password and API key assignments. Your own patterns go in a rules file with --rules-file.
Other tools
Gitleaks (MIT, Go) and TruffleHog (AGPL-3.0, with verification of live secrets) are mature and widely used. SecretSentinel is a smaller Python tool. Its goal is to be easy to read and extend, with a simple YAML rule format and a small codebase. If you already use Gitleaks, keep using it.
Contributing
The project is Apache-2.0 licensed. Issues and pull requests are welcome. The CONTRIBUTING.md explains the layout and how to add a rule. To report a vulnerability in SecretSentinel, use the private reporting option described in SECURITY.md, not a public issue.
If you try it on a real repository and it finds something, or misses something, open an issue. That feedback is what the next version needs.
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes β full credit and traffic to the original publisher.