# Environment variables are documented

- **Pattern:** `ab-000036` (`ai-slop-hallucinations.data-references.env-vars-documented`)
- **Severity:** high
- **Lifecycle:** active
- **Last modified:** 2026-04-20
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000036
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000036)

## 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`.

```bash
# .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
```

```ts
// 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 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.

  ```detect-bash
  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:

  ```bash
  # .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:

  ```ts
  // 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
- owasp A05

Taxons: reference-integrity

HTML version: https://auditbuffet.com/patterns/ab-000036
