A hardcoded secret in source code is immediately compromised the moment the repository is cloned, forked, or accidentally made public — and Git history preserves it even after deletion. NIST 800-53 rev5 IA-5 (Authenticator Management) prohibits embedding authenticators in code, and SC-28 requires protection of information at rest including stored credentials. CWE-798 covers this exact failure; OWASP A07 (Identification and Authentication Failures) lists hardcoded credentials as a primary cause of large-scale breaches. A single leaked Stripe live key or database password is sufficient to exfiltrate all user data or drain financial accounts.
Critical because a hardcoded secret in any committed file immediately grants adversaries full access to whatever service the credential controls, requiring no further exploitation.
Remove every literal secret from source files and replace with process.env references. Add .env.local to .gitignore before the first commit; use .env.example with placeholder values as the only committed env template.
// lib/db.ts — BEFORE (wrong)
const client = new Client({ password: 'hunter2' })
// lib/db.ts — AFTER (correct)
const client = new Client({ password: process.env.DB_PASSWORD })
For secrets already in Git history, rotate the credentials immediately — removing the commit does not protect any clone taken before the rewrite. Store live secrets in Vercel Environment Variables, AWS Secrets Manager, or HashiCorp Vault. Run npx secretlint or git-secrets in CI to block future commits.
ID: gov-fisma-fedramp.access-control.no-hardcoded-secrets
Severity: critical
What to look for: List all directories and file patterns searched (e.g., src/, lib/, app/, config/). Search the entire codebase for hardcoded secrets using patterns: password = "...", api_key = "...", secret = "...", database connection strings, private keys, tokens, or API keys directly in code files. For each flagged item, quote the variable name and assignment pattern (not the actual secret value). Check environment variable declarations — they should reference process.env, not literal values. Search for common secret names: password, apikey, secret, token, privatekey, jwt_secret, aws_secret, stripe_secret, etc.
Pass criteria: No hardcoded secrets found in any committed code across at least 5 directory trees searched. All credentials come from environment variables or secure configuration. Even example/demo code does not contain real secrets. Report even on pass: report the count of files scanned and directories searched.
Fail criteria: Any hardcoded password, API key, token, or secret string found in source code, config files, or test data. Example: const DB_PASSWORD = "p@ssw0rd123" in a config file, or stripe.apiKey = "sk_live_..." in code. Do not pass when a cursory scan of only top-level files is performed — actively search all source files, config files, and test fixtures.
Skip (N/A) when: Never — hardcoded secrets are a critical security vulnerability.
Detail on fail: Name the files and secret types found. Example: "Database password hardcoded in lib/db.ts: 'const password = \"admin123\"'. AWS secret key found in .env.example (not .env, but example still contains real secret)."` Keep the detail under 500 characters; use ellipsis if needed.
Cross-reference: For environment variable management and .gitignore configuration, the Security Headers audit covers env-in-gitignore and secret detection patterns.
Remediation: Remove all hardcoded secrets immediately. Use environment variables for all sensitive data:
// lib/db.ts (WRONG)
const password = "p@ssw0rd123"
// lib/db.ts (CORRECT)
const password = process.env.DB_PASSWORD || ''
Store real secrets in .env.local (never committed) or your platform's secrets manager (Vercel Secrets, AWS Secrets Manager, HashiCorp Vault). Use .env.example to document required env vars without exposing values.