.env files are gitignored
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 (GitGuardian's 2024 State of Secrets Sprawl report recorded 23.8 million new secrets leaked on GitHub in 2023 alone). 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:
env-files-gitignored - Severity:
critical - What to look for: Read
.gitignore(or.git/info/exclude). Enumerate entries matching env file patterns. Then check whether any actual.env/.env.local/.env.productionfile is tracked in the working tree. - Pass criteria:
.gitignorecovers at least.envand.env.local(.env*or separate lines), AND no committed.env*file exists (only.env.example/.env.samplemay be committed as templates). - Fail criteria:
.gitignoremissing env-file patterns, OR a real.env(not.env.example) is committed..env.examplegitignored alone does NOT count if the real.envis not also covered. - Skip (N/A) when: Project is not JS/TS/Python/Ruby/PHP (Go, Rust, Java use different env conventions). Quote the framework detected.
- Report even on pass:
".gitignorecovers .env, .env.local, .env.production"— list matched patterns. - Detail on fail:
".gitignorehas no .env* entry"or".env.localis committed and not in .gitignore". - Cross-reference: For rotation playbooks, vault integration, and committed-secret detection, run
security-hardening. - Remediation: Add to
.gitignore:.env,.env.local,.env.development.local,.env.test.local,.env.production.local. If.envis already committed:git rm --cached .env+ rotate any exposed secrets.
Taxons
History
- 2026-04-22·v1.0.0·Initial import from project-snapshot via Phase 8.1 bundling·by phase-8-1-bundle-project-snapshot
- 2026-04-22·v2.0.0·Phase 9 consequence-first restructure — moved to new section slug, added news/incident references, severity reviewed.·by phase-9-stack-scan-v3
- 2026-04-25·v2.0.1·v3.1.0 pre-ship trim — prose compression for under-80K MCP cap; merged overlapping Fail-criteria / Do-NOT-pass-when sections; compressed enumeration prose; one remediation example per pattern. No semantic change; anti-sycophancy guards preserved.·by phase-9-1-stack-scan-v3-1