js-security-hardcoded-secret-in-script
Problem: scripts/test-combo-autoswitch.mjs (line 5) hardcoded a live API key as a process.env fallback:
// BAD — before: live key baked into committed script (guard blocks every commit)
const OPENAI_API_KEY =
process.env.OPENAI_API_KEY ??
'sk-live-9f2cA1b3D4e5F6g7H8i9J0k1L2m3N4o5P6q7R8s9T0u'; // LIVE KEY - NEVER COMMIT
Fix applied (matches the reported foreman-direct fix): replaced the live key with a clearly-fake allowlisted placeholder and hardened the script to never silently run on a fallback:
// GOOD — after: key comes ONLY from env; placeholder is clearly fake (sk-test-…)
const OPENAI_API_KEY = process.env.OPENAI_API_KEY;
if (!OPENAI_API_KEY) {
console.error('[test-combo-autoswitch] OPENAI_API_KEY is not set — refusing to run.');
process.exit(1); // fail fast instead of emitting a fake/empty key
}
// CI stub may use the allowlisted fake placeholder:
export const __placeholderKeyForCiOnly = 'sk-test-0000000000000000000000000000000000000000';
Guard side (gitreins-style guard/secrets.mjs) — detects live credential shapes but explicitly allowlists fake placeholders, so legitimate CI stubs pass:
// Live-key detector (flags sk-live-…, real sk-…, AKIA…, api_key=…)
re: /\bsk-(?!test-|fake-|placeholder-|your-|xxxx)[A-Za-z0-9][A-Za-z0-9-]{19,}\b/g,
// Allowlist: clearly fake placeholders are safe to commit
const FAKE_PREFIXES = /^(sk-test-|sk-fake-|sk-placeholder-|sk-your-|REPLACE_ME|CHANGE_ME|xxxx)/i;
Rules (defense in depth):
1. Never put a real key (or any plausible key) as an env fallback in committed code — a fallback means the secret ships with the repo.
2. If a stub key is required for CI, use a clearly-fake sk-test-…/REPLACE_ME value and keep the guard's allowlist in sync.
3. Fail fast when the env var is missing; don't degrade to a placeholder at runtime.
4. If a real key was ever committed, rotate it and purge it from git history — the guard gates future commits but doesn't undo past ones.
Reproduced the failure and verified the fix in a scratch repo (`/tmp/js-secret-demo`) with a gitreins-style guard: | # | Test | Result | |---|------|--------| | 1 | Guard scans file with live `sk-live-…` key | **FAIL** exit 1: `scripts/test-combo-autoswitch.mjs:8 [openai-api-key] sk-live-9f2c…` — reproduces the blocked-commit symptom | | 2 | Guard after fix (placeholder + env-only) | **PASS** exit 0: `✓ SECRETS GUARD PASS (3 files scanned, 0 live secrets)` | | 3 | Runtime with `env -u OPENAI_API_KEY` | **FAIL fast** exit 1: `OPENAI_API_KEY is not set — refusing to run.` (no silent fallback) | | 4 | Runtime with `OPENAI_API_KEY` set | Works: `OK preset=bedrock keyPrefix=sk-proj-` (key read purely from env) | | 5 | Committed `sk-test-…` stub (CI stub) | Guard **PASS** — placeholder allowlisted | | 6 | `git grep "sk-live"` across all commits | Only a comment mentioning the pattern; **no live key in committed tree** | Edge cases covered: env var absent, env var present, placeholder-shaped stub in code, guard allowlist vs. live-key detection, and post-commit history scan. Notably, this harness shell itself has `OPENAI_API_KEY`/`DEEPSEEK_API_KEY` etc. set in the environment — which is exactly the right place for them; the guard correctly inspects committed *code*, not env.
{"model": "deepseek-v4-flash", "problem_class": "js-security-hardcoded-secret-in-script", "result": "passed", "tests": 6}