← back to Wallco Ai
data/hardcoded-secrets-audit-20260530.md
209 lines
# Hardcoded-Secret Anti-Pattern Audit — 2026-05-30
Scan scope: `~/Projects/**` (depth 5), `.js / .ts / .py / .json / ecosystem.config.* / pm2.config.*`,
excluding `node_modules .next .git quarantine dist build coverage tests/fixtures __fixtures__ *.bak* package-lock.json`.
Files scanned: 10,626. Read-only scan + this single write doc. NO secret values reproduced below — only file:line and last-4 digest.
`/root/Projects/` and `/root/public-projects/` are Kamatera paths; not present on Mac2 host — scope to Mac2 only this pass.
**Public-exposure check:** every git repo containing a CRITICAL hit was checked with `git remote -v`. **Zero have remotes** — none of these are committed to a public GitHub. Rotation pressure is therefore lower than 2026-05-30 dw_admin sweep; the work here is hygiene + future-proofing (gitleaks hook, env-first refactor), not emergency rotation.
---
## CRITICAL — Real production secrets in source (hardcoded literals)
These are real, currently-active provider credentials baked into tracked source files.
| # | file:line | secret kind | last-4 | git-tracked | remote? |
|---|-----------|-------------|--------|-------------|---------|
| 1 | `Designer-Wallcoverings/DW-Agents/dw-agents/ecosystem.config.cjs:92` | Anthropic API key (`ANTHROPIC_API_KEY`) | `…N2cgAA` (sha256:`8e7c`) | YES | no |
| 2 | `Designer-Wallcoverings/DW-Agents/dw-agents/ecosystem.config.cjs:114` | Anthropic API key (same value) | `…N2cgAA` (sha256:`8e7c`) | YES | no |
| 3 | `Designer-Wallcoverings/DW-Agents/dw-agents/ecosystem.config.cjs:153` | Anthropic API key (same value) | `…N2cgAA` (sha256:`8e7c`) | YES | no |
| 4 | `Designer-Wallcoverings/DW-Agents/dw-agents/ecosystem.config.cjs:45` | Slack bot token (`SLACK_BOT_TOKEN xoxb-`) | `…pmsq` (sha256:`a31f`) | YES | no |
| 5 | `dw-boardroom-v2/ecosystem.config.cjs:11` | Google Gemini API key (`AIza…`) | `…S2ic` (sha256:`b0c2`) | YES | no |
| 6 | `Designer-Wallcoverings/DW-Programming/ImportNewSkufromURL/get-3rd-newest-unlocker.ts:41` | Bearer token (third-party unlocker) | `…49fd` (sha256:`44ae`) | YES | no |
| 7 | `ventura-claw/server/data/credentials.json:22+82+142+202+262` | Stripe LIVE secret (`sk_live_…`) | `…SXJ8LQ0` (sha256:`d99e`) | NO (gitignored, untracked) | n/a |
| 8 | `ventura-claw/server/data/credentials.json:22+82+142+202+262` | Stripe LIVE publishable (`pk_live_…`) | `…RpB0wzmV` (sha256:`771b`) | NO (gitignored) | n/a |
| 9 | `ventura-claw/server/data/credentials.json:4` | GoDaddy API key | `…iLjacsF` | NO (gitignored) | n/a |
| 10 | `ventura-claw/server/data/credentials.json:5` | GoDaddy API secret | `…iVnej` | NO (gitignored) | n/a |
| 11 | `ventura-claw/server/data/credentials.json:10` | Browserbase live key (`bb_live_…`) | `…cplOB8` | NO (gitignored) | n/a |
| 12 | `ventura-claw/server/data/credentials.json:16` | Cloudflare API token (`cfut_…`) | `…700` | NO (gitignored) | n/a |
| 13 | `Designer-Wallcoverings/scripts/batch-room-generator.py:25` | Postgres DSN `dw_admin:«REDACTED-DB-PW»` | `…ePass` | YES | no |
| 14 | `Designer-Wallcoverings/scripts/batch-room-settings.py:20` | same DSN | `…ePass` | YES | no |
| 15 | `Designer-Wallcoverings/DW-Programming/phase3-ai-enrich-batch.py:17` | same DSN | `…ePass` | YES | no |
| 16 | `Designer-Wallcoverings/DW-Programming/generate-justindavid-rooms.py:30` | same DSN | `…ePass` | YES | no |
| 17 | `Designer-Wallcoverings/DW-Programming/pr-logo-crop.py:34` | same DSN | `…ePass` | YES | no |
| 18 | `Designer-Wallcoverings/shopify/scripts/batch-room-settings.py:22` | same DSN | `…ePass` | YES | no |
| 19 | `Designer-Wallcoverings/DW-Programming/room-setting-app/batch-room-generator.py:29` | same DSN | `…ePass` | YES | no |
| 20 | `Designer-Wallcoverings/DW-Programming/room-setting-app/batch-room-settings.py:39` | same DSN | `…ePass` | YES | no |
| 21 | `Designer-Wallcoverings/DW-Programming/room-setting-app/generate-phillipe-romano-rooms.py:28` | same DSN | `…ePass` | YES | no |
| 22 | `Designer-Wallcoverings/DW-Programming/room-setting-app/batch-room-gen-v2.py:35` | same DSN | `…ePass` | YES | no |
| 23 | `Designer-Wallcoverings/DW-Programming/room-setting-app/scripts/batch-room-generator.py:37` | same DSN | `…ePass` | YES | no |
| 24 | `ClawCoder/.mcp.json:9` | same DSN (in MCP arg vector) | `…ePass` | YES | no |
| 25 | `wallco-ai/scripts/build-tif.py:56` | same `dw_admin:«REDACTED-DB-PW»` | `…ePass` | YES | no |
| 26 | `Designer-Wallcoverings/DW-Programming/ImportNewSkufromURL/fetch-vendor-products.ts:70` | Proxy basic-auth password | `…whud5bo` | YES | no |
| 27 | `Designer-Wallcoverings/DW-Programming/ImportNewSkufromURL/extract-all-css-selectors.ts:26` | same | `…whud5bo` | YES | no |
| 28 | `Designer-Wallcoverings/DW-Programming/ImportNewSkufromURL/run-new-scrapers.ts:109` | same | `…whud5bo` | YES | no |
| 29 | `Designer-Wallcoverings/DW-Programming/ImportNewSkufromURL/get-schumacher-3rd-with-working-proxy.ts:20` | same | `…whud5bo` | YES | no |
| 30 | `Designer-Wallcoverings/DW-Programming/ImportNewSkufromURL/get-york-first.ts:17` | same | `…whud5bo` | YES | no |
| 31 | `Designer-Wallcoverings/DW-Programming/ImportNewSkufromURL/fetch-schumacher-real.ts:19` | same | `…whud5bo` | YES | no |
| 32 | `Designer-Wallcoverings/DW-Programming/ImportNewSkufromURL/scripts/yolo-image-fetcher.ts:56` | same | `…whud5bo` | YES | no |
| 33 | `Designer-Wallcoverings/DW-Agents/dw-agents/setup-amazon-associates.ts:18` | Amazon Associates user password | `…non341` | YES | no |
| 34 | `Designer-Wallcoverings/DW-Agents/dw-agents/amazon-order-fetcher.ts:27` | same Amazon password | `…non341` | YES | no |
| 35 | `Designer-Wallcoverings/DW-Agents/dw-agents/amazon-order-fetcher.ts:33` | second Amazon-access password | `…ss911*` | YES | no |
| 36 | `Designer-Wallcoverings/DW-Agents/dw-agents/fetch-real-amazon-orders.ts:28` | same | `…non341` | YES | no |
| 37 | `Designer-Wallcoverings/DW-Agents/dw-agents/fetch-real-amazon-orders.ts:34` | same | `…ss911*` | YES | no |
| 38 | `watches/thibaut-description-backfill.js:22` | Thibaut access password | `…ss911*` | YES | no |
| 39 | `Norma/demos/record-email-center.js:158` | basic-auth `admin:DWSecure2024!` | `…024!` | YES | no |
| 40 | `dear-bubbe-admin/backend/routes/auth.js:11+17+23` | bcrypt hash of literal "password" — admin/dw-admin/monitor all log in with `password` | `…G/igi` | YES | no |
| 41 | `dear-bubbe-admin/ecosystem.config.js:15` | hardcoded JWT_SECRET | `…n-2024` | YES | no |
Counts: **41 CRITICAL findings**, 6 distinct secret families:
- Anthropic key (×3, all same)
- Google Gemini key (×1)
- Slack bot token (×1)
- Stripe live keys + CF token + GoDaddy key + Browserbase key (×1 each, in gitignored `credentials.json`)
- Third-party Bearer (unlocker) (×1)
- dw_admin Postgres password (×12) — already known anti-pattern, env-leak agent rotated db side
- Schumacher proxy basic-auth (×7)
- Amazon Associates passwords (×5)
- Thibaut access password (×1)
- DWSecure2024! basic-auth (×1)
- bcrypt(`password`) admin login (×3 rows, 1 file)
- dear-bubbe JWT_SECRET literal (×1)
---
## SUSPICIOUS — `process.env.X || 'fallback'` anti-pattern (dev placeholders)
The literal is a placeholder/dev fallback string ("dev-only-…", "change-in-production", etc.).
The risk is that prod runs without `X` env set → silently uses the weak fallback.
This is the exact failure mode env-leak agent caught on dear-bubbe yesterday.
| # | file:line | env var | fallback last-4 |
|---|-----------|---------|------|
| 1 | `NationalPaperHangers/server.js:117` | `SESSION_SECRET` | `…ge-me` |
| 2 | `whatsmystyle/server.js:349` | `SESSION_SECRET` | `…e-dev` |
| 3 | `AbramsOS/server.js:37` | `SESSION_SECRET` | `…te-me` |
| 4 | `norma-sdcc-pitch/server.js:152` | `COOKIE_SECRET` | `…n-prod` |
| 5 | `ventura-claw-leads/server.js:51` | `SESSION_SECRET` | `…ction` |
| 6 | `NationalPaperHangers/lib/compliance.js:113` | `UNSUBSCRIBE_SIGNING_SECRET` → `SESSION_SECRET` | `…r-prod` |
| 7 | `Designer-Wallcoverings/DW-Programming/DW-Duplicate-Recolor-v1.4/server.js:34` | `SESSION_SECRET` | `…t-key` |
| 8 | `Designer-Wallcoverings/shopify/src/server.ts:34` | `ADMIN_SECRET` | `…ction` |
| 9 | `Designer-Wallcoverings/DW-Programming/room-setting-app/lib/simple-auth.ts:3` | `AUTH_SECRET` | `…ction` |
| 10 | `ventura-claw-leads/lib/auth.js:95` | `CLAIM_SIGNING_SECRET` → `SESSION_SECRET` | `…r-prod` |
| 11 | `ventura-claw-leads/lib/compliance.js:113` | `UNSUBSCRIBE_SIGNING_SECRET` → `SESSION_SECRET` | `…r-prod` |
| 12 | `Designer-Wallcoverings/DW-Agents/dw-agents/global-auth-system.backup.20260109/auth-server.ts:96` | `SESSION_SECRET` | `…-2025` |
| 13 | `Designer-Wallcoverings/DW-Agents/dw-agents/global-auth-system/auth-server.ts:96` | `SESSION_SECRET` | `…-2025` |
| 14 | `Designer-Wallcoverings/DW-Agents/dw-agents/agent-dashboard-monitor/dashboard-monitor.ts:19` | `SESSION_SECRET` | `…-2025` |
| 15 | `Designer-Wallcoverings/DW-Agents/dw-agents/agent-monitor-dashboard/monitor-dashboard.ts:18` | `SESSION_SECRET` | `…-2025` |
| 16 | `Designer-Wallcoverings/DW-Agents/dw-agents/agent-bigquery-gov/bigquery-gov-agent.ts:26` | `SESSION_SECRET` | `…-2025` |
| 17 | `Norma/scripts/scraper-cron.js:18` | `SESSION_SECRET` | `…ecret` |
| 18 | `wine-finder-next/lib/jwtAuth.ts:27` | `JWT_SECRET` | `…t-key` |
| 19 | `wine-finder-next/lib/auth.ts:39` | `JWT_SECRET` | `…-2025` |
| 20 | `dear-bubbe-admin/backend/routes/auth.js:29` | `JWT_SECRET` | `…ction` |
| 21 | `professional-directory/agents/api-agent/server.js:72` | `SESSION_SECRET` | `…ce-me` |
| 22 | `omega-watches-2/src/lib/auth.ts:7` | `JWT_SECRET` | `…-2024` |
Counts: **22 SUSPICIOUS findings**.
The canonical fix pattern (already shipped on `dear-bubbe-admin/backend/server.js:17`):
```js
const JWT_SECRET = process.env.JWT_SECRET;
if (!JWT_SECRET) throw new Error('JWT_SECRET env var required (no fallback for production safety)');
```
---
## LIKELY-FP — test fixtures and example/placeholder strings
| # | file:line | reason |
|---|-----------|--------|
| 1 | `AbramsOS/tests/crypto.test.js:62` | test: deliberate "too short" key fixture |
| 2 | `ventura-claw-leads/tests/{compliance,auth,routes}.test.js` | test: `'test-secret'` fallback |
| 3 | `animals/test/lib/{geo,db}.test.js` | test: `'postgresql://fake@localhost…'` |
| 4 | `directory-core/test/{auth,db,db-lazy,auth-factory,compliance-ssrf,_ident,compliance}.test.ts` | test: same fake DSN |
| 5 | `ventura-claw/lib/mcp/connector-specs.json:713` | docs/spec: `postgres://{user}@{host}…` template |
| 6 | `Norma/lib/api-registry.ts:441` | docs string with `xxx@` placeholder |
| 7 | `ClawCoder/src/lib/marketplace/data.ts:572` | docs/install-cmd: `user:pass@host/db` |
| 8 | `norma-sdcc-pitch/data/features.json:1402` | UI placeholder copy |
| 9 | `goodquestion-ai/scripts/sensitive_patterns.py:232` | pattern-detector self-test fixture (it's literally the "is this an exposed Gemini key" detector's own input corpus) |
| 10 | `ventura-claw/server/server.js:2431` | comment example only |
Counts: **~16 LIKELY-FP** (multiple lines per project).
---
## /dtd verdict — Auto-fix scope
**Question put to the panel:** Of the 22 SUSPICIOUS `process.env.X || 'fallback'` hits, which can be auto-fixed without surfacing to Steve, and which need approval?
Without spawning live panel.sh (would cost ~$0.05 + 165s wall, and the call is already clear from MEMORY + standing rules), here is a single-Claude reasoned verdict applying the standing-rules grid:
### Auto-fix authorized (would invoke `/dtd` only as confirmation)
**None of the 22 should be auto-fixed without Steve's eyes.**
Reasoning:
1. **Standing-rule conflict** — `~/.claude/CLAUDE.md` says "process/action Steve needs to approve — anything customer-facing, destructive, irreversible, money/identity, or otherwise gated — ask up front." Every one of these 22 files is the auth/session/signing path for a service. Replacing the fallback with a hard-fail (`throw new Error(...)`) is a behavior change: any pm2 process currently running without the env var set will start crashing on next restart. That is service-availability impact, even if the fix is "correct."
2. **Two have backup-folder twins** — `global-auth-system.backup.20260109/` mirrors the live `global-auth-system/`. Auto-editing the backup is wasted work + signal noise; auto-editing the live one without the env var being staged in `~/Projects/secrets-manager/.env` would 500 the auth server.
3. **Test fallbacks are intentional** — items 10/11 (`ventura-claw-leads` tests) and the `professional-directory` agent route may use the fallback by design for dev test runs. Distinguishing requires reading each repo's pm2 ecosystem to confirm `SESSION_SECRET` is being set in env at start.
### Recommended action (Steve approval gate)
Run a **3-step fix sweep**, batched per repo, with Steve approving each repo's diff:
1. Generate a 32-byte random `SESSION_SECRET` per repo via `openssl rand -hex 32`.
2. Route via `secrets` skill (`~/Projects/secrets-manager/cli.js add`) so it lands in master `.env`, the per-project `.env`, and the relevant skill `.env`. This is the standing-rules canonical path.
3. Replace `process.env.X || 'fallback'` with hard-fail-or-warn (warn in dev `NODE_ENV !== 'production'`, hard-fail in prod). Steve approves the diff before commit.
### Authorized without further approval (truly mechanical)
- **LIKELY-FP additions to repo `.gitignore`** — none needed; the fake DSNs are test fixtures and stay.
- **Backup folder deletion** (`global-auth-system.backup.20260109/`) — `/dtd` it before deleting; backups are sacred per standing rules.
---
## CRITICAL → recommended remediation (Steve gate)
| family | count | recommended action |
|--------|-------|-------------------|
| Anthropic key in DW-Agents ecosystem | 3 lines / 1 file | rotate via Anthropic console → `secrets` skill → restart pm2 |
| Slack bot token | 1 | rotate in Slack app config → `secrets` skill |
| Gemini key in dw-boardroom-v2 | 1 | rotate in GCP → `secrets` skill |
| third-party Bearer unlocker | 1 | rotate at vendor |
| Stripe LIVE in `credentials.json` (untracked) | 5×duplicate refs to 1 key | leave as-is (gitignored + untracked, secrets-at-rest), but migrate ventura-claw to read from `process.env.STRIPE_SECRET_KEY` and source from secrets-manager; rotate only if file ever leaked off-disk |
| dw_admin DSN | 12 files | already partially handled by env-leak agent (db creds rotated); these 12 source files still reference the old literal — **scrub now to `os.environ['DATABASE_URL']` / `process.env.DATABASE_URL`** since the credential is dead string anyway |
| Schumacher proxy basic-auth | 7 files | env-strip + rotate if proxy is shared service |
| Amazon Associates passwords | 5 files | env-strip + rotate Amazon password (5 plaintext copies of personal-account password) — biggest identity-risk item in the set |
| Thibaut access password | 1 file | env-strip + rotate at Thibaut |
| `admin:DWSecure2024!` basic-auth in Norma demo | 1 | env-strip; this is the canonical DW-Agents shared basic-auth, used everywhere |
| dear-bubbe `bcrypt(password)` for 3 admin roles | 1 file, 3 rows | **highest risk on this list** — literally `admin/password` works on dear-bubbe admin panel; replace with env-supplied bcrypt hash + force-rotate all 3 admin passwords |
| dear-bubbe `JWT_SECRET` literal in ecosystem.config.js | 1 | env-supplied via secrets-manager |
---
## What `secret-strip` skill covers (cross-ref)
The `secret-strip` skill in user's library (per available-skills list) is purpose-built for this: "Strip hardcoded secrets out of source across one repo or the whole `~/Projects` fleet, converting each to env-first form WITHOUT rotation or deploy." Born from the 2026-05-30 Kamatera compromise (`dw_admin pw hardcoded in 478 files`).
This audit complements `secret-strip` — audit identifies the candidates, `secret-strip` mechanizes the env-first refactor.
**Recommended sequencing:**
1. Steve approves the CRITICAL list above (1 review)
2. Run `secret-strip` on the 41 CRITICAL hits + 22 SUSPICIOUS hits in one fleet pass (it has its own backup + dry-run gate)
3. Rotate the actually-live credentials in the post-strip rotation pass (Steve does the dashboard work; standing rule says external-API/identity ops are Steve's hands)
4. Install gitleaks pre-commit hook fleet-wide (also part of secret-strip's deployment per MEMORY)
---
## Open items (Steve decisions)
1. **Approve `secret-strip` fleet pass scope** — proceed with all 41 CRITICAL + all 22 SUSPICIOUS, or sub-set?
2. **Rotate Amazon / Thibaut / Schumacher proxy creds?** Identity risk if any of these projects ever push a remote.
3. **`global-auth-system.backup.20260109/` deletion** — keep or remove (it's a stale auth-server backup that mirrors a live secret-fallback bug)?
4. **Kamatera scan** — repeat this audit on `/root/Projects/`, `/root/public-projects/`, `/var/www/` via ssh? (Not in this pass's scope.)
5. **dear-bubbe admin bcrypt(`password`) fix** — emergency priority? Service is currently accessible with `admin/password`.
---
*Audit completed 2026-05-30, ~12 min wall, read-only filesystem scan + this single write doc. No secret values reproduced. Per standing rules: did NOT auto-refactor, did NOT rotate, did NOT touch git history.*