DEV Community

ke jia
ke jia

Posted on

I Failed a Build Over One Line in .env. That's the Point.

A staging API key got into a public repo last month. Not in a config file, not in a log — in a .env that someone committed with a "wip" message and never cleaned up. The key sat in the history for eleven days. Eleven days of webhook calls from a machine I don't own.

The embarrassing part: our pipeline had a linter, a type checker, and a dependency audit. It had nothing that said "hey, this file contains a live secret."

The four-line fix

The cheapest security control I've added in years is a CI step that refuses to build when a .env file contains something that looks like a real credential.

I use dotguard for it. It's a zero-dependency Node script that scans every .env* file in the repo for hardcoded passwords, API keys, tokens, private keys, and database URLs. The whole scan is regex-based, runs locally, and takes under a second on a mid-size monorepo.

Here's what it found in a test project:

$ npx @wuchunjie/dotguard ./

  Scanning: .

  .env (5 issues)
    L  1 | Hardcoded password
       DB_PASSWORD=***
    L  2 | API key
       STRIPE_API_KEY=***
    L  4 | API key
       OPENAI_API_KEY=***
    L  3 | Access token
       GITHUB_TOKEN=***
    L  5 | Database URL
       DATABASE_URL=postgres://user:***@localhost:5432/app

  --------------------------------
  5 potential secrets exposed!
  Add .env to .gitignore & use .env.example instead.
Enter fullscreen mode Exit fullscreen mode

Note what it does not do: it doesn't ring any SaaS bell. No upload, no account, no telemetry. The scanner never sees your file — it reads it from your own disk. That matters more than it should in 2026, and I'll write a separate post about why I stopped trusting cloud-based secret scanners.

The CI gate

The tool exits 1 when it finds a potential secret. That single fact is the whole gate:

# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
  secret-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - name: Scan for exposed secrets
        run: npx @wuchunjie/dotguard .
Enter fullscreen mode Exit fullscreen mode

That's it. Four meaningful lines. If a PR introduces STRIPE_SECRET_KEY=sk_live_..., the build goes red before a human ever reviews the diff.

I deliberately keep it as a hard failure, not a warning. A warning becomes background noise inside a month. A red build gets fixed in the same sitting.

What it catches (and what it doesn't)

Worth being honest about the limits:

  • It scans .env* files only. A secret pasted into a README or a test fixture is out of scope — that's a different scanner's job.
  • It's pattern-based. It flags things that look like secrets by key name and value shape. A credential stored under FROB_CONFIG_BLOB won't trip it.
  • It doesn't touch git history. If the key was committed last week, dotguard on the current tree won't find it. For history you want a proper secret-rotation workflow and something like git log -S or a dedicated history scanner.

For the specific failure mode that hurt me — a fresh .env entering the repo in a PR — it does exactly one job and does it before merge.

The part nobody warns you about

The first week of a hard secret gate is uncomfortable. People commit .env.local because it's local. People paste "just for testing" credentials. The gate catches all of it, and every catch is a five-minute conversation that would have been a two-hour incident otherwise.

By the second week, cp .env.example .env became muscle memory on the team. That's the actual product. The regexes are the least of it.

Install it, wire it into CI today, and let the first red build be your proof of concept.

npx @wuchunjie/dotguard
Enter fullscreen mode Exit fullscreen mode

More Tools

Tool What it does Command
scaffoldx-cli 12 production-ready project templates in 3 seconds npx scaffoldx-cli
dotguard Scan .env files for exposed secrets npx @wuchunjie/dotguard
gitpulse Git repo analytics in your terminal npx @wuchunjie/gitpulse
snippetx Terminal code snippet manager npx @wuchunjie/snippetx

If these save you time, consider buying me a coffee. All tools are MIT-licensed, zero-dependency, and run fully offline.

Top comments (0)