Skip to main content

.env files are gitignored

ab-002565 · project-snapshot.secrets.env-files-gitignored
Severity: criticalactive

Why it matters

A committed .env file leaks every credential it contains to anyone with repo read access — and on public GitHub, to automated secret-scanners that crawl new commits within seconds. Database URLs, Stripe live keys, OAuth client secrets, and third-party API tokens routinely end up in these files. AI coding tools default to creating .env alongside .env.example when scaffolding, and will happily add the .env as a tracked file if the project doesn't already carry a gitignore entry covering it. The majority of "my secrets leaked" incidents on GitHub start exactly here: a single accidental commit, often by a contributor who didn't realize the file wasn't ignored. Once it lands in git history, removing it requires a history rewrite AND rotating every credential the file contained.

Severity rationale

Critical because a committed `.env` hands over production credentials wholesale — one leaked file typically means rotating every service the project connects to, and public-repo exposure is scraped by attackers in minutes, not days.

Remediation

Add to .gitignore:

.env
.env.local
.env.development.local
.env.test.local
.env.production.local

If a .env file is already committed, remove it from git history with git rm --cached .env and rotate any secrets it contained.

Deeper remediation guidance and cross-reference coverage for this check lives in the security-hardening Pro audit — run that after applying this fix for a more exhaustive pass on the same topic.

Detection

  • ID: project-snapshot.secrets.env-files-gitignored
  • Severity: critical
  • What to look for: Read .gitignore (or .git/info/exclude if present). Enumerate every entry that matches an env file pattern. Then check whether any actual .env / .env.local / .env.production file exists in the working tree as a tracked file.
  • Pass criteria: .gitignore includes a pattern that covers at least .env and .env.local (e.g., .env* or two separate lines), AND no committed .env* file exists in the working tree (only .env.example or .env.sample may exist as committed templates).
  • Fail criteria: Either .gitignore is missing env-file patterns, or a real .env (not .env.example) is committed.
  • Skip (N/A) when: Project is not a JavaScript/TypeScript/Python/Ruby/PHP project (e.g., Go projects use different env conventions). Quote the framework you detected when skipping.
  • Do NOT pass when: .env.example is the only env file gitignored and the real .env is not in .gitignore. The protective entry must cover the actual env file, not just templates.
  • Report even on pass: ".gitignore covers .env, .env.local, .env.production" — list which patterns matched.
  • Detail on fail: ".gitignoredoes not list .env files (no.env*or.env entry found)" or ".env.local is committed to the repo and not in .gitignore".
  • Cross-reference: For full secrets-management coverage (rotation playbooks, vault integration, committed-secret detection), run the security-hardening audit.
  • Remediation: Add to .gitignore:
    .env
    .env.local
    .env.development.local
    .env.test.local
    .env.production.local
    
    If a .env file is already committed, remove it from git history with git rm --cached .env and rotate any secrets it contained.

Taxons

History