# No hardcoded secrets in source code

- **Pattern:** `ab-001258` (`environment-security.secrets-management.no-hardcoded-secrets`)
- **Severity:** critical
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001258
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001258)

## Why it matters

Hardcoded secrets in source code violate CWE-798 (Use of Hard-coded Credentials) and OWASP A07 (Identification & Authentication Failures), and they are permanent: unlike a leaked .env file, a credential baked into a function call survives every deployment, every refactor, and every repo clone. A Stripe live key or database password embedded in a TypeScript file is readable by every developer, CI runner, contractor, and open-source contributor who touches the repo. NIST IA-5 requires credential management controls — hardcoded values have no rotation path, no access control, and no audit trail.

## Severity rationale

Critical because hardcoded credentials are unrevokable without a code change and are visible to everyone with repository access, making targeted rotation impossible.

## Remediation

Move every secret out of source files and into environment variables. Validate presence at startup so missing config fails fast rather than failing silently mid-request.

```ts
// src/config/secrets.ts
// BEFORE (wrong)
export const STRIPE_KEY = 'sk_live_51234...'

// AFTER (correct)
export const STRIPE_KEY = process.env.STRIPE_SECRET_KEY
if (!STRIPE_KEY) throw new Error('STRIPE_SECRET_KEY not set')
```

For each secret removed from source code, generate a new credential in the service dashboard (the old one is compromised), store it in your secrets manager or CI environment, and delete it from git history with `git filter-repo`.

## Detection

- **ID:** `no-hardcoded-secrets`
- **Severity:** `critical`
- **What to look for:** Search the entire codebase for hardcoded secrets. Use tools like `truffleHog`, `gitleaks`, `detect-secrets`, or manual grep patterns. Search for API keys, tokens, passwords, private keys, database credentials, and OAuth tokens. Check both source files and configuration files (vite.config.ts, next.config.ts, etc.).
- **Pass criteria:** Count every source file searched and enumerate all pattern matches for secret-like strings. 0% of files may contain hardcoded secrets (except clearly marked placeholders like `xxx_placeholder` or `CHANGEME`). Report the count of files scanned even on pass.
- **Fail criteria:** Any hardcoded secret found in source code or config files — API key, token, password, private key, or database URL with credentials embedded. Quote the variable name and file location (never the actual secret value). Should not pass when even a single secret-like pattern is found outside environment variables.
- **Skip (N/A) when:** Never — hardcoded secrets are a critical vulnerability.
- **Detail on fail:** Specify the type of secret found and the file location. Example: `"Stripe API key found hardcoded in src/payment/stripe.ts line 12; AWS access key found in terraform/main.tf line 45"`. Do NOT include the actual secret value.
- **Remediation:** Move all secrets to environment variables. Use a tool like `dotenv` to load .env files in development:

  ```ts
  // src/config/secrets.ts — BEFORE (wrong)
  export const STRIPE_KEY = 'sk_live_51234...'

  // src/config/secrets.ts — AFTER (correct)
  export const STRIPE_KEY = process.env.STRIPE_SECRET_KEY
  if (!STRIPE_KEY) throw new Error('STRIPE_SECRET_KEY not set')
  ```

  For each rotated secret, generate a new one and update it in your secrets manager or CI/CD environment.

## External references

- cwe CWE-798
- cwe CWE-259
- owasp A07
- nist IA-5
- pci-dss Req-3.4

Taxons: cryptography-and-secrets

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