Skip to content

didrod205/envlint

🔐 envlint

Lint your .env files and catch leaked secrets — locally, before you commit.

npm version CI node license

A deterministic CLI that lints your .env files: syncs them against .env.example, validates syntax & hygiene, enforces an optional schema, and detects hardcoded secrets (AWS, Stripe, GitHub, OpenAI, private keys and more) — with a score, A–F grade and JSON/Markdown reports. Runs 100% locally; your secrets never leave your machine.


One-line summary

envlint compares your .env to its example, flags missing/extra/duplicate keys and value problems, and scans for real secrets accidentally committed — all offline, no API key, no server.

Why this project exists

.env files are where teams quietly hurt themselves:

  • A teammate adds STRIPE_SECRET_KEY but forgets .env.example, so everyone else's local build breaks and prod is missing a variable at runtime.
  • Someone pastes a real AWS or Stripe key into .env and it gets committed — now it's in git history, and rotating it is a fire drill.
  • Values drift: a PORT that isn't a number, a URL with an unquoted space that silently truncates, a placeholder changeme shipped to staging.

These are mechanical, high-stakes problems that belong in a pre-commit hook or CI gate, not in a code review someone skims. And because .env is sensitive, you can't just paste it into an LLM. envlint is the deterministic, local check.

Key features

  • 🔄 Sync against .env.example — missing keys, extra/undocumented keys.
  • 🔐 Secret detection — provider patterns (AWS, Stripe, GitHub, Google, OpenAI, Anthropic, Slack, Twilio, SendGrid, npm, JWTs, private keys) plus a Shannon-entropy fallback for opaque tokens. Placeholders are ignored.
  • 🧹 Syntax & hygiene — duplicate keys, invalid names, empty values, unquoted values with spaces, trailing whitespace, lowercase keys, placeholders.
  • 📐 Optional schematype (string/number/boolean/url/email/port), enum, pattern per key.
  • 📊 Score + A–F grade, per file and overall; secrets weigh heaviest.
  • 📄 JSON & Markdown export, colored console output, CI gate exit codes.
  • ⚙️ Config file, per-rule severities, ignore lists.
  • 🔒 Zero network. Secrets are masked in output and never transmitted.

Install

# run without installing
npx @didrod2539/envlint scan .env

# or install
npm install -g @didrod2539/envlint    # global CLI (provides `envlint`)
npm install -D @didrod2539/envlint    # project dev-dependency (for CI / hooks)

Node ≥ 18. ESM + CJS + TypeScript types.

Quick start

envlint scan .env
.env  0/100 (F)  10 keys vs .env.example
  ✗ Missing key "SESSION_TIMEOUT" (present in .env.example)
  ⚠ Key "EXTRA_DEBUG" is not in .env.example:21
  ✗ Duplicate key "ENABLE_SIGNUP" (lines 19, 20):20
  ⚠ Unquoted value with spaces for "DATABASE_URL" (line 8):8
  ✗ Possible JSON Web Token in "JWT_SECRET" (line 11):11
  ✗ Possible Stripe Secret Key in "STRIPE_SECRET_KEY" (line 14):14
  ✗ Possible AWS Access Key ID in "AWS_ACCESS_KEY_ID" (line 15):15

Overall  0/100 (F)  1 file(s), 10 key(s),  3 secret(s) , 8 error(s), 3 warning(s), 2 info

(envlint auto-finds .env.example / .env.sample / .env.template next to your file.)

CLI usage

envlint scan [...targets]     # lint .env files or directories
envlint report <input.json>   # re-render a saved JSON report as Markdown
envlint init                  # scaffold envlint.config.json (with a schema)
envlint --help
envlint --version

scan options:

Option Description
--config <file> Path to a config file (otherwise auto-detected)
--example <file> Example/schema file to compare against
--no-secrets Disable secret scanning
--json <file> Write a JSON report
--md <file> Write a Markdown report
--min-score <n> Exit non-zero if the overall score < n (CI gate)
--quiet Hide info-level issues in the console

Pointed at a directory, scan finds .env, .env.local, .env.production, etc. (skipping *.example/*.sample/*.template).

Example result

Full reports for the bundled sample files are in examples/sample-report.md and examples/sample-report.json.

📸 Screenshot / demo GIF placeholder: ./docs/screenshot.png — record the terminal running npx @didrod2539/envlint scan examples/.env.local.

Configuration

Create envlint.config.json (or run envlint init):

{
  "example": ".env.example",
  "scanSecrets": true,
  "entropyThreshold": 4.0,
  "entropyMinLength": 20,
  "minScore": 90,
  "allowEmpty": ["OPTIONAL_FLAG"],
  "ignoreKeys": ["LEGACY_*"],
  "disableRules": [],
  "ruleSeverity": { "lowercase-key": "warning" },
  "schema": {
    "PORT": { "type": "port" },
    "NODE_ENV": { "enum": ["development", "production", "test"] },
    "DATABASE_URL": { "type": "url" }
  }
}
Field Meaning
example Example/schema file (auto-detected if null)
scanSecrets Enable provider/entropy secret scanning
entropyThreshold / entropyMinLength Tune the generic high-entropy check
minScore CI gate threshold (overridable with --min-score)
allowEmpty Keys allowed to have empty values
ignoreKeys Keys to skip — exact, or trailing-* prefix wildcard
disableRules Rule ids to turn off
ruleSeverity Override severity per rule id
schema Optional per-key type / enum / pattern constraints

Rule ids: missing-keys, extra-keys, duplicate-key, invalid-key, empty-value, placeholder-value, unquoted-spaces, trailing-whitespace, lowercase-key, secret-detected, schema-*.

Real-world use cases

  1. Block secret leaks in a pre-commit hook. Add envlint scan .env --min-score 100 (or wire it into [husky]/lint-staged). A real AWS/Stripe key in .env fails the commit before it ever reaches git history.
  2. Keep .env.example honest in CI. Run envlint scan .env.ci --example .env.example. A PR that adds an env var without documenting it in the example fails the build, so onboarding never breaks.
  3. Audit a config you inherited. envlint scan . --md audit.md profiles every .env* file in a repo and produces a shareable report of what's missing, malformed, or dangerously hardcoded.

Programmatic API

import { analyze, buildReport, toMarkdown } from "@didrod2539/envlint";

const file = analyze({ source: ".env", content, reference: { source: ".env.example", content: ex } });
console.log(file.score, file.secrets, file.issues);

const report = buildReport([file], { version: "0.1.0" });
await fs.writeFile("report.md", toMarkdown(report));

Roadmap

  • A bundled pre-commit hook / husky recipe and a GitHub Action.
  • --fix to sync missing keys into .env and quote unquoted values.
  • More provider secret patterns (Azure, GCP service accounts, DigitalOcean…).
  • .env.vault / multi-environment diffing (.env vs .env.production).
  • Allowlist file for known-safe values (e.g. test fixtures).
  • Baseline mode to ignore pre-existing findings and only fail on new ones.

FAQ

Does envlint send my .env anywhere? No. It runs entirely on your machine — no API key, no telemetry, no uploads, no network calls. Detected secrets are masked in all output.

How does secret detection work? A curated set of provider regexes (AWS, Stripe, GitHub, OpenAI, etc.) plus a Shannon-entropy fallback for long, random, opaque tokens. Obvious placeholders (your-key-here, changeme, <token>) are deliberately ignored. See src/secrets.ts.

Won't the entropy check have false positives? It's conservative (length + character-class + entropy threshold, all tunable via config), and you can --no-secrets or ignoreKeys anything. Prefer a missed edge case over noise.

Is it safe to keep example secrets in the repo? Yes — that's the point of .env.example: it should contain only placeholders. envlint flags when a real-looking secret appears so the example stays clean.

Does it work with my framework? Any project using dotenv-style files: Node, Next.js, Vite, Rails, Django, Docker --env-file, etc. It parses the common .env grammar (quotes, export, comments).

Is the score official? No — it's a transparent metric (severity-weighted penalties, with a heavy extra penalty per detected secret). Use it to track and gate. See src/score.ts.

Contributing

Contributions welcome! Each check is a small, self-contained rule in src/rules/, and secret patterns are a declarative table in src/secrets.ts. See CONTRIBUTING.md and the Code of Conduct.

git clone https://github.com/didrod205/envlint.git
cd envlint
npm install
npm test
npm run build
node dist/cli.js scan examples/.env.local --example examples/.env.example

License

MIT © envlint contributors

💖 Sponsor

envlint is free, MIT-licensed, and built in spare time. If it stopped a secret from hitting your git history, please consider supporting it:

Where your support goes: more secret patterns, a pre-commit hook + GitHub Action, a --fix mode, multi-environment diffing, and fast issue responses.

About

Deterministic CLI that lints .env files: syncs against .env.example, validates syntax & schema, and detects hardcoded secrets (AWS, Stripe, GitHub, OpenAI, private keys). JSON/Markdown reports, runs locally — secrets never leave your machine.

Topics

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors