DEV Community

ke jia
ke jia

Posted on

Wire Dotguard Into GitHub Actions: A Full-Repo Secret Scan in One YAML Block

Here is the entire integration, before the walkthrough: one step in your workflow file, a run line that calls npx @wuchunjie/dotguard, and a build that fails when a secret is found. That is the whole design, and the rest of this article is the details that make it real. How to choose the workflow trigger — the pull request event is the right one, because the secret should never reach the default branch, and a push trigger on the main branch is a second net for the direct pushes that slip through. How to read the JSON report when the scan finds something, file by file, line by line, so the fix is a two-minute job instead of a forensic exercise.

How to handle the first false positive without disabling the check — the answer is a documented exception, not a muted pipeline, because a muted pipeline is a scanner that is no longer a scanner. And the part everyone skips: running the scan once, before the integration, on the current state of the repository, so the first CI run is not a list of historical findings you did not cause. The setup takes twenty minutes, which is the longest part of the whole project. The protection is permanent, which is the shortest part. The article is the twenty minutes, step by step, with the exact YAML, the exact report, and the exact exceptions file for when the first false positive arrives, which it will.

I have written a lot of dotguard content, but this is the one that answers the question people actually ask: how do you actually use it, step by step, from zero? No theory, no marketing. Just the commands, the output, and the decisions you will face along the way. If you have been meaning to try @wuchunjie/dotguard and kept putting it off because the documentation felt like a commitment, this is the version that fits in one sitting. Scan .env files, config files, and source code for exposed secrets. Zero dependencies, JSON reports, 1000+ files in seconds. — and everything below is what that actually looks like in practice, with the exact outputs you will see and the exact moments where the workflow forks.

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.

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.

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.

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.

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 JSON Report That CI Can Actually Use

A security tool is only as good as its integration surface, and the scanner's is a JSON report. Run it with the report flag and you get a machine-readable list of every finding: file, line, rule, severity. Exit codes are meaningful, so a pipeline can fail the build on any finding without parsing colored terminal output. That design decision pays off in the boring way: the tool fits into CI or a local pre-push script with zero glue code. When the report is data instead of text, other tools can consume it — a dashboard, a chat alert, a compliance export. Security tooling that cannot be integrated is a demo, not infrastructure. The JSON report is the part of the design that turns a one-person tool into a team habit, and it costs nothing to use: the same command, one extra flag, and the output becomes something the rest of the system can act on.

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.

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.

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 Team-Wide Hook: Distributing the Habit

Personal hooks die with the person who set them up. The pattern that actually works is distribution: put the scan in a hook that lives in the repository itself, committed to version control, so every clone gets the behavior automatically. A new developer clones the repository, runs their first command, and the scanner is already there. Nobody has to be told, nobody has to remember, and the behavior cannot be forgotten because it is part of the repository. This is the difference between a tool and a practice: a tool is what one person runs, a practice is what the repository does to everyone. The scanner is small enough that the distributed version costs nothing to maintain — one file, one command, zero configuration — which is exactly the size at which a team habit becomes cheaper than the individual habit it replaces. The repository becomes the enforcer, and enforcement by repository is the only enforcement that survives turnover.

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.

The takeaway

That is the whole workflow. npx @wuchunjie/dotguard gets you started, and the repository at https://github.com/wuchunjie00/dotguard has the full reference when you need it. If this saved you an hour, a coffee at ko-fi.com/wuchunjie keeps the tools free. And if you want the rest of the toolkit — dotguard for secret scanning, gitpulse for repository analytics, snippetx for snippets — the same npx pattern works for all of them. One command each, zero dependencies each, and a terminal that finally does the mechanical part of the job. The tutorial ends here; the habit starts now.

Top comments (0)