The two tools at the top of the secret scanning world make different bets about where verification happens. TruffleHog, the data team's favorite, verifies candidates against the issuing services: it finds a string that looks like a credential and then asks the credential's own API whether the credential is real, which is the strongest possible confirmation and the one that requires a network and the right permissions. dotguard makes the other bet: it matches the patterns locally, with no calls out, which means it works in the air-gapped build, the restricted CI runner, and the compliance environment where a security tool that phones home is exactly the thing the security review will not approve.
The comparison is a shop type question more than a tool question. The team that lives in the cloud, with the permissions and the network and the CI that can reach the credential APIs, gets more precision from the verifier, because the false positive is checked against the source of truth before it becomes a ticket. The Node shop with the restricted runners and the private registries and the compliance review gets more coverage from the local matcher, because the scan runs everywhere the code runs, and the findings are candidates that a human confirms in five seconds, which is the division of labor the tool was designed around. The section below is the same set of repositories through both approaches: the findings each one makes, the verification step in each, the false positive handling, and the verdict for each shop type, because the honest answer is that the tools are optimized for different rooms, and the room you scan in is the deciding factor.
A comparison is a set of questions with two answers each, and the questions below are the ones the choice actually turns on, not the ones the marketing pages argue. The two sides are dotguard, Scan .env files, config files, and source code for exposed secrets. Zero dependencies, JSON reports, 1000+ files in seconds. installed with npx @wuchunjie/dotguard, and the other option, which gets the same treatment and the same chance to win a section. The setup is identical where the setup can be identical, and the identical is the part that makes the difference the difference instead of the configuration. The verdict comes at the end of each section, because the section-level verdict is the one the reader can check against their own context, and the context is the part the global verdict does not know.
Scanning the Worktree, and the History Behind It
The scanner covers what is on disk: the environment files, the config files, the source in your working tree. That catches the obvious case — the file you just created and are about to commit. For the subtler case, the secret that is already in history, you combine it with the version control system: find the files that ever contained the pattern, then scan them. A committed secret does not stop being a secret when it is deleted from the current branch; it lives in every clone, every fork, and every mirror. The honest workflow is two steps: scan the present automatically, and audit the past with the same rules applied to the files that history touched. Detection is a habit, and habits are easier to keep when the tooling is small enough to run by reflex. The present scan is the reflex; the past audit is the quarterly deep clean, and both use the same rules, which is what makes the pair coherent instead of two unrelated chores.
The Exit Code Is the Integration
The scanner's interface to the machine is the exit code, and the exit code is the part that makes it CI-ready without any glue. A clean scan exits zero, a scan with findings exits nonzero, and the nonzero is what the pipeline turns into a failure, and the failure is what blocks the push. The pre-push hook uses the same code: the hook runs the scan, and the push goes through only if the exit code is zero. The JSON report is the second interface, for the things that consume data instead of pass/fail: the dashboard that charts findings over time, the chat alert that posts the file and the line, the compliance export that needs the record. The two interfaces cover the two consumers, the pipeline and the person, and the coverage is the design. A tool that needs a wrapper script to be useful in CI is a tool that will not be in CI, because the wrapper is the step that does not get written, and the not-written is the check that does not run.
Two Rhythms: The Daily Path and the Quarterly Tree
The scanner runs on two cadences, and the two cadences are the practice. The daily one is the targeted scan: the path pointed at the directory that changed, the service that touched the config, the deploy folder that holds the real credentials. The targeted scan is fast enough to run before the push, and the fast is what makes it the reflex. The quarterly one is the recursive sweep: the flag pointed at the whole projects directory, the tree that includes the forgotten experiment and the demo repository and the fork that nobody maintains. The sweep is the deep clean, and the deep clean is what catches the leak that is already old, because the old leak is the one the targeted scan never points at, and the never-pointed-at is where the leak waits. The same command, the same rules, two scopes, two rhythms. The monorepo does not need a different tool for the two cadences, it needs the same tool aimed at the two scopes, and the aiming is the practice, and the practice is what the scanner's flags exist to make cheap.
What Zero Dependencies Buys You in a Security Tool
A security tool has a special trust problem: you are asking it to read your most sensitive files. The natural question is what the tool itself trusts. For this one, the answer is nothing. No dependencies, no network calls, no telemetry, no update daemon. It reads files, matches patterns, and writes a report. That matters in the places where security tooling gets blocked: restricted CI runners, air-gapped builds, compliance environments that require an audit of every third-party package in the pipeline. A single-file scanner with zero dependencies is auditable in an afternoon by a security reviewer who would never approve a forty-package tree. In security, small is not a feature. Small is the product. The auditability is the trust model: you can read the whole thing, you can verify what it matches, and you can be confident that the thing reading your secrets is not also phoning home with them. That confidence is not a nice property of the design; it is the design.
The False Positive Conversation
Every scanner has false positives, and the honest relationship with them is the part that keeps the tool in the workflow. The fixtures are the main source: the test that asserts against a fake key, the README example that shows a token shape, the mock that returns a secret-looking string. The false positive is not a bug in the scanner, it is the scanner doing its job on data that looks like the job, and the cost of the false positive is the five-second check, which is the cost the tool is designed around. The known-false-positive list is the management: the findings you have already classified stay classified, and the classification is what the report's rule field supports, because the rule tells you the shape that fired, and the shape is what the fixture is. The conversation to have with the team is not about removing the false positives, it is about budgeting for them: the five seconds each, and the list that grows, and the list that makes the next triage faster. The scanner that has no false positives is the scanner that misses the custom key, and the custom key is the one that matters.
How the Scan Actually Works
The scanner looks for the patterns that real secrets actually take. Cloud provider keys start with a known prefix, Git tokens start with a known prefix, messaging platform tokens have a known shape, payment processor live keys have a known prefix, and web tokens start with a known base64 header. It also checks for high-entropy strings assigned to suspicious variable names — password, token, secret, key — because the values do not always follow the format, but variable names are a reliable signal. The scan covers environment files, config files, and source code, and it reports the file, the line, and the matched pattern so a human can verify in seconds rather than minutes. It is deliberately a detection tool, not a verdict tool: it finds candidates, a human confirms, and the key gets rotated either way. That division of labor is what keeps false positives from becoming noise fatigue.
Writing the Detection Rules Is a Window Into Developer Habits
The detection rule set is a fossil record of how developers actually handle secrets. The cloud provider rule catches the most leaks by volume, which says something about how much infrastructure runs on a single provider. The web-token rule catches a specific habit: developers pasting a full token into code to debug one endpoint, then forgetting it is there. The high-entropy rule exists because a surprising number of teams generate strong keys and store them with weak discipline — random value, obvious variable name, plaintext file. Building the rules taught me that secret leaks are not a knowledge problem; everyone knows the environment file should not be committed. They are a friction problem. The right thing is slow, the wrong thing is fast, and the scanner removes the friction from the right thing by making the check automatic. Every rule in the set is a documented instance of the friction, and the rule set is a map of where the friction lives in a codebase.
The 8-Month Leak That Started This
The scanner was built after an audit of my own public repositories found three leaked secrets. The worst was a production database password that had been committed, in plaintext, for eight months. Nothing dramatic happened — no breach, no incident report — which is exactly the problem. Most leaks are silent. They sit in a file that was committed in month one, get copied into forks, become the default credential in a demo, and nobody notices until the rotation happens by accident. A scanner is not a luxury for teams with a security budget. It is the same category of tool as a spellchecker: cheap, automatic, and the only thing standing between a careless commit and a very bad quarter. The eight-month leak is not a story about a mistake; it is a story about the absence of a check, and the check is the entire product.
What a Clean Scan Does for the Team
The clean scan is the output nobody celebrates, and the no-celebration is the point, because the clean scan is the state the team should be in by default. The value of the default-clean is the attention: the team's security attention goes to the findings, and the findings are the exceptions, and the exceptions are what get the rotate-today treatment instead of the quarter-review treatment. The clean scan also does the onboarding: the new developer who runs the scan and gets nothing learns the habit without the incident, and the habit learned without the incident is the habit that holds, because the first experience is the quiet one instead of the alarming one. The quiet is the design goal, and the quiet is what the pre-push hook and the CI gate buy, because the gate catches the leak before the team sees it, and the team that never sees the leak is the team that never has the incident meeting. The clean scan is the boring output, and the boring output is the expensive one to keep.
The Rules: What the Scanner Actually Matches
The scanner works on two rule families, and knowing both is what makes the report readable. The first family is the known-shape rule: the cloud provider key prefixes, the git token formats, the webhook secret shapes, the database URL patterns. These are the rules with names, and they fire on the credentials the ecosystem has already standardized. The second family is the entropy heuristic: a string with the statistical fingerprint of a generated secret, assigned to a variable whose name says what it is. The heuristic is the net for the custom key, the one that follows no documented format, and it is the family that needs the five-second human check, because the heuristic is a candidate, not a verdict. The report marks which family fired, and the mark is what separates the rotate-today finding from the probably-a-fixture one. The two families together cover the cases that matter, and the report tells you which one caught the finding, which is the part that makes the triage fast instead of anxious.
Monorepos, Home Directories, and the Recursive Flag
The scanner assumes your secrets live in one project, and then breaks that assumption on purpose. The path flag lets you point it at a subdirectory — scan just the deploy configs, or just one service in the tree. The recursive flag lets you do the opposite: point it at your projects directory and audit everything you have ever written, including the old side projects you forgot about. That last use is the one that pays for itself. Leaks do not respect project boundaries; the forgotten demo repository from a few years ago is a live credential the moment someone finds it. A scanner that can sweep an entire directory tree in seconds turns auditing all your code from a month-long project into a coffee break. The monorepo use is the inverse: one command covers every service, so the scan cost does not grow with the organization. Either direction, the same principle: the scan should be cheap enough to run often, and running it often is what catches the leak while it is still cheap to fix.
The takeaway
The comparison ends where the context begins, and the context is the part only the reader has. The sections gave the data: the same input, the two answers, the section-level verdicts. dotguard is Scan .env files, config files, and source code for exposed secrets. Zero dependencies, JSON reports, 1000+ files in seconds., available via npx @wuchunjie/dotguard, source at https://github.com/wuchunjie00/dotguard. The reader who is all in one ecosystem gets the ecosystem's tool, and the reader who spans ecosystems gets the tool that spans with them, and the two are the parts the data above supports. The choice is the reader's, and the reader's is the part the comparison respects, because the respects is what the ad does not.
Top comments (0)