Environment variables are documented
Why it matters
An undocumented environment variable is a reference to something that may not exist on the deployment target. CWE-798 covers hardcoded credentials, but the inverse failure — a credential variable that is referenced but never defined — is equally damaging: on a fresh Vercel or Railway deploy, process.env.STRIPE_SECRET_KEY is undefined, and the Stripe client silently initializes with no key, causing every payment request to fail with a confusing API error rather than a startup crash. OWASP A05 (Security Misconfiguration) covers this class of deployment-time misconfiguration. The env-vars-documented check enforces reference-integrity between code and its runtime configuration contract.
Severity rationale
High because an undocumented env var causes silent `undefined` substitution at runtime, turning every dependent call into a misconfiguration failure that may bypass security checks or crash payment flows.
Remediation
Document every non-platform env var in .env.example and add a typed validation schema so missing values cause an explicit startup failure rather than a silent undefined.
# .env.example — committed to the repo, values are placeholders
STRIPE_SECRET_KEY=sk_test_xxxxxxxxxxxx
DATABASE_URL=postgresql://localhost:5432/myapp
OPENAI_API_KEY=sk-xxxxxxxxxxxxx
// src/env.ts — validated at startup; missing vars throw immediately
import { z } from 'zod'
export const env = z.object({
STRIPE_SECRET_KEY: z.string().startsWith('sk_'),
DATABASE_URL: z.string().url(),
OPENAI_API_KEY: z.string().min(1),
}).parse(process.env)
Import from src/env.ts everywhere instead of reading process.env directly. This eliminates undocumented vars at the schema level and surfaces missing configuration at startup rather than mid-request.
Detection
-
ID:
env-vars-documented -
Severity:
high -
What to look for: Walk source files for every reference to
process.env.X,process.env["X"],import.meta.env.X,Deno.env.get("X"),Bun.env.X. Also include destructured access:const { X } = process.env. Before evaluating, extract and quote the var nameXfrom each reference. Then check whetherXexists in any of:.env.example,.env.template,.env.sample, theenv/defineblock ofnext.config.*/vite.config.*, a typed env schema file (env.ts,env.mjs,src/env.ts,src/env/server.ts,src/env/client.ts, matching the t3-oss/env-nextjs pattern), OR this exact platform allowlist of standard runtime env vars:NODE_ENV,CI,PORT,HOST,HOSTNAME,PWD,HOME,USER,LOGNAME,SHELL,PATH,LANG,LC_ALL,TZ,TERM,TMPDIR,npm_lifecycle_event,npm_package_name,npm_package_version,npm_config_*,INIT_CWD,NEXT_RUNTIME,NEXT_PHASE,__NEXT_PROCESSED_ENV,VERCEL,VERCEL_ENV,VERCEL_URL,VERCEL_REGION,VERCEL_GIT_*,NETLIFY,NETLIFY_*,RENDER,RENDER_*,RAILWAY_*,FLY_APP_NAME,FLY_REGION,AWS_REGION,AWS_LAMBDA_*,AWS_EXECUTION_ENV. Count all env var references, total documented, total undocumented. -
Detector snippet (shell-capable tools only): If the tool has shell access, check whether a committed
.env.example/.env.template/.env.sampleexists. Exit 0 means at least one documentation file is present — proceed to prose to confirm each env var is actually documented. Exit 1 means NO documentation file exists — fail the check. Exit >=2 or command not found — fall back to prose reasoning.test -f .env.example -o -f .env.template -o -f .env.sample -
Pass criteria: 100% of
process.envreferences are documented in.env.example, in a typed env schema, in a config file's env block, or in the platform allowlist. Report: "X env var references inspected, Y documented, 0 undocumented." -
Fail criteria: At least 1 env var is referenced in code but not documented anywhere.
-
Do NOT pass when: The reference is a computed key (
process.env[varName]) ANDvarNameis a string variable — this hides hallucinations. Flag with detail. -
Skip (N/A) when: Project has ZERO
process.env/import.meta.env/Deno.env.get/Bun.envreferences in source code (excluding platform allowlist vars likeNODE_ENV). If env vars ARE referenced but no documentation artifact exists (no.env.example, no typed env schema, no config env block), that is a FAIL — not a skip. Detail should list the undocumented vars and note: "No env documentation artifact found (.env.example, typed schema, or config env block). All N non-platform env vars are undocumented." -
Cross-reference: For full env var hygiene including secret scanning and rotation policies, the Environment Security audit (
environment-security) covers env management in depth. -
Detail on fail:
"3 undocumented env vars: STRIPE_SECRET_KEY referenced in src/lib/stripe.ts (not in .env.example), DATABASE_URL_REPLICA referenced in src/lib/db.ts (typo of DATABASE_URL?), OPENAI_KEY referenced in src/lib/ai.ts (env schema declares OPENAI_API_KEY)." -
Remediation: An undocumented env var means the code references something that has no defined source — on a fresh deploy, the value is undefined and the code silently breaks. Fix each one by documenting it:
# .env.example STRIPE_SECRET_KEY=sk_test_xxxxxxxxxxxx DATABASE_URL=postgresql://localhost:5432/myapp OPENAI_API_KEY=sk-xxxxxxxxxxxxxBetter: use a typed env schema (Zod-based) so missing values fail fast at startup:
// src/env.ts import { z } from 'zod' export const env = z.object({ STRIPE_SECRET_KEY: z.string().min(1), DATABASE_URL: z.string().url(), OPENAI_API_KEY: z.string().min(1), }).parse(process.env)
External references
- cwe · CWE-798 — Use of Hard-coded Credentials: undocumented env vars are often secrets embedded in code rather than injected at runtime
- owasp:2021 · A05 — Security Misconfiguration: missing env documentation leads to misconfigured deployments
Taxons
History
- 2026-04-18·v1.0.0·Initial import from ai-slop-hallucinations·automated
- 2026-04-20·v1.1.0·Add Phase 6.0 detect-bash snippet to check for .env.example presence·by cakleinman