devtools
comparison
06 Sept 2026
Six secret scanners against sixteen planted credentials, and what each one walked past
I hid sixteen fake credentials in a git repo across seven kinds of hiding place, ran six open-source scanners, and counted. All six missed the same one.
By Purvansh Parmar
Checked 2026-09-06
Read 8 min
Entries 6
Run gitleaks. Then keep reading, because on a repo I built specifically to be found, gitleaks walked straight past both passwords sitting inside connection URLs, and all six scanners walked past the one sitting in a CSV column. I planted 16 fabricated credentials across 7 hiding places, ran each tool on its stock settings, and then pointed the same six at pallets/flask, which has 5,556 commits and nothing worth stealing. The first number tells you what a scanner can find. The second tells you how much of your Friday it will cost.
How this was checkedI built a git repo holding 16 fabricated credentials across 7 hiding places, ran six scanners over it on stock settings, then ran the same six over pallets/flask at 5,556 commits to measure false positives and wall time. Gitleaks and Titus tied at 12 of 16. Gitleaks found neither of the two passwords embedded in connection URLs, Titus and Nosey Parker both ignore AKIA prefixes until you pass --ruleset all, and not one of the six found the secret sitting in a CSV column.
What I compared on
- 16 planted credentials across 7 hiding places: vendor-prefixed token, labelled value, unlabelled high-entropy value, password inside a connection URL, PEM block, value in a CSV column, and a file committed then deleted
- false positives and wall time over pallets/flask, 5,556 commits and 236 tracked files
- stock settings first, because stock settings are what a pre-commit hook runs
- licence, because one of these is AGPL and you may want to ship it
Regex and entropy scanner for git history and directories, one Go binary.
Free
MIT, all of it, no account and no tier. The upstream config in v8.30.1 carries 222 rule blocks. Note that gitleaks git and gitleaks dir are different scans: dir mode missed both credentials I committed and then deleted, git mode caught both.
Paid
No published price list and no hosted tier. Enterprise questions go to the maintainer by email, so budget for a conversation instead of a checkout page.
Wins
Top score on stock settings at 12 of 16, and the only tool that found the unlabelled 32-character value under a password key in a YAML file. It was also the only one to recover every planted secret from history rather than most of them.
Loses
Zero of two on passwords inside connection URLs. A line reading DATABASE_URL=postgresql://appsvc:<32 chars>@host:5432/app produced nothing, because the generic rule wants a keyword adjacent to the value and a DSN gives it none. It also returned 12 findings on flask, every one of them a generic-api-key hit on an example value in docs/config.rst.
12 of 16 planted222 rule blocks1.62s over 5,556 commits12 findings on flask, all docs examples
Detector-first scanner that can call the vendor to check whether a key is still live.
Free
AGPL-3.0 CLI, free, no account. The project advertises 800+ detectors, and live verification is in the free tier rather than behind the paywall.
Paid
TruffleHog Enterprise adds a dashboard, continuous monitoring, SSO and 20+ integrations. The price is not published anywhere, so it is a sales call.
Wins
Zero findings on flask. Not one false positive across 5,556 commits, because it reports only what a named detector claims. It caught both connection-URL passwords gitleaks missed, and trufflehog filesystem also reads .git/objects, so filesystem mode surfaces history secrets whether you asked for that or not.
Loses
Slowest here by a distance: 15.41s on flask, near ten times gitleaks. It carries no generic entropy rule, so my unlabelled YAML password and the Supabase service_role JWT both went unreported. AGPL-3.0 is a real constraint if you intend to vendor it into something closed.
10 of 16 planted0 false positives on flask15.41s over 5,556 commitsAGPL-3.0
Praetorian's replacement for Nosey Parker, same rule heritage, new engine and scoring.
Free
Apache-2.0, v1.2.9 dated 31 August 2026, a single 61 MB binary. It scans directories, git history, Docker images, and remote GitHub or GitLab URLs without cloning first.
Paid
None. Praetorian sells consulting, not this.
Wins
Best coverage once the rules are actually on: 13 of 16 with --ruleset all, including both connection-URL passwords and the JWT. It also groups matches into findings, which turned 198 raw matches on flask into 5 things a human has to look at.
Loses
The default profile quietly drops teh AWS access key ID rule. AKIAQYLPMN5HZ3XW7TQD in a plain text file yields nothing on defaults, and yields AWS API Key the moment you pass --ruleset all. A tool whose out-of-the-box configuration ignores the most recognisable credential prefix in the industry is one you have to configure before you can trust a green run.
12 of 16 on defaults, 13 with --ruleset all2.37s over 5,556 commits198 matches, 5 findings on flask61 MB binary
The scanner Titus replaces, read-only since its owner archived it in April 2026.
Free
Apache-2.0, v0.24.0, still downloadable and still works. Its default ruleset holds 162 rules, with 15 asset rules and 6 password-hash rules sitting beside it.
Paid
None, and none is coming. The repository was archived on 24 April 2026 and the README now points at Titus.
Wins
Fastest full scan I measured: 1.03s over flask, ahead of gitleaks. It deduplicates hard, collapsing 368 matches into 4 findings, so an existing CI wiring is not something you need to rip out this week.
Loses
It is retired. It also scored under its own successor, 10 of 16 against 12, missing the deleted OpenAI key that Titus recovered, and it carries the same default ruleset that skips AKIA prefixes. Adopting it in September 2026 means adopting a dead tool on purpose.
10 of 16 planted1.03s over 5,556 commits162 rules in the default rulesetarchived 24 April 2026
An ESLint-shaped linter for credentials, pointed at files rather than at git.
Free
MIT, v13.0.5, entirely free. The recommended preset loads 28 rules, and configuration is a .secretlintrc.json that reads like every other lint config already in the repo.
Paid
None, and no hosted tier exists. If you want this detection with a dashboard behind it, GitHub Secret Protection is $19 per active committer per month, counted over a rolling 90 days.
Wins
368ms, the fastest number on this page, and 0 findings on flask. It caught the Postgres DSN password that gitleaks missed, and its per-file message output drops into an existing lint step without anyone learning a new tool.
Loses
It cannot read git history at all. The two credentials I committed and then deleted are invisible to it, so a hook built on Secretlint will never tell you what is already sitting in the repo behind you. It read both the CSV and the YAML and reported nothing in either, and 7 of 16 is last place among the general-purpose entries here.
7 of 16 planted28 rules in the recommended preset368msno git history support
A shell script from AWS Labs that greps commits against patterns you register.
Free
Apache-2.0, and free in the sense that it is 360 lines of bash you can read in a sitting. It has never cut a release, and the newest commit in the repository is dated 17 September 2025.
Paid
None. There is no company behind it selling an upgrade, and no GitHub release has ever been cut, so what you clone is what you get.
Wins
It does one job and does it properly. git secrets --register-aws installs the AWS patterns, after which it found both halves of the AWS credential pair in the working tree and again in history, in 2.6s, with nothing false on flask. As a commit hook that blocks AWS keys it is still fine.
Loses
2 of 16. It ships with no patterns whatsoever until you register some, and AWS is the only bundled provider, so the GitHub token, the Stripe key, the Slack token, the private key and every connection string went through untouched. This is an AWS guard rail, not a secret scanner.
2 of 16 planted2.60s over 5,556 commits0 releases everAWS patterns only
Side by side
| Planted found (of 16) | flask findings | flask wall time | Licence |
| Gitleaks | 12 | 12, all docs examples | 1.62s | MIT |
| Titus | 12, or 13 with all rules | 5 | 2.37s | Apache-2.0 |
| TruffleHog | 10 | 0 | 15.41s | AGPL-3.0 |
| Nosey Parker | 10 | 4 | 1.03s | Apache-2.0, archived |
| Secretlint | 7 | 0 | 0.37s | MIT |
| git-secrets | 2 | 0 | 2.60s | Apache-2.0 |
Verdict
Put gitleaks in CI and run Titus on a schedule. Gitleaks wins the default case: top score on stock settings, 222 readable rules, and the only clean sweep of secrets buried in history. Then cover its blind spot, because it will not see a password inside a connection URL, and a .env full of DSNs is the most ordinary file in any repository. A weekly titus scan with --ruleset all closes that gap plus the JWT case for 2.37 seconds of compute. Skip Nosey Parker now that it is archived. Assume none of them will find the key in your CSV, and grep for that one yourself.
Sources