Environment file under version control

ID

env_file_in_version_control

Severity

low

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Family

SCM

Tags

ASVS50:v13.3.1, non-reachable, security, source-code, spvs10-v1.5.2, supply-chain

Description

A .env file is the conventional place to keep the local configuration of an application: database URLs, API keys, OAuth client secrets, signing keys. The convention that makes it safe is that the file stays on the developer machine and is listed in .gitignore, while the repository carries only a redacted .env.example.

This detector reports .env and its per-environment variants (.env.production, .env.local, .env.docker, …) when they are under version control. Template files that are meant to be committed — .env.example, .env.sample, .env.template, .env.dist, .env.defaults — are never reported.

It is the repository-hygiene counterpart of the secret detectors: the finding is the tracked file, whether or not its current values look like secrets, because a tracked env file accumulates credentials over time. The values themselves are reported separately by the secrets scanner.

The detector needs a git working tree to establish whether a file is tracked, so it is skipped when the scanned directory is not under git (an extracted archive, or a checkout without git metadata).

Security

Anything committed in an env file is readable by everyone with repository access, including forks, mirrors, CI logs and any third-party tool granted read scope. Deleting the file later does not help: the values stay in the git history and remain retrievable from any clone, so every credential the file contained has to be treated as compromised and rotated.

The exposure grows quietly. A .env committed while it held only a local database password becomes the place where a production API key is added months later, by which point nobody rechecks whether the file is tracked.

Mitigation / Fix

Stop tracking the file, keep it ignored, and publish a redacted template instead:

# stop tracking, keep the local copy
git rm --cached .env

# ignore it from now on
echo '.env' >> .gitignore
echo '.env.*' >> .gitignore
echo '!.env.example' >> .gitignore

# commit a redacted template so the required variables stay documented
cp .env .env.example   # then blank every value
git add .gitignore .env.example

Rotate every credential the committed file contained, since the values remain in the history. If the file must be purged from history as well, rewrite it with git filter-repo (or the BFG) and force-push in coordination with everyone working on the repository, remembering that existing clones and forks keep the old objects.

Provision the real values at run time from the environment or a secrets manager, as required by ASVS V13.3.1.