A scanner that only prints colored text is a demo. A scanner whose output is data is infrastructure. The JSON report is the interface between the tool and everything else in your system, and reading it well is a ten-minute skill with a permanent payoff. Each finding is a small object: the file it came from, the line number, the rule or pattern that matched, and a severity marker. The file and line make verification a five-second job; open the spot, look at the value, decide in one breath whether it is a real credential or a known fixture. The rule tells you what kind of thing it is, which matters when you are deciding how urgently to rotate.
When to care is the second half of reading the report. Any finding in a file that ships, that is checked in, or that a build reads is a live concern: verify, rotate, re-scan. Findings in local-only files are a hygiene concern. Findings you have already confirmed as test fixtures become your known-false-positive list, which is itself useful data, because it maps where the codebase looks sensitive without being sensitive. The report also feeds machines, which is the deeper point. Exit codes let a pipeline fail on any finding, and the JSON format means a dashboard, a chat alert, or a compliance export can consume the same scan without parsing terminal output. The report is not a transcript of the scan. It is the scan, in a form the rest of the system can act on.
This is the hands-on section, and it is written as the run goes, which means the commands are in the order they are typed and the output is the output that came back, including the parts that look like errors and are not. dotguard is Scan .env files, config files, and source code for exposed secrets. Zero dependencies, JSON reports, 1000+ files in seconds. The install line is npx @wuchunjie/dotguard, and the repository is https://github.com/wuchunjie00/dotguard if you want to read the source before you run it, because the reading is the option, not the requirement, and the requirement is the run. Each step below is short enough that a copy-paste session can follow it without losing the thread, and the thread is the part that the long tutorial loses, and the loses is what sends the reader to a different tab.
The Cost of a Leaked Key, Quantified
The numbers on public cloud incidents are uncomfortable in a specific way: they are all larger than the annual budget of the team that leaked the key. Compromised cloud credentials have produced bills in the hundreds of thousands of dollars in a single weekend. Leaked repository tokens have been used to push malicious code into downstream packages that millions of installs then inherit. Leaked database passwords have emptied tables into the clear. The economics are one-directional: the cost of a scan is a line of workflow file and a few seconds of CPU; the cost of a miss is measured in six figures and a postmortem you will give in the morning with your team in the room. The asymmetry is the entire argument for running the scanner by default, on every push, in every repository, whether or not you think you have secrets in it. The repositories that are sure they have no secrets are the ones that have never checked, and the check is the only difference between those two statements.
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.
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.
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.
Rotation Beats Detection, Every Time
There is a comfortable misconception that finding a secret is the win. It is not. The win is the key that no longer works. A detected secret that stays valid is a secret that is still leaking; the only difference is that now you know about it, which is a strange kind of liability. The workflow that matters is: scan, confirm, rotate, and then scan again to confirm the rotation happened and the old value is gone from the working tree or accepted as a known dead value. Rotation is boring, it touches other teams, and it is the step everyone skips. A detection tool that makes rotation feel like a natural next step — by giving you the exact file and line, so the fix is a two-minute job — is doing more than pattern matching. The finding is a ticket, and the ticket's definition of done is the rotated key, not the closed alert. Designing the tool around that definition of done is what separates a scanner from a security workflow.
One Line in GitHub Actions
The entire CI integration is one step: a run line that invokes the scanner via npx. No service container, no token to configure, no daemon. Every push gets a full scan, and the build fails if a secret is found, which means the secret never reaches the default branch. The beauty of the one-line integration is the maintenance cost: there is nothing to update, no version to pin, and no vendor to renew. When a security control costs one line of workflow file, the only question is why it is not already there. That is the bar every pre-merge security control should meet, and the scanner was designed to meet it on purpose. The one line is also the onboarding story: new contributors see the check in the workflow file, understand what it is doing, and never have to be told to run it. The pipeline is the policy, and the policy is one line long.
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.
Secrets Hide in Unlikely Places
The environment file is where people look, and it is where secrets are least well hidden. The real distribution is wider: a database URL inside a container compose file, an API key in a YAML config under a deploy directory, a webhook token in a JSON file checked in for one test, a token pasted into a README as a working example. The scanner covers source code, not just dotfiles, precisely because the pattern of where secrets actually live is messier than the pattern of where people think they live. If your security review only opens environment files, you are reviewing one out of five places. The scanner's job is to make the review exhaustive by making it automatic, and the exhaustive part is the whole value. The surprising findings — the ones in files nobody would call secret files — are the ones that justify the tool, because they are the ones no checklist would ever reach. A checklist asks about secrets; the scanner asks about every file, which is a different and better question.
False Positives Are the Real Cost of a Scanner
Every secret scanner has to make a tradeoff: miss a real key, or flag a false one. The scanner leans toward flagging, and the price is occasional false positives — an encoded blob that looks like a key, a test fixture with a fake credential, a documentation example that uses a real-looking prefix. That is why the output includes the file, the line, and the matched rule: the cost of verifying a finding should be five seconds, not a forensic exercise. A scanner with zero false positives that also misses real keys is a liability, not a tool. Budget a minute per finding, verify, rotate if it is real, and note the known test values so the team stops re-checking them. The loop is the product. Over time the false-positive list becomes its own artifact — a record of the places in the codebase that look like secrets and are not, which is useful information in its own right, because it maps the codebase's sensitive-looking surfaces.
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.
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.
The takeaway
The run is the proof, and the proof is the part the tutorial is. dotguard gives you Scan .env files, config files, and source code for exposed secrets. Zero dependencies, JSON reports, 1000+ files in seconds. and the giving is one command: npx @wuchunjie/dotguard. The repository, https://github.com/wuchunjie00/dotguard, is where the source lives and the issues go, and the goes is the part that the stuck reader uses, because the stuck is the part the tutorial cannot see from here. The path above is the one that was run and the run was clean, and the clean is the part that the next run inherits, and the inherits is what the tutorial buys for the reader who follows it to the end.
Top comments (0)