[E00-S04-T05] .env.example contains placeholders only #404
No Reviewers
Labels
Clear labels
agent/analyst-drafted
agent/analyst-drafted
needs/human-decision
needs/human-decision
needs/security-review
needs/security-review
tier/t0
tier/t1
tier/t2
tier/t3
kind
bug
kind
bug
kind
epic
kind
epic
kind
initiative
EPPP programme initiative
kind
story
kind
story
kind
task
EPPP engineering card/task decomposed from a story
kind
toil
kind
toil
loop
1
loop
1
loop
2
loop
2
loop
3
loop
3
risk
agent-full
risk
agent-full
risk
human-gated
risk
human-gated
risk
human-only
risk
human-only
size
l
size
l
size
m
size
m
size
s
size
s
status
blocked
status
blocked
status
done
Workflow: Done
status
in-progress
status
in-progress
status
proposed
status
proposed
status
ready
status
ready
status
review
status
review
stream
checkout
stream
checkout
stream
onboarding
stream
onboarding
stream
platform
stream
platform
trivial — implementer only, auto-merge
standard — implementer + reviewer + tester
complex — security if triggered, human merge
critical — full chain + security, human merge
No labels
Milestone
No items
No Milestone
Projects
Clear projects
No projects
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: Fabrika/PersonalBlog#404
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
What changed
.env.example(new, repo root) — the committed configuration template ([E00-S04-T05]): documents every configuration environment source read by the config package (HOST/PORT/DATABASE_URL/EPPP_SESSION_SECRET) plus the optional Docker Compose override variables (POSTGRES_DB/POSTGRES_USER/POSTGRES_PASSWORD/POSTGRES_PORT/APP_PORT), with placeholder values only — no real secret values..gitignore— real.env/.env.*files stay ignored; the committed.env.exampleis explicitly un-ignored (!.env.example) so the template stays tracked.tests/env-example.test.mjs(new, dependency-freenode:test): locks in every acceptance criterion with mutation probes proving the assertions are non-vacuous.env-examplejob (E00-S04-T05) gates the suite on every PR. ⚠️ Pipeline tripwire: any change to.gitea/workflows/requires a human maintainer decision/approval on this PR (security finding F1 — issue #186 Notes; no code change required beyond that sign-off).compose.yamlnow point at the committed template instead of deferring it to a later task.Security review findings (PR #404 round 1, request-changes) — addressed
gitleaks detect --source . --no-git --redact→ 0 hitsEPPP_SESSION_SECRETplaceholder 42 chars (fails open)change-me(9 chars < the schema's 32-char minimum) so an uneditedcp .env.example .envfails startup validation (fails closed)maskValue()helper; every message that echoes a template value masks/truncates it (never the raw string)Criterion → test mapping
.env.examplecontains placeholders onlyenv-example: "a committed .env.example exists at the repo root"; ".env.example contains placeholders only and no real secret values" — every assignment value must be a benign non-secret default (0.0.0.0, ports,localhost,eppp, the no-credential localDATABASE_URL) or carry an explicit placeholder marker (change-me/<…>); every line is a comment, a blank line, or a well-formedKEY=value; no duplicate variables; the file documents every config-schema environment source (HOST/PORT/DATABASE_URL/EPPP_SESSION_SECRET). Mutation probes: a long secret-looking value, a credential-URI value, a missing required variable, and a malformed line all failenv-example: the same value predicate rejects secret-shaped values, and the compose dev-default credential (postgres://eppp:eppp@db:5432/eppp) is rejected anywhere in the file — values or comments. Mutation probes: injecting the compose default credential orpostgres://alice:supersecret@…fails. Supporting: ".gitignore keeps real .env files ignored while un-ignoring the committed example" (+ mutation probe dropping!.env.examplefails)EPPP_SESSION_SECRETfails closed: its.env.exampleplaceholder is shorter than the schema's 32-character minimumenv-example: "EPPP_SESSION_SECRET's placeholder is shorter than the schema's 32-character minimum (fails closed)" — asserts the shipped placeholder length is < 32 chars so an unedited copy is rejected at startup. Mutation probe: replacing it with the 40-char runtime-built probe fails the placeholder-only criterion# gitleaks:allowenv-example: the secret-shaped probe (SECRET_SHAPED_PROBE) is built at runtime by joining short non-secret fragments — no secret-shaped literal in the source tree. Verified:gitleaks detect --source . --no-git --redact→ 0 hits on the branchenv-example:maskValue()truncates any template-derived value echoed in an assertion message (placeholder values, forbidden credential values, malformed lines); messages echo e.g.got: "aB3d...<40 chars>", never the raw stringRisks
node:test-only (no dependencies, lockfile untouched)..env.exampleis already excluded from the Docker build context by the existing**/.env.*dockerignore pattern (covered bysecrets-not-embedded), so the template cannot enter an image..gitea/workflows/change on this PR (issue #186 Notes, finding F1).