Against .env.example
(or .env.sample, .env.template, .env.dist, <file>.example, or --example): missing variables, undocumented extras.
GitHub Packages · MIT · Node ≥ 18
Validate .env files against .env.example or a schema, catch weak and default secrets, and keep .env files out of git, including its history.
1 Point the @mrlouiswright scope at GitHub Packages in .npmrc
@mrlouiswright:registry=https://npm.pkg.github.com2 Install
npm i -D @mrlouiswright/dotenv-sentinelPublished to GitHub Packages, not npmjs.com. GitHub Packages needs a GitHub token with read:packages to install, even for public packages: add //npm.pkg.github.com/:_authToken=${GITHUB_TOKEN} to .npmrc or run npm login --registry=https://npm.pkg.github.com.
$ npx dotenv-sentinel appdotenv-sentinel — 3 target(s) app/.env.example variables: 6 gitTracked: true gitIgnored: false CRITICAL app/.env.example:6 AWS_ACCESS_KEY_ID looks like a real AWS access key (AKI…LE (20 chars)) in an example file [example-contains-secret] ↳ AWS_ACCESS_KEY_ID ↳ fix: Rotate it and replace it with a placeholder. LOW app/.env.example:5 JWT_SECRET has an unquoted value with spaces; some loaders cut it off [unquoted-space] ↳ JWT_SECRET ↳ fix: Wrap the value in double quotes. ... 2 critical, 5 high, 5 medium, 9 low✖ Failing (fail-on: high).
dotenv-sentinel finds every .env, “.env.*” and “*.env” file in a project and checks three things: completeness (variables missing compared with .env.example or a JSON schema, empty required values, wrong types), secret hygiene (changeme, “<your-key>”, short secrets, debug mode in production files, real credentials left in example files) and git exposure (env files that are tracked, not covered by .gitignore, or present anywhere in history). Values are never printed: every secret is masked. CLI, library and GitHub Action. Node.js 18+, zero runtime dependencies (uses the git CLI when available).
(or .env.sample, .env.template, .env.dist, <file>.example, or --example): missing variables, undocumented extras.
(--schema env.schema.json): required, empty, type (string, number, integer, boolean, url, email, port, json, enum), pattern, enum, minLength, secret.
variables named like secrets (*_SECRET, *_TOKEN, *_PASSWORD, API_KEY, APP_KEY…) holding well-known defaults or placeholders, or shorter than --min-secret-length (16). Example files holding real-looking credentials (AWS, GitHub, Stripe, Slack, private keys, passwords in URLs, high-entropy values).
(*prod*): DEBUG=true, NODE_ENV=development, http:// URLs to remote services.
invalid lines, unterminated quotes, duplicate keys, unquoted values with spaces, ${VAR} references to undefined variables.
tracked env files with values (committed-env), env files not matched by .gitignore, and env files added in any commit of any branch (env-in-history; rotate those secrets).
# current directory, including git checks
npx dotenv-sentinel# Example
npx dotenv-sentinel apps/api --schema apps/api/env.schema.json# Example
npx dotenv-sentinel .env.production --example .env.example --fail-on mediumpermissions:
contents: read
security-events: write # only for the SARIF upload
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with: { persist-credentials: false }
- uses: MrLouisWright/dotenv-sentinel@v1
with:
targets: '.'
fail-on: high
sarif: dotenv-sentinel.sarif
- uses: github/codeql-action/upload-sarif@v3
if: always()
with: { sarif_file: dotenv-sentinel.sarif }
| Flag | Description |
|---|---|
--example <file> | Compare every env file with this example |
--schema <file> | JSON schema: { "variables": { "PORT": { "type": "port" }, ... } } |
--min-secret-length <n> | Minimum length for secret variables (default 16) |
--no-git | Do not ask git about tracked/ignored files |
--no-history | Do not search git history for committed .env files |
| Policy and output | |
-c, --config <file> | JSON config (default .dotenv-sentinel.json if present) |
-f, --format <fmt> | text (default) | json | sarif | markdown |
--json/--sarif/--markdown <file> | Also write that format to a file |
--fail-on <sev> | critical | high | medium | low | info | none (default high) |
--list-rules | Print rules and default severities |
--no-color | Disable colours |
Full reference, config file format and API in the README.
Support this project
I build free, open-source tools that help developers ship safer websites. Every coffee helps keep them maintained, documented and free for everyone.
Buy me a coffee (opens Stripe in a new tab)Secure checkout by Stripe