Finds secrets in your code. Then actually helps you fix them.
Every other scanner prints a list and walks away. secox stays β shows you the revocation steps, rewrites the file, checks git history, and if the damage is already in old commits, hands you the exact command to fix that too.
# one-liner
curl -fsL ewry.net/secox/install.sh | sh
# homebrew
brew tap immanuwell/secox https://github.com/immanuwell/homebrew-secox.git
brew install immanuwell/secox/secox
# cargo
cargo install secoxsecox init # drop a pre-commit hook, doneQuick start:
secox init # install pre-commit hook (and to delete - secox init --uninstall)
secox scan # scan current directory
secox scan --staged # scan only staged files
Usage: secox <COMMAND>
Commands:
init Install the secox pre-commit hook
scan Scan for secrets in files or git history
resolve Interactively triage findings: rotate real secrets, allow false positives
rules List all built-in detection rules
baseline Manage the .secox-baseline.json file for legacy repos
help Print this message or the help of the given subcommand(s)
Options:
-h, --help
Print help (see a summary with '-h')
-V, --version
Print version
Examples:
secox init install pre-commit hook for this repo
secox init --global install once for all repos (core.hooksPath)
secox scan scan the current directory
secox scan --staged scan only what is staged right now
secox scan --git-history audit the full commit history
secox scan --include-low widen the net (more noise, fewer misses)
secox scan --format json | jq . pipe findings to jq
secox scan --ignore "*.snap" skip snapshot files
secox scan --ignore vendor/ skip vendored dependencies
secox resolve triage blocked-commit findings interactively
secox rules list all built-in detection rules
Suppress a single finding inline:
api_key = "sk-live-..." # secox:allow
Suppress all findings in a file β add to the top:
# secox:allow-file
Run this:
secox resolveFor each finding you pick r, a, or s:
r β rotate. Shows the revocation URL + step-by-step for that provider (AWS, GitHub, Stripe, OpenAI, Anthropic, Slack, GitLab, and 15 more). Asks if you want the hardcoded value swapped for a proper env var reference in-file (os.environ["KEY"] for Python, process.env.KEY for JS/TS, os.Getenv("KEY") for Go, etc.). Then runs git log -S against your history β if the secret is already in old commits, you get the exact git filter-repo command to paste.
a β allow. False positive. Injects # secox:allow on that line. Never bothers you again.
s β skip. Deal with it later.
secox flags sensitive filenames the moment they hit the staging area β no regex needed, no content to scan:
.env,.env.local,.env.production,.env.*id_rsa,id_ed25519,*.pem,*.p12,*.jkscredentials.json,serviceAccountKey.json,terraform.tfvars,.netrc,.htpasswd
Committing id_rsa is almost never intentional. Now it's blocked at git commit instead of discovered six months later in a security audit.
The first scan on a legacy codebase usually explodes with hundreds of stale findings. That's why people disable hooks. secox has a baseline:
secox baseline # snapshot everything that exists right now
git add .secox-baseline.json && git commitFrom that point on, secox scan only shows new secrets β the ones you're about to add. Work through the old ones at your own pace with secox resolve, then refresh:
secox baseline --update # after rotating a batchCommit the baseline file and the whole team shares the same suppression list.
Three layers before anything fires:
1. Context-aware β os.getenv("SECRET") is a lookup. <YOUR_KEY_HERE> is a placeholder. Values in test fixtures get downgraded. All skipped.
2. Semantic checks β entropy, character diversity, and a bigram filter that catches English prose sneaking through. password = "These Are Just Words" doesn't fire.
3. Provider-level validation β GitHub tokens have a CRC-32 checksum in the last 6 chars; secox validates it (fabricated tokens β Medium). AWS key suffixes get entropy-checked (AKIAIOSFODNN7EXAMPLE from the AWS docs is too repetitive, skipped). JWTs get their base64url header decoded β no alg field, not a JWT.
43 rules. Provider-specific ones fire only on their exact prefix+format. Generic ones (password, api_key, secret assignments) go through all three layers first.
One line:
api_key = "sk-live-..." # secox:allowWhole file (add to the top):
# secox:allow-file
Whole directories β commit a .secoxignore (gitignore syntax):
vendor/
*.snap
tests/fixtures/
Add --verify and secox will ping each provider's API to check if the secret is still live:
secox scan --verify
secox scan --staged --verify
secox scan --git-history --verifyEach finding gets a status line:
Status: β verified active
Status: β invalid / rotated
Providers with live verification:
| Provider | Covered rules |
|---|---|
| GitHub | PAT (classic + fine-grained), OAuth token, App token |
| OpenAI | API key, project key |
| Anthropic | API key |
| HuggingFace | Access token |
| GitLab | Personal access token |
| Slack | Bot, user, and app tokens |
| Stripe | Live secret key, restricted key |
| SendGrid | API key |
| Mailchimp | API key |
| DigitalOcean | Personal access token |
| Linear | API key |
| Doppler | Service token |
Providers not yet verified (AWS, Twilio, Databricks, Azure) require HMAC signing or multi-credential correlation β plain HTTP check isn't enough.
secox scan --git-history # audit every past commit (slow, worth it once)
secox scan --format sarif # GitHub Code Scanning
secox scan --format json # pipe to jq / ship to SIEM
secox scan --no-fail # advisory mode, exits 0
secox init --global # one hook for every repo on the machinesecox <command> --help has examples for everything.

