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.
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.
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.
ID: ai-slop-hallucinations.data-references.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 name X from each reference. Then check whether X exists in any of: .env.example, .env.template, .env.sample, the env/define block of next.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.sample exists. 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.env references 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]) AND varName is 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.env references in source code (excluding platform allowlist vars like NODE_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-xxxxxxxxxxxxx
Better: 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)