Skip to content

[Security/Supply chain] Keep developer-local .env secrets out of Git and Docker contexts #213

Description

@seonghobae

Buyer / repository-security gap

Protected develop@622e5e6c3d534f230c390f10e3832efadfc01825 tells operators to override local Compose database/service credentials through environment configuration, but its repository boundaries historically allowed developer-local .env files to enter two different evidence/supply-chain paths:

  1. root .gitignore did not ignore .env or .env.*, so a credential-bearing local file could be staged into Git;
  2. root .dockerignore did not ignore those files while the production Dockerfile uses COPY . . in its build stage, so an untracked local environment file could still be transferred into the Docker build context/cache even when it never reached the final runtime image.

Status: active_pr via Draft PR #214, branch security/ignore-local-env-files-622e5e6.

RCA

  • Immediate cause: neither root ignore boundary had local environment-file rules.
  • Technical root cause: secret values were externalized from application configuration, but the repository/build-context boundaries did not protect the common local files used to materialize those values.
  • Git control failure: secret scanning is downstream detection after content enters version-control evidence; there was no deterministic pre-commit contract making ordinary .env files untrackable by default.
  • Docker control failure: .gitignore is not the Docker build-context authority; COPY . . requires an independent .dockerignore boundary. Keeping a file out of Git alone does not keep it out of a local or remote Docker build transfer/cache.
  • Risk: database, connector, identity-provider or deployment credentials can enter commits, PR/CI history, forks, Docker build contexts/caches, remote builders or support artifacts through ordinary developer workflow.

Bounded remediation

PR #214 requires the same explicit policy at both boundaries:

.env
.env.*
!.env.example

The negative rule permits a separately reviewed non-secret .env.example template to remain source-controlled/build-visible if one is intentionally added later. This PR does not add such a template, secret value, credential generator, Compose-password change, or secret-scanner suppression.

This is defense in depth, not a replacement for GitHub secret scanning, least-privilege credentials, deployment secret stores, Docker/CI secret mounts, key rotation, or the separate Compose default-password issue #195.

Test-first evidence

RED 1 — Git tracking boundary

Fail-first source 541a7fd875b2bbe1d37cc708481bcb59fc6f5398 added only RepositorySecretFilePolicyTest. CI 31359234145, macOS job 93364640620, checked synthetic merge 21ebba7a3f04eeb413af67affe8615c53828e873 and ran 276 ETL tests with exactly one failure and zero errors/skips: root .gitignore did not ignore .env.

GREEN b6a673515da775505ab85bcc24c75956abaf9e80 changed only root .gitignore; the test then passed on the hosted matrix.

RED 2 — Docker build-context boundary

After the Git boundary was green, test-only source f73d4c460634a125ce0f087f81f56a56b4c01a46 extended the same production-policy contract before .dockerignore changed. CI 31359631408, macOS job 93365758840, checked synthetic merge 397d406f81ba30e2c6d2f34f9cba199490ccffe8 and ran 277 ETL tests with exactly one failure, zero errors/skips:

RepositorySecretFilePolicyTest.localEnvironmentFilesAreExcludedFromDockerBuildContextroot .dockerignore must ignore .env.

This was the intended Docker build-context RED rather than setup/import/fixture failure. The Dockerfile's COPY . . makes .dockerignore the relevant boundary.

GREEN 2 — exact current candidate

Exact current PR #214 source is 3e37e937cc45d6bf40e9c6b39cf5c1de93aebe6c. The smallest correction adds the three environment-file rules only to root .dockerignore. Exact comparison to RED 2 is one fast-forward commit, ahead 1 / behind 0, merge base exactly f73d4c..., and the only changed file is .dockerignore with three additions.

All exposed workflow aggregates associated with exact current source are terminal-success:

  • CI 31359696114;
  • Dependency Review 31359696100;
  • CycloneDX SBOM 31359696115;
  • SAST Semgrep 31359696080;
  • Security Scan 31359696123.

CI macOS job 93365948358 checked synthetic merge 70d1f3087c0a19da4820a88c28179b47a9ba2101, ran RepositorySecretFilePolicyTest 2/2 green, ETL 277/277 green, CDC 106/106 green, gateway 3/3 green, and completed the Maven reactor successfully. The run is merge-preview compatibility evidence rather than accepted literal-source proof.

The same current run still reproduces the inherited JaCoCo zero-class false green and protected-base diagnostic gaps already owned by #162/#164/#205 and #170#176/#210#212. #121/central controls own accepted literal-source execution, #196 owns vulnerability-graph completeness, and independent formal review remains separate.

Acceptance

  1. ordinary .env and .env.* files are non-trackable by default while !.env.example remains explicitly reviewable — GREEN;
  2. ordinary .env and .env.* files are excluded from Docker build transfer while the explicit non-secret example exception is preserved — GREEN;
  3. both contracts are platform-independent from Maven module working directory — green on macOS/Ubuntu/Windows;
  4. no secret values or environment file are added by the PR — current bounded diff is ignore-policy + test only;
  5. exact-current aggregate CI/dependency/SBOM/SAST/security has regenerated successfully, without reclassifying synthetic-source, scanner-completeness, coverage, or review evidence;
  6. canonical Security/Test Strategy/Traceability can index the Git/build-context secret boundary during the next safe docs: establish canonical commercial architecture baseline #149/[Documentation] Close canonical architecture coverage and live traceability gaps #159 reconciliation.

Do not close this issue until protected develop actually contains both policies and protected acceptance is regenerated.

Relationship: #195 local database credential/exposure policy; #165 release/provenance; #159 canonical documentation; central secret scanning remains a separate control; #168/#169 own the unrelated tracked Zipkin executable/supply-chain lane.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions