[object Object]

← back to Norma Sdcc Pitch

harden(auth): refuse to boot pitch deck in NODE_ENV=production when env passwords missing

6be4a5a31c5a97689bf3548466c2b682fa518c41 · 2026-05-21 19:02:47 -0700 · SteveStudio2

Was: prod boot with unset SDCC_PASSWORD/STEVE_PASSWORD silently fell back
to hard-coded defaults (Pulse-2026 / DWSecure2024!) and logged a warning.
That's the worst kind of credential drift — nobody notices until it leaks.

Now: in NODE_ENV=production, ALL configured passwords must be set as env
vars. If any are missing, process.exit(2) with a stderr explanation of
exactly which ones to set. Dev (NODE_ENV != production) is unchanged —
defaults still allowed for convenience, single warning per default used.

Verified: prod (with env vars set) still boots clean + good-pw login
works. Negative test (env -i NODE_ENV=production node server.js) exits
with the refusal message + code 2.

Files touched

Diff

commit 6be4a5a31c5a97689bf3548466c2b682fa518c41
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date:   Thu May 21 19:02:47 2026 -0700

    harden(auth): refuse to boot pitch deck in NODE_ENV=production when env passwords missing
    
    Was: prod boot with unset SDCC_PASSWORD/STEVE_PASSWORD silently fell back
    to hard-coded defaults (Pulse-2026 / DWSecure2024!) and logged a warning.
    That's the worst kind of credential drift — nobody notices until it leaks.
    
    Now: in NODE_ENV=production, ALL configured passwords must be set as env
    vars. If any are missing, process.exit(2) with a stderr explanation of
    exactly which ones to set. Dev (NODE_ENV != production) is unchanged —
    defaults still allowed for convenience, single warning per default used.
    
    Verified: prod (with env vars set) still boots clean + good-pw login
    works. Negative test (env -i NODE_ENV=production node server.js) exits
    with the refusal message + code 2.
---
 server.js | 28 ++++++++++++++++++++++++----
 1 file changed, 24 insertions(+), 4 deletions(-)

diff --git a/server.js b/server.js
index 1efd3b5..5980095 100644
--- a/server.js
+++ b/server.js
@@ -39,8 +39,13 @@ const NORMA_BASE = process.env.NORMA_BASE || 'http://localhost:7400';
 // cleared from process.env immediately after hashing so they only exist in
 // memory for the duration of the hash call. Login comparison uses scrypt +
 // crypto.timingSafeEqual (constant-time) — no plaintext compare on any
-// request path. Defaults are kept for dev convenience but trigger a stderr
-// warning in production. `crypto` is already required at the top of this file.
+// request path.
+//
+// In production, BOTH env vars must be set. Booting with hard-coded defaults
+// under NODE_ENV=production is refused — silent fallback to known passwords
+// is the worst kind of credential drift (the kind nobody notices until it's
+// in a leak). In dev (NODE_ENV != production), defaults are allowed for
+// convenience.
 const SCRYPT_N = 16384;       // 2^14 — fast enough for login, slow enough vs brute force
 const SCRYPT_KEYLEN = 64;
 function deriveLoginHash(plaintext, salt) {
@@ -53,10 +58,25 @@ const LOGIN_RECORDS = (() => {
     sdcc:  { env: 'SDCC_PASSWORD',  default: 'Pulse-2026'    },
     steve: { env: 'STEVE_PASSWORD', default: 'DWSecure2024!' },
   };
+  const isProd = process.env.NODE_ENV === 'production';
+  const missing = [];
+  for (const [user, { env }] of Object.entries(seed)) {
+    if (!process.env[env]) missing.push({ user, env });
+  }
+  if (isProd && missing.length > 0) {
+    const lines = missing.map(({ user, env }) => `  - ${env}  (for "${user}" account)`);
+    console.error(
+      '\n[auth] REFUSING TO BOOT in NODE_ENV=production with missing login passwords:\n' +
+      lines.join('\n') +
+      '\n\nSet them in /root/Projects/norma-sdcc-pitch/.env (or whatever you use)\n' +
+      'and restart. Falling back to hard-coded defaults in prod is not allowed.\n',
+    );
+    process.exit(2);
+  }
   for (const [user, { env, default: dflt }] of Object.entries(seed)) {
     const pt = process.env[env] || dflt;
-    if (!process.env[env] && process.env.NODE_ENV === 'production') {
-      console.warn(`[auth] WARNING: ${env} not set in env; using hard-coded default for "${user}"`);
+    if (!process.env[env]) {
+      console.warn(`[auth] dev fallback: ${env} not set, using default for "${user}"`);
     }
     const salt = crypto.randomBytes(16);
     out[user] = { salt, hash: deriveLoginHash(pt, salt) };

← 88191f5 fix(auth): replace plain-text password compare with scrypt +  ·  back to Norma Sdcc Pitch  ·  Add per-site favicon (kills /favicon.ico 404) e7cc6ea →