[object Object]

← back to Norma

auto-save: 2026-08-05T13:12:15 (2 files) — scripts/test-instance.sh tests/

d9cb4a20575a5c676e84ad0873c11a4b8190347c · 2026-08-05 13:12:24 -0700 · Steve Abrams

Files touched

Diff

commit d9cb4a20575a5c676e84ad0873c11a4b8190347c
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Aug 5 13:12:24 2026 -0700

    auto-save: 2026-08-05T13:12:15 (2 files) — scripts/test-instance.sh tests/
---
 scripts/test-instance.sh     |  26 +++
 tests/auth-cluster-a.mjs     | 433 +++++++++++++++++++++++++++++++++++++++++++
 tests/b-api-sweep-verify.mjs | 242 ++++++++++++++++++++++++
 tests/cluster-c-ui-tiers.mjs | 244 ++++++++++++++++++++++++
 tests/cluster-d-hazards.mjs  | 177 ++++++++++++++++++
 tests/cluster-e-gotchas.mjs  | 270 +++++++++++++++++++++++++++
 6 files changed, 1392 insertions(+)

diff --git a/scripts/test-instance.sh b/scripts/test-instance.sh
new file mode 100755
index 0000000..b0f3e5f
--- /dev/null
+++ b/scripts/test-instance.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+# Norma isolated TEST instance — runs the production build on :7411 against the
+# scratch DB `sdcc_test` with every real-send integration pointed at an
+# unreachable sink, so the campaign can exercise write paths + auth without
+# touching live :7400 / the sdcc DB or sending any real email/Slack/Gmail.
+#
+# Usage: bash scripts/test-instance.sh   (foreground; run via run_in_background)
+set -euo pipefail
+export PATH="/opt/homebrew/opt/postgresql@14/bin:/opt/homebrew/bin:$PATH"
+cd "$(dirname "${BASH_SOURCE[0]}")/.."
+
+export PORT=7411
+export NODE_ENV=production
+export DATABASE_URL="postgresql://127.0.0.1:5432/sdcc_test"
+export SESSION_SECRET="norma-test-secret-do-not-use-in-prod-0f3a9c"
+# --- sink every external-send integration (unreachable → graceful failure) ---
+export SMTP_HOST="127.0.0.1"; export SMTP_PORT="1"; export SMTP_USER="sink"; export SMTP_PASS="sink"
+export TEST_SEND_TO="sink@invalid.test"
+export SLACK_WEBHOOK_URL="http://127.0.0.1:1/sink"
+export GEORGE_URL="http://127.0.0.1:1"; export GEORGE_AUTH="sink"
+export GEMINI_API_KEY=""            # AI routes must fail gracefully, not burn tokens
+export GMAIL_CLIENT_ID=""; export GMAIL_CLIENT_SECRET=""
+export CRON_SECRET="test-cron-secret"
+
+echo "[test-instance] starting next on :$PORT against sdcc_test (sinked integrations)"
+exec node_modules/.bin/next start -p "$PORT"
diff --git a/tests/auth-cluster-a.mjs b/tests/auth-cluster-a.mjs
new file mode 100644
index 0000000..2afd4af
--- /dev/null
+++ b/tests/auth-cluster-a.mjs
@@ -0,0 +1,433 @@
+/**
+ * Norma Auth Cluster A — adversarial verification suite
+ * Target: http://127.0.0.1:7411 (test instance, sdcc_test DB)
+ * Run: node tests/auth-cluster-a.mjs
+ */
+
+const BASE = 'http://127.0.0.1:7411';
+const PASS = '\x1b[32mPASS\x1b[0m';
+const FAIL = '\x1b[31mFAIL\x1b[0m';
+const WARN = '\x1b[33mWARN\x1b[0m';
+
+let results = [];
+
+function record(id, label, status, detail) {
+  results.push({ id, label, status, detail });
+  const tag = status === 'PASS' ? PASS : status === 'FAIL' ? FAIL : WARN;
+  console.log(`[${tag}] ${id}: ${label}`);
+  if (detail) console.log(`       ${detail}`);
+}
+
+// ── Helper: login and get cookie ───────────────────────────────────────────
+async function login(username, password) {
+  const r = await fetch(`${BASE}/api/auth/login`, {
+    method: 'POST',
+    headers: { 'Content-Type': 'application/json' },
+    body: JSON.stringify({ username, password }),
+  });
+  const cookie = r.headers.get('set-cookie') || '';
+  const body = await r.json().catch(() => ({}));
+  return { status: r.status, cookie, body };
+}
+
+function extractCookie(setCookieHeader) {
+  // e.g. "norma-auth=TOKEN; Path=/; HttpOnly; ..."
+  const match = setCookieHeader.match(/norma-auth=([^;]+)/);
+  return match ? `norma-auth=${match[1]}` : '';
+}
+
+// ── Test suite ─────────────────────────────────────────────────────────────
+
+// A1: Valid admin login
+async function t_A1() {
+  const { status, cookie, body } = await login('admin', 'TestPass123!');
+  if (status === 200 && body.success && cookie.includes('norma-auth')) {
+    record('A1', 'Valid admin login returns 200 + sets norma-auth cookie', 'PASS', `role=${body.role}`);
+  } else {
+    record('A1', 'Valid admin login returns 200 + sets norma-auth cookie', 'FAIL',
+      `status=${status} body=${JSON.stringify(body)} cookie=${cookie.slice(0,60)}`);
+  }
+  return extractCookie(cookie);
+}
+
+// A2: Valid staff login
+async function t_A2() {
+  const { status, cookie, body } = await login('teststaff', 'TestPass123!');
+  if (status === 200 && body.success && cookie.includes('norma-auth')) {
+    record('A2', 'Valid staff login returns 200 + sets norma-auth cookie', 'PASS', `role=${body.role}`);
+  } else {
+    record('A2', 'Valid staff login returns 200 + sets norma-auth cookie', 'FAIL',
+      `status=${status} body=${JSON.stringify(body)}`);
+  }
+  return extractCookie(cookie);
+}
+
+// A3: Valid pulse login
+async function t_A3() {
+  const { status, cookie, body } = await login('testpulse', 'TestPass123!');
+  if (status === 200 && body.success && cookie.includes('norma-auth')) {
+    record('A3', 'Valid pulse login returns 200 + sets norma-auth cookie', 'PASS', `role=${body.role}`);
+  } else {
+    record('A3', 'Valid pulse login returns 200 + sets norma-auth cookie', 'FAIL',
+      `status=${status} body=${JSON.stringify(body)}`);
+  }
+  return extractCookie(cookie);
+}
+
+// A4: Wrong password → 401, NO session cookie
+async function t_A4() {
+  const { status, cookie, body } = await login('admin', 'WrongPassword!');
+  const hasAuthCookie = cookie.includes('norma-auth=') && !cookie.includes('norma-auth=;');
+  if (status === 401 && !hasAuthCookie) {
+    record('A4', 'Wrong password → 401, no session cookie set', 'PASS', `body=${JSON.stringify(body)}`);
+  } else {
+    record('A4', 'Wrong password → 401, no session cookie set', 'FAIL',
+      `status=${status} cookie=${cookie.slice(0,80)} body=${JSON.stringify(body)}`);
+  }
+}
+
+// A5: Unknown username → 401
+async function t_A5() {
+  const { status, body } = await login('nonexistent_user_xyz', 'TestPass123!');
+  if (status === 401) {
+    record('A5', 'Unknown username → 401', 'PASS', `body=${JSON.stringify(body)}`);
+  } else {
+    record('A5', 'Unknown username → 401', 'FAIL', `status=${status} body=${JSON.stringify(body)}`);
+  }
+}
+
+// A6: Empty body → 400
+async function t_A6() {
+  const r = await fetch(`${BASE}/api/auth/login`, {
+    method: 'POST',
+    headers: { 'Content-Type': 'application/json' },
+    body: JSON.stringify({}),
+  });
+  const body = await r.json().catch(() => ({}));
+  if (r.status === 400) {
+    record('A6', 'Empty credentials body → 400', 'PASS', `body=${JSON.stringify(body)}`);
+  } else {
+    record('A6', 'Empty credentials body → 400', 'FAIL', `status=${r.status} body=${JSON.stringify(body)}`);
+  }
+}
+
+// A7: Malformed JSON → 400
+async function t_A7() {
+  const r = await fetch(`${BASE}/api/auth/login`, {
+    method: 'POST',
+    headers: { 'Content-Type': 'application/json' },
+    body: 'not-json{{{',
+  });
+  const body = await r.json().catch(() => ({}));
+  if (r.status === 400) {
+    record('A7', 'Malformed JSON body → 400', 'PASS', `body=${JSON.stringify(body)}`);
+  } else {
+    record('A7', 'Malformed JSON body → 400', 'FAIL', `status=${r.status} body=${JSON.stringify(body)}`);
+  }
+}
+
+// A8: /api/auth/session with no cookie → 200 authenticated:false (NOT 401)
+async function t_A8() {
+  const r = await fetch(`${BASE}/api/auth/session`);
+  const body = await r.json().catch(() => ({}));
+  if (r.status === 200 && body.authenticated === false) {
+    record('A8', 'GET /api/auth/session unauthenticated → 200 + authenticated:false', 'PASS', '');
+  } else {
+    record('A8', 'GET /api/auth/session unauthenticated → 200 + authenticated:false', 'FAIL',
+      `status=${r.status} body=${JSON.stringify(body)}`);
+  }
+}
+
+// A9: /api/auth/session with valid admin cookie → authenticated:true + correct data
+async function t_A9(adminCookie) {
+  const r = await fetch(`${BASE}/api/auth/session`, {
+    headers: { Cookie: adminCookie },
+  });
+  const body = await r.json().catch(() => ({}));
+  if (r.status === 200 && body.authenticated === true && body.role === 'admin') {
+    record('A9', 'GET /api/auth/session with admin cookie → authenticated:true, role:admin', 'PASS',
+      `user=${body.user}`);
+  } else {
+    record('A9', 'GET /api/auth/session with admin cookie → authenticated:true, role:admin', 'FAIL',
+      `status=${r.status} body=${JSON.stringify(body)}`);
+  }
+}
+
+// A10: Tampered token → 401/redirect (should NOT authenticate)
+async function t_A10() {
+  // Build a plausible-looking but invalid token
+  const fakePayload = Buffer.from(JSON.stringify({ u: 'admin', r: 'admin', o: null, t: Date.now().toString() })).toString('base64');
+  const fakeSig = 'deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef';
+  const fakeToken = `${fakePayload}.${fakeSig}`;
+  
+  // Check: session endpoint should return authenticated:false
+  const r = await fetch(`${BASE}/api/auth/session`, {
+    headers: { Cookie: `norma-auth=${fakeToken}` },
+  });
+  const body = await r.json().catch(() => ({}));
+  if (body.authenticated === false || r.status === 401) {
+    record('A10', 'Tampered token rejected by /api/auth/session', 'PASS', `status=${r.status}`);
+  } else {
+    record('A10', 'Tampered token rejected by /api/auth/session', 'FAIL',
+      `status=${r.status} body=${JSON.stringify(body)} — TAMPERED TOKEN ACCEPTED!`);
+  }
+}
+
+// A11: Protected route with no cookie → redirect to /login (NOT data leak)
+async function t_A11() {
+  const r = await fetch(`${BASE}/api/users`, { redirect: 'manual' });
+  // Should get 401 from requireRole, or middleware redirect (3xx)
+  if (r.status === 401 || r.status === 302 || r.status === 307 || r.status === 308) {
+    record('A11', 'Protected /api/users with no cookie → 401 or redirect', 'PASS', `status=${r.status}`);
+  } else {
+    const body = await r.json().catch(() => ({}));
+    record('A11', 'Protected /api/users with no cookie → 401 or redirect', 'FAIL',
+      `status=${r.status} body=${JSON.stringify(body).slice(0,200)} — DATA LEAK?`);
+  }
+}
+
+// A12: Staff can NOT access admin-only endpoint
+// /api/users is admin|staff so we need an admin-only route. Check /api/settings or similar
+async function t_A12(staffCookie) {
+  // Try an admin-only route — check the users management (admin only per requireRole admin+staff is not that helpful)
+  // Let's check permissions endpoint which might be admin-only
+  const r = await fetch(`${BASE}/api/users/permissions`, {
+    headers: { Cookie: staffCookie },
+    redirect: 'manual',
+  });
+  const body = await r.json().catch(() => ({}));
+  record('A12', `Staff hitting /api/users/permissions → status=${r.status}`, 
+    r.status === 403 ? 'PASS' : r.status === 200 ? 'WARN' : 'PASS',
+    `body=${JSON.stringify(body).slice(0,100)}`);
+}
+
+// A13: Pulse role cannot access /api/users (admin+staff only)
+async function t_A13(pulseCookie) {
+  const r = await fetch(`${BASE}/api/users`, {
+    headers: { Cookie: pulseCookie },
+    redirect: 'manual',
+  });
+  const body = await r.json().catch(() => ({}));
+  if (r.status === 403) {
+    record('A13', 'Pulse role → /api/users → 403 Forbidden', 'PASS', '');
+  } else if (r.status === 200) {
+    record('A13', 'Pulse role → /api/users → 403 Forbidden', 'FAIL',
+      `GOT 200! body=${JSON.stringify(body).slice(0,200)} — PRIVILEGE ESCALATION`);
+  } else {
+    record('A13', 'Pulse role → /api/users → 403 Forbidden', 'WARN',
+      `status=${r.status} body=${JSON.stringify(body).slice(0,100)}`);
+  }
+}
+
+// A14: Logout clears cookie (Max-Age=0)
+async function t_A14(adminCookie) {
+  const r = await fetch(`${BASE}/api/auth/logout`, {
+    method: 'POST',
+    headers: { Cookie: adminCookie },
+  });
+  const setCookie = r.headers.get('set-cookie') || '';
+  if (r.status === 200 && (setCookie.includes('Max-Age=0') || setCookie.includes('max-age=0'))) {
+    record('A14', 'POST /api/auth/logout → 200 + sets Max-Age=0 cookie', 'PASS', '');
+  } else {
+    record('A14', 'POST /api/auth/logout → 200 + sets Max-Age=0 cookie', 'FAIL',
+      `status=${r.status} set-cookie=${setCookie.slice(0,100)}`);
+  }
+}
+
+// A15: Cookie NOT Secure in test env (USE_HTTPS not set) — verify it's present or absent correctly
+async function t_A15() {
+  const { cookie } = await login('admin', 'TestPass123!');
+  // In test env (NODE_ENV != production || USE_HTTPS != true), Secure flag should be ABSENT
+  // If Secure IS present on a plain HTTP server, the cookie won't be sent by browsers
+  const hasSecure = cookie.toLowerCase().includes('secure');
+  if (!hasSecure) {
+    record('A15', 'Secure flag absent in non-HTTPS test env (correct)', 'PASS', '');
+  } else {
+    record('A15', 'Secure flag absent in non-HTTPS test env', 'WARN',
+      `Cookie has Secure flag on HTTP-only test server — may break browser auth: ${cookie.slice(0,120)}`);
+  }
+}
+
+// A16: HttpOnly flag IS present (prevents JS cookie theft)
+async function t_A16() {
+  const { cookie } = await login('admin', 'TestPass123!');
+  if (cookie.includes('HttpOnly')) {
+    record('A16', 'Cookie has HttpOnly flag (JS theft prevention)', 'PASS', '');
+  } else {
+    record('A16', 'Cookie has HttpOnly flag (JS theft prevention)', 'FAIL',
+      `Cookie is missing HttpOnly! Raw: ${cookie.slice(0,120)}`);
+  }
+}
+
+// A17: Middleware — does it allow /api/v1/ without auth? (per middleware.ts line 16)
+async function t_A17() {
+  // /api/v1/ is explicitly allowed unauthenticated per middleware
+  // Check if there's a live v1 route that might leak data
+  const r = await fetch(`${BASE}/api/v1/`, { redirect: 'manual' });
+  const body = await r.text().catch(() => '');
+  record('A17', `/api/v1/ unauthenticated passthrough (by design) → status=${r.status}`,
+    'WARN',
+    `Middleware explicitly bypasses auth for /api/v1/* — verify each v1 route self-authenticates. body=${body.slice(0,100)}`);
+}
+
+// A18: Does middleware do a STRUCTURAL-only token check (no crypto verify)?
+// This is a known architectural choice — flag it
+async function t_A18() {
+  // Craft a token with valid structure + valid timestamp but WRONG signature
+  // The middleware should let it through (structural only) but route handler should reject
+  const fakePayload = Buffer.from(JSON.stringify({ u: 'admin', r: 'admin', o: null, t: Date.now().toString() })).toString('base64');
+  const fakeSig = 'deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef';
+  const fakeToken = `${fakePayload}.${fakeSig}`;
+  
+  // Hit a protected route that requireRole-gates
+  const r = await fetch(`${BASE}/api/users`, {
+    headers: { Cookie: `norma-auth=${fakeToken}` },
+    redirect: 'manual',
+  });
+  const body = await r.json().catch(() => ({}));
+  
+  // If middleware passes it through but requireRole rejects → 401 = correct defense-in-depth
+  // If 200 → forged token accepted = CRITICAL bug
+  if (r.status === 401 || r.status === 403) {
+    record('A18', 'Forged token: middleware passes structural check but route handler rejects crypto', 'PASS',
+      `Middleware structural-only is by design; route handler catches forgery. status=${r.status}`);
+  } else if (r.status === 200) {
+    record('A18', 'Forged token: middleware passes structural check but route handler rejects crypto', 'FAIL',
+      `CRITICAL: forged token accepted by route handler! body=${JSON.stringify(body).slice(0,200)}`);
+  } else {
+    record('A18', 'Forged token: middleware passes structural check but route handler rejects crypto', 'WARN',
+      `Unexpected status=${r.status} body=${JSON.stringify(body).slice(0,100)}`);
+  }
+}
+
+// A19: Brute-force protection — 10 failures triggers 429
+// We won't actually exhaust 10 tries but verify the counter logic path
+async function t_A19() {
+  // Try 3 bad logins and verify we're still in 400 range (401), not locked yet
+  for (let i = 0; i < 3; i++) {
+    await login('admin', `BadPass${i}!`);
+  }
+  const { status: s1 } = await login('admin', 'StillBadPass!');
+  if (s1 === 401) {
+    record('A19', 'After 3 failures still in window → still 401 (not prematurely locked)', 'PASS', '');
+  } else {
+    record('A19', 'After 3 failures still in window → still 401 (not prematurely locked)', 'WARN',
+      `status=${s1}`);
+  }
+  // Verify a successful login STILL works despite failures (counter not incremented on success)
+  const { status: s2, body } = await login('admin', 'TestPass123!');
+  if (s2 === 200) {
+    record('A19b', 'Successful login works after some failures (counter clears on success)', 'PASS', '');
+  } else {
+    record('A19b', 'Successful login works after some failures', 'FAIL',
+      `status=${s2} body=${JSON.stringify(body)}`);
+  }
+}
+
+// A20: Response body does NOT contain password_hash
+async function t_A20() {
+  const { body } = await login('admin', 'TestPass123!');
+  if (JSON.stringify(body).includes('password_hash') || JSON.stringify(body).includes('$2')) {
+    record('A20', 'Login response does NOT leak password_hash', 'FAIL',
+      `CRITICAL: response contains password hash! body=${JSON.stringify(body).slice(0,200)}`);
+  } else {
+    record('A20', 'Login response does NOT leak password_hash', 'PASS', '');
+  }
+}
+
+// A21: GET on /api/auth/login → 405 Method Not Allowed
+async function t_A21() {
+  const r = await fetch(`${BASE}/api/auth/login`);
+  if (r.status === 405) {
+    record('A21', 'GET /api/auth/login → 405 (only POST allowed)', 'PASS', '');
+  } else {
+    record('A21', 'GET /api/auth/login → 405 (only POST allowed)', 'WARN',
+      `status=${r.status}`);
+  }
+}
+
+// A22: Pulse public API routes accessible without auth (by design in middleware)
+async function t_A22() {
+  const r = await fetch(`${BASE}/api/pulse/petitions`, { redirect: 'manual' });
+  record('A22', `Pulse public /api/pulse/petitions → status=${r.status} (should be accessible, not 401)`,
+    (r.status !== 401 && r.status !== 403) ? 'PASS' : 'FAIL',
+    `status=${r.status}`);
+}
+
+// A23: /api/cron accessible without auth — VERIFY this isn't a hazard
+async function t_A23() {
+  const r = await fetch(`${BASE}/api/cron`, { redirect: 'manual' });
+  const body = await r.text().catch(() => '');
+  record('A23', `/api/cron unauthenticated → status=${r.status}`,
+    'WARN',
+    `Middleware bypasses auth for /api/cron/* — any cron route that triggers side effects is exposed. body=${body.slice(0,100)}`);
+}
+
+// A24: /api/webhooks accessible without auth — same concern
+async function t_A24() {
+  const r = await fetch(`${BASE}/api/webhooks`, { redirect: 'manual' });
+  const body = await r.text().catch(() => '');
+  record('A24', `/api/webhooks unauthenticated → status=${r.status}`,
+    'WARN',
+    `Middleware bypasses auth for /api/webhooks/* — verify each webhook self-validates signatures. body=${body.slice(0,100)}`);
+}
+
+// A25: SQL injection via username field
+async function t_A25() {
+  const { status, body } = await login("' OR '1'='1", 'anything');
+  if (status === 401) {
+    record('A25', "SQL injection in username → rejected (parameterized query)", 'PASS', '');
+  } else if (status === 200) {
+    record('A25', "SQL injection in username → rejected", 'FAIL',
+      `CRITICAL: SQL injection may have succeeded! body=${JSON.stringify(body)}`);
+  } else if (status === 500) {
+    record('A25', "SQL injection in username → 500 (possible injection vector)", 'FAIL',
+      `Server errored on injection payload — check for raw SQL concatenation`);
+  } else {
+    record('A25', "SQL injection in username → rejected", 'WARN', `status=${status}`);
+  }
+}
+
+// ── Run all tests ──────────────────────────────────────────────────────────
+console.log('=== Norma Auth Cluster A — Adversarial Verification ===\n');
+console.log(`Target: ${BASE}\n`);
+
+const adminCookie = await t_A1();
+const staffCookie = await t_A2();
+const pulseCookie = await t_A3();
+await t_A4();
+await t_A5();
+await t_A6();
+await t_A7();
+await t_A8();
+await t_A9(adminCookie);
+await t_A10();
+await t_A11();
+await t_A12(staffCookie);
+await t_A13(pulseCookie);
+await t_A14(adminCookie);
+await t_A15();
+await t_A16();
+await t_A17();
+await t_A18();
+await t_A19();
+await t_A20();
+await t_A21();
+await t_A22();
+await t_A23();
+await t_A24();
+await t_A25();
+
+// ── Summary ────────────────────────────────────────────────────────────────
+console.log('\n=== SUMMARY ===');
+const passes = results.filter(r => r.status === 'PASS').length;
+const fails = results.filter(r => r.status === 'FAIL').length;
+const warns = results.filter(r => r.status === 'WARN').length;
+console.log(`PASS: ${passes}  FAIL: ${fails}  WARN: ${warns}`);
+if (fails > 0) {
+  console.log('\nFAILURES:');
+  results.filter(r => r.status === 'FAIL').forEach(r => {
+    console.log(`  [${r.id}] ${r.label}`);
+    if (r.detail) console.log(`         ${r.detail}`);
+  });
+}
diff --git a/tests/b-api-sweep-verify.mjs b/tests/b-api-sweep-verify.mjs
new file mode 100644
index 0000000..3183e8b
--- /dev/null
+++ b/tests/b-api-sweep-verify.mjs
@@ -0,0 +1,242 @@
+/**
+ * Cody the Contrarian — B-api-sweep adversarial verification
+ * Tests the isolated Norma test instance at http://127.0.0.1:7411
+ * against scratch DB sdcc_test
+ */
+
+const BASE = 'http://127.0.0.1:7411';
+const results = { confirmed: [], refuted: [], missed: [] };
+
+async function login(username, password) {
+  const res = await fetch(`${BASE}/api/auth/login`, {
+    method: 'POST',
+    headers: { 'Content-Type': 'application/json' },
+    body: JSON.stringify({ username, password }),
+  });
+  const cookie = res.headers.get('set-cookie');
+  return { ok: res.ok, body: await res.json(), cookie };
+}
+
+function cookieHeader(cookie) {
+  if (!cookie) return {};
+  const match = cookie.match(/norma-auth=[^;]+/);
+  return match ? { Cookie: match[0] } : {};
+}
+
+async function get(path, cookie, extraHeaders = {}) {
+  const res = await fetch(`${BASE}${path}`, {
+    headers: { ...cookieHeader(cookie), ...extraHeaders },
+  });
+  let body;
+  try { body = await res.json(); } catch { body = null; }
+  return { status: res.status, body };
+}
+
+async function post(path, data, cookie, extraHeaders = {}) {
+  const res = await fetch(`${BASE}${path}`, {
+    method: 'POST',
+    headers: { 'Content-Type': 'application/json', ...cookieHeader(cookie), ...extraHeaders },
+    body: JSON.stringify(data),
+  });
+  let body;
+  try { body = await res.json(); } catch { body = null; }
+  return { status: res.status, body };
+}
+
+// ─── Setup: get session cookies ───────────────────────────────────────────
+
+const admin = await login('admin', 'TestPass123!');
+const staff = await login('teststaff', 'TestPass123!');
+const pulse = await login('testpulse', 'TestPass123!');
+
+console.log('Admin login:', admin.ok, admin.body.role);
+console.log('Staff login:', staff.ok, staff.body.role);
+console.log('Pulse login:', pulse.ok, pulse.body.role);
+
+// ─── TEST 1: Unauthenticated access - redirects to login (not leaking data) ───
+
+const unauth = await get('/api/contacts', null);
+console.log('\n[T1] Unauthenticated /api/contacts:', unauth.status, '(expect 307)');
+if (unauth.status === 307) {
+  console.log('  PASS: unauthenticated redirects to login');
+} else {
+  results.missed.push(`Unauthenticated /api/contacts returned ${unauth.status} instead of redirect`);
+}
+
+// ─── TEST 2: Staff can read ALL users (including admin user details) ───────────
+
+const staffUsers = await get('/api/users', staff.cookie);
+console.log('\n[T2] Staff /api/users:', staffUsers.status, JSON.stringify(staffUsers.body));
+if (staffUsers.status === 200 && staffUsers.body?.users?.some(u => u.role === 'admin')) {
+  results.missed.push('Staff role can enumerate admin user accounts via /api/users (exposes admin username + UUID)');
+  console.log('  FINDING: Staff can enumerate admin users - username and ID exposed');
+}
+
+// ─── TEST 3: Pulse cannot access /api/users ───────────────────────────────────
+
+const pulseUsers = await get('/api/users', pulse.cookie);
+console.log('\n[T3] Pulse /api/users:', pulseUsers.status, '(expect 403)');
+if (pulseUsers.status === 403) {
+  console.log('  PASS: Pulse cannot list users');
+}
+
+// ─── TEST 4: Staff cannot access admin-only tier-credentials ─────────────────
+
+const staffCreds = await get('/api/settings/tier-credentials', staff.cookie);
+console.log('\n[T4] Staff /api/settings/tier-credentials:', staffCreds.status, '(expect 403)');
+if (staffCreds.status !== 403) {
+  results.missed.push(`Staff accessed tier-credentials settings (got ${staffCreds.status})`);
+}
+
+// ─── TEST 5: Staff cannot access admin impersonation ─────────────────────────
+
+const staffImpersonate = await post('/api/admin/impersonate', 
+  { userId: 'de9380e9-ac0a-4662-8246-b81c898dca85' }, staff.cookie);
+console.log('\n[T5] Staff /api/admin/impersonate:', staffImpersonate.status, '(expect 403)');
+if (staffImpersonate.status !== 403) {
+  results.missed.push(`Staff can impersonate admin (got ${staffImpersonate.status})`);
+}
+
+// ─── TEST 6: ingest-news FK constraint failure (248/248 items fail) ───────────
+
+const ingestNews = await post('/api/cron/ingest-news', {}, admin.cookie);
+console.log('\n[T6] /api/cron/ingest-news:');
+console.log('  success:', ingestNews.body?.success);
+console.log('  inserted:', ingestNews.body?.inserted);
+console.log('  error count:', ingestNews.body?.errors?.length);
+if (ingestNews.body?.errors?.length > 0) {
+  const firstErr = ingestNews.body.errors[0];
+  const hasDbLeak = firstErr.includes('news_items') || firstErr.includes('constraint') || firstErr.includes('org_id_fkey');
+  results.missed.push(`ingest-news: 100% of news items fail to insert (FK constraint news_items_org_id_fkey - default org UUID not seeded in test DB). Error leaks internal table name: ${hasDbLeak}`);
+  console.log('  FINDING: ALL items fail with FK constraint error. DB schema drift.');
+  console.log('  Error leaks internal table/constraint names:', hasDbLeak);
+}
+
+// ─── TEST 7: Error messages leak DB internals via audit log ──────────────────
+
+const auditLog = await get('/api/audit', staff.cookie);
+console.log('\n[T7] /api/audit (staff):');
+if (auditLog.status === 200 && auditLog.body?.events?.length > 0) {
+  const leaksDbDetails = JSON.stringify(auditLog.body).includes('foreign key constraint');
+  console.log('  Events count:', auditLog.body.events.length);
+  console.log('  Leaks FK constraint details in audit log accessible to staff:', leaksDbDetails);
+  if (leaksDbDetails) {
+    results.missed.push('Audit log (accessible to staff) surfaces raw PostgreSQL FK error messages including table names and constraint names');
+  }
+}
+
+// ─── TEST 8: Gmail-sync cron accessible via admin session (not just cron secret) ─
+
+const gmailSync = await get('/api/cron/gmail-sync', admin.cookie);
+console.log('\n[T8] /api/cron/gmail-sync with admin session:');
+console.log('  Status:', gmailSync.status);
+if (gmailSync.status === 200) {
+  results.missed.push('Cron endpoint /api/cron/gmail-sync accepts admin session cookie (not cron-secret only) — browser-authenticated admin can trigger gmail sync');
+  console.log('  FINDING: Admin browser session can trigger cron gmail-sync');
+}
+
+// ─── TEST 9: Staff can trigger dispatch to social platforms ──────────────────
+
+const dispatch = await post('/api/dispatch', {
+  content_type: 'petition',
+  content_id: '00000000-0000-0000-0000-000000000001',
+  platform: 'twitter'
+}, staff.cookie);
+console.log('\n[T9] Staff /api/dispatch:');
+console.log('  Status:', dispatch.status, 'Body:', JSON.stringify(dispatch.body));
+if (dispatch.status === 200) {
+  results.missed.push('Staff can POST to /api/dispatch to trigger social platform dispatch (no admin-only gate)');
+} else if (dispatch.status === 404 && dispatch.body?.error === 'petition not found') {
+  console.log('  INFO: Staff CAN reach dispatch handler (returned 404 on missing content, not 403)');
+  results.missed.push('Staff can reach /api/dispatch handler (returns 404 not 403) - authorization check passes for staff on this write endpoint');
+}
+
+// ─── TEST 10: Password rehash silently updates DB - timing oracle ─────────────
+
+// Try legacy SHA-256 format login - can't easily test without knowing a SHA256 hash
+// But check that the upgrade flow is gated properly (conceptual)
+console.log('\n[T10] Password rehash flow: requires SHA-256 hash in DB - cannot test without seeded legacy user');
+
+// ─── TEST 11: Session token - no expiry check on server? ─────────────────────
+
+// The session has a 24h max age - but let's check if the expiry is verified server-side
+const authCode = admin.body;
+const rawCookie = admin.cookie;
+console.log('\n[T11] Session token analysis:');
+const cookieMatch = rawCookie?.match(/norma-auth=([^;]+)/);
+if (cookieMatch) {
+  const token = decodeURIComponent(cookieMatch[1]);
+  const [payloadB64] = token.split('.');
+  try {
+    const payload = JSON.parse(Buffer.from(payloadB64, 'base64').toString());
+    console.log('  Payload:', JSON.stringify(payload));
+    // Check if the timestamp is verified
+    const ageMs = Date.now() - parseInt(payload.t);
+    console.log('  Token age (ms):', ageMs, '(within 24h window:', ageMs < 86400000, ')');
+  } catch(e) {
+    console.log('  Cannot decode token:', e.message);
+  }
+}
+
+// ─── TEST 12: X-Org-Id header forgery by staff ───────────────────────────────
+
+const staffFakeOrg = await get('/api/sessions', staff.cookie, { 'X-Org-Id': '11111111-1111-1111-1111-111111111111' });
+console.log('\n[T12] Staff with forged X-Org-Id:');
+console.log('  Status:', staffFakeOrg.status);
+// Staff gets auth.orgId from session (null), not header - so staff cannot forge org scope
+// But admin uses getOrgId(request) which reads the HEADER
+const adminFakeOrg = await get('/api/sessions', admin.cookie, { 'X-Org-Id': '11111111-1111-1111-1111-111111111111' });
+console.log('  Admin with forged X-Org-Id status:', adminFakeOrg.status);
+if (adminFakeOrg.status === 200) {
+  console.log('  INFO: Admin can scope queries to any org via X-Org-Id header (by design for admin)');
+}
+
+// ─── TEST 13: Brute force rate limit - does it actually work? ─────────────────
+
+console.log('\n[T13] Brute force rate limit test (3 bad logins):');
+for (let i = 0; i < 3; i++) {
+  const r = await login('admin', 'wrongpassword');
+  console.log(`  Attempt ${i+1}: status implied by body:`, r.body?.error || 'ok');
+}
+// After 10 failures, should get 429 - not testing full 10 to avoid polluting state
+
+// ─── TEST 14: Generate endpoint - staff can trigger Gemini (paid API) ─────────
+
+const generate = await post('/api/generate', { action: 'daily' }, staff.cookie);
+console.log('\n[T14] Staff POST /api/generate:');
+console.log('  Status:', generate.status, 'Error:', generate.body?.error);
+if (generate.status === 200) {
+  results.missed.push('Staff can trigger AI generation (Gemini paid API) via /api/generate without admin-only gate');
+}
+
+// ─── Final summary ─────────────────────────────────────────────────────────────
+
+console.log('\n\n=== ADVERSARIAL VERIFICATION SUMMARY ===');
+console.log('Confirmed defects found:', results.missed.length);
+results.missed.forEach((m, i) => console.log(`  ${i+1}. ${m}`));
+
+/*
+ * FINAL FINDINGS (manually confirmed via curl, reproduced 2026-08-05):
+ *
+ * CONFIRMED DEFECTS:
+ * 1. ingest-news 100% FK failure: test DB news_items.org_id DEFAULT (53e2a0e7) 
+ *    references nonprofit_accounts UUID that does NOT exist in sdcc_test DB.
+ *    247/247 inserts fail. success:true is lying.
+ *
+ * 2. Raw DB error leakage: "insert or update on table news_items violates foreign key
+ *    constraint news_items_org_id_fkey" returned directly in API response body and 
+ *    stored in audit log (accessible to staff role).
+ *
+ * 3. Staff enumerates admin accounts: /api/users allows admin+staff roles, exposing
+ *    admin username + UUID. Code comment says "Admin-only" but code says otherwise.
+ *
+ * 4. Legacy token backward compat hardcodes admin role (lib/auth.ts:157).
+ *    Not externally forgeable (requires SESSION_SECRET) but latent risk.
+ *
+ * 5. Dispatch allows staff to trigger social platform dispatch (admin-staff vs 
+ *    social/publish which is admin-only) - inconsistent permission model.
+ *
+ * 6. IP-based lockout affects all users from shared IP (DoS vector).
+ *
+ * WORKER REPORTED: null (no findings at all) - that's a complete miss.
+ */
diff --git a/tests/cluster-c-ui-tiers.mjs b/tests/cluster-c-ui-tiers.mjs
new file mode 100644
index 0000000..9061347
--- /dev/null
+++ b/tests/cluster-c-ui-tiers.mjs
@@ -0,0 +1,244 @@
+/**
+ * Cluster C: UI Tiers adversarial test suite
+ * Tests auth gates, role separation, forged token bypass, pulse isolation, etc.
+ */
+
+const BASE = 'http://127.0.0.1:7411';
+let passed = 0;
+let failed = 0;
+const findings = [];
+
+async function login(username, password) {
+  const res = await fetch(`${BASE}/api/auth/login`, {
+    method: 'POST',
+    headers: { 'Content-Type': 'application/json' },
+    body: JSON.stringify({ username, password }),
+  });
+  const setCookie = res.headers.get('set-cookie') || '';
+  const match = setCookie.match(/norma-auth=([^;]+)/);
+  const token = match ? match[1] : null;
+  const body = await res.json();
+  return { status: res.status, token, body };
+}
+
+function cookieHeader(token) {
+  return { Cookie: `norma-auth=${token}` };
+}
+
+async function req(method, path, token, data) {
+  const headers = { 'Content-Type': 'application/json', ...(token ? cookieHeader(token) : {}) };
+  const opts = { method, headers };
+  if (data !== undefined) opts.body = JSON.stringify(data);
+  const res = await fetch(`${BASE}${path}`, opts);
+  const text = await res.text();
+  let body;
+  try { body = JSON.parse(text); } catch { body = text; }
+  return { status: res.status, body };
+}
+
+async function get(path, token) { return req('GET', path, token); }
+async function post(path, data, token) { return req('POST', path, token, data); }
+
+function assert(label, condition, detail = '') {
+  if (condition) {
+    console.log(`  PASS: ${label}`);
+    passed++;
+  } else {
+    console.log(`  FAIL: ${label}${detail ? ' | ' + detail : ''}`);
+    failed++;
+    findings.push({ label, detail });
+  }
+}
+
+// ─── Setup: get real tokens ─────────────────────────────────────────────────
+console.log('\n=== SETUP: LOGIN ===');
+const adminL = await login('admin', 'TestPass123!');
+const staffL = await login('teststaff', 'TestPass123!');
+const pulseL = await login('testpulse', 'TestPass123!');
+
+assert('admin login yields token', !!adminL.token, JSON.stringify(adminL.body));
+assert('staff login yields token', !!staffL.token, JSON.stringify(staffL.body));
+assert('pulse login yields token', !!pulseL.token, JSON.stringify(pulseL.body));
+
+const adminToken = adminL.token;
+const staffToken = staffL.token;
+const pulseToken = pulseL.token;
+
+// ─── Test 1: Unauthenticated access blocked ──────────────────────────────────
+console.log('\n=== TEST 1: UNAUTHENTICATED ACCESS TO PROTECTED ROUTES ===');
+const sensitiveRoutes = [
+  '/api/users', '/api/audit', '/api/sessions', '/api/email-sends',
+  '/api/agents', '/api/admin/permissions', '/api/contacts',
+  '/api/outreach-templates', '/api/drafts',
+];
+for (const route of sensitiveRoutes) {
+  const r = await get(route, null);
+  assert(`${route} blocks unauthenticated (401/403/redirect)`,
+    r.status === 401 || r.status === 403 || r.status === 307 || r.status === 302,
+    `got ${r.status}: ${JSON.stringify(r.body).slice(0, 100)}`
+  );
+}
+
+// ─── Test 2: Forged token bypass (structural-only middleware) ────────────────
+console.log('\n=== TEST 2: FORGED TOKEN — MIDDLEWARE STRUCTURAL BYPASS ===');
+// Middleware only checks structure + age, NOT crypto. Route handlers call verifyAuth.
+// If a route handler forgets verifyAuth, forged token gets through.
+const fakePayload = JSON.stringify({ u: 'admin', r: 'admin', o: null, t: Date.now().toString() });
+const fakeB64 = Buffer.from(fakePayload).toString('base64');
+const fakeToken = `${fakeB64}.deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef`;
+
+const forgedAdminResp = await get('/api/users', fakeToken);
+assert(
+  'Forged token with bad HMAC rejected by route handler (401/403)',
+  forgedAdminResp.status === 401 || forgedAdminResp.status === 403,
+  `got ${forgedAdminResp.status}: ${JSON.stringify(forgedAdminResp.body).slice(0, 200)}`
+);
+
+// Page routes: middleware is the ONLY gate — does it check crypto or just structure?
+// A structurally valid forged token will pass the middleware check (it only decodes + checks age)
+// This means any page route that trusts the session from middleware alone is bypassable.
+const forgedPageResp = await get('/supervisor', fakeToken);
+assert(
+  'Forged token blocked from page routes too (not 200 with real content)',
+  forgedPageResp.status !== 200,
+  `got ${forgedPageResp.status}: ${String(forgedPageResp.body).slice(0, 100)}`
+);
+
+// ─── Test 3: Pulse role cannot access admin/staff API endpoints ──────────────
+console.log('\n=== TEST 3: PULSE ROLE PRIVILEGE ESCALATION ===');
+const adminOnlyRoutes = [
+  '/api/users', '/api/audit', '/api/admin/permissions',
+  '/api/sessions', '/api/email-sends', '/api/agents',
+  '/api/outreach-templates', '/api/clients',
+];
+for (const route of adminOnlyRoutes) {
+  const r = await get(route, pulseToken);
+  assert(
+    `pulse blocked from ${route} (403/401)`,
+    r.status === 401 || r.status === 403,
+    `got ${r.status}: ${JSON.stringify(r.body).slice(0, 150)}`
+  );
+}
+
+// ─── Test 4: Staff cannot access admin-only endpoints ────────────────────────
+console.log('\n=== TEST 4: STAFF ROLE BOUNDARY ===');
+const staffPermResp = await get('/api/admin/permissions', staffToken);
+assert(
+  'staff cannot GET /api/admin/permissions (403)',
+  staffPermResp.status === 403,
+  `got ${staffPermResp.status}: ${JSON.stringify(staffPermResp.body).slice(0, 100)}`
+);
+
+const staffImpResp = await post('/api/admin/impersonate', { username: 'teststaff' }, staffToken);
+assert(
+  'staff cannot POST /api/admin/impersonate (403)',
+  staffImpResp.status === 403,
+  `got ${staffImpResp.status}: ${JSON.stringify(staffImpResp.body).slice(0, 100)}`
+);
+
+// Staff CAN access audit?
+const staffAuditResp = await get('/api/audit', staffToken);
+assert(
+  'staff can access /api/audit (200) — allowed per requireRole(admin,staff)',
+  staffAuditResp.status === 200,
+  `got ${staffAuditResp.status}: ${JSON.stringify(staffAuditResp.body).slice(0, 100)}`
+);
+
+// ─── Test 5: Admin impersonate flow ──────────────────────────────────────────
+console.log('\n=== TEST 5: IMPERSONATION FLOW ===');
+const impResp = await post('/api/admin/impersonate', { username: 'teststaff' }, adminToken);
+assert(
+  'admin can impersonate another user (200)',
+  impResp.status === 200 && impResp.body?.success === true,
+  `got ${impResp.status}: ${JSON.stringify(impResp.body).slice(0, 150)}`
+);
+
+// ─── Test 6: Cron endpoints require auth ─────────────────────────────────────
+console.log('\n=== TEST 6: CRON ROUTES WITHOUT SECRET ===');
+const cronGET = await get('/api/cron/gmail-sync', null);
+assert(
+  '/api/cron/gmail-sync unauthenticated returns 401',
+  cronGET.status === 401,
+  `got ${cronGET.status}: ${JSON.stringify(cronGET.body).slice(0, 100)}`
+);
+
+const cronPOST = await post('/api/cron/daily', {}, null);
+assert(
+  '/api/cron/daily unauthenticated returns 401',
+  cronPOST.status === 401,
+  `got ${cronPOST.status}: ${JSON.stringify(cronPOST.body).slice(0, 100)}`
+);
+
+// ─── Test 7: Pulse public API accessible without auth ────────────────────────
+console.log('\n=== TEST 7: PULSE PUBLIC API WITHOUT AUTH ===');
+const pulsePublicRoutes = ['/api/pulse/petitions', '/api/pulse/stats', '/api/petitions'];
+for (const route of pulsePublicRoutes) {
+  const r = await get(route, null);
+  assert(
+    `${route} accessible without auth (not 401/403)`,
+    r.status !== 401 && r.status !== 403,
+    `got ${r.status}`
+  );
+}
+
+// ─── Test 8: Login credential rejection ──────────────────────────────────────
+console.log('\n=== TEST 8: CREDENTIAL REJECTION ===');
+const badPwd = await login('admin', 'wrongpassword');
+assert('bad password returns 401', badPwd.status === 401, `got ${badPwd.status}`);
+assert('bad password returns no token', !badPwd.token, `token: ${badPwd.token}`);
+
+const badUser = await login('nonexistent', 'TestPass123!');
+assert('nonexistent user returns 401', badUser.status === 401, `got ${badUser.status}`);
+
+// ─── Test 9: v1 health route info leak ───────────────────────────────────────
+console.log('\n=== TEST 9: v1/health OPEN ROUTE ===');
+const v1Health = await get('/api/v1/health', null);
+assert(
+  '/api/v1/health accessible without auth',
+  v1Health.status === 200,
+  `got ${v1Health.status}: ${JSON.stringify(v1Health.body).slice(0, 100)}`
+);
+const healthStr = JSON.stringify(v1Health.body);
+assert(
+  'v1/health does not leak DB credentials or secret env vars',
+  !healthStr.includes('password') && !healthStr.includes('secret') && !healthStr.includes('SESSION_SECRET'),
+  `body: ${healthStr.slice(0, 300)}`
+);
+
+// ─── Test 10: Pulse cannot POST to create sessions ───────────────────────────
+console.log('\n=== TEST 10: PULSE CANNOT WRITE ADMIN DATA ===');
+const pulsePost = await post('/api/sessions', {}, pulseToken);
+assert(
+  'pulse cannot POST /api/sessions (403/401)',
+  pulsePost.status === 401 || pulsePost.status === 403,
+  `got ${pulsePost.status}: ${JSON.stringify(pulsePost.body).slice(0, 150)}`
+);
+
+const pulseEmail = await post('/api/email-sends', { subject: 'test', body_html: '<p>x</p>' }, pulseToken);
+assert(
+  'pulse cannot POST /api/email-sends (403/401)',
+  pulseEmail.status === 401 || pulseEmail.status === 403,
+  `got ${pulseEmail.status}: ${JSON.stringify(pulseEmail.body).slice(0, 150)}`
+);
+
+// ─── Test 11: Admin can read audit (smoke test data not leaking) ──────────────
+console.log('\n=== TEST 11: AUDIT LOG ACCESS ===');
+const auditResp = await get('/api/audit', adminToken);
+assert('admin can GET /api/audit (200)', auditResp.status === 200, `got ${auditResp.status}`);
+
+// ─── Test 12: Outreach generate endpoint - pulse blocked ─────────────────────
+console.log('\n=== TEST 12: OUTREACH GENERATE — ROLE GATE ===');
+const pulseGenerate = await post('/api/outreach/generate', { target_name: 'Test' }, pulseToken);
+assert(
+  'pulse cannot POST /api/outreach/generate (403/401)',
+  pulseGenerate.status === 401 || pulseGenerate.status === 403,
+  `got ${pulseGenerate.status}: ${JSON.stringify(pulseGenerate.body).slice(0, 150)}`
+);
+
+// ─── SUMMARY ─────────────────────────────────────────────────────────────────
+console.log(`\n${'='.repeat(60)}`);
+console.log(`RESULTS: ${passed} passed, ${failed} failed`);
+if (findings.length > 0) {
+  console.log('\nFAILED ASSERTIONS:');
+  findings.forEach(f => console.log(`  - ${f.label}: ${f.detail}`));
+}
diff --git a/tests/cluster-d-hazards.mjs b/tests/cluster-d-hazards.mjs
new file mode 100644
index 0000000..6dc9358
--- /dev/null
+++ b/tests/cluster-d-hazards.mjs
@@ -0,0 +1,177 @@
+/**
+ * Cluster D — Security Hazard Tests
+ * Target: http://127.0.0.1:7411 (isolated test instance)
+ * Cody the Contrarian adversarial verification run
+ */
+
+const BASE = 'http://127.0.0.1:7411';
+
+let passed = 0, failed = 0, total = 0;
+
+function result(name, ok, detail) {
+  total++;
+  if (ok) { passed++; console.log(`  PASS  ${name}`); }
+  else { failed++; console.log(`  FAIL  ${name}${detail ? ': ' + detail : ''}`); }
+}
+
+async function login(username, password) {
+  const r = await fetch(`${BASE}/api/auth/login`, {
+    method: 'POST',
+    headers: { 'Content-Type': 'application/json' },
+    body: JSON.stringify({ username, password }),
+    redirect: 'manual',
+  });
+  const setCookie = r.headers.get('set-cookie') || '';
+  const match = setCookie.match(/norma-auth=([^;]+)/);
+  return { ok: r.ok, status: r.status, cookie: match ? match[1] : null };
+}
+
+function jar(cookie) {
+  return { Cookie: `norma-auth=${cookie}` };
+}
+
+async function get(path, cookie) {
+  return fetch(`${BASE}${path}`, {
+    headers: cookie ? jar(cookie) : {},
+    redirect: 'manual',
+  });
+}
+
+async function post(path, body, cookie) {
+  return fetch(`${BASE}${path}`, {
+    method: 'POST',
+    headers: { 'Content-Type': 'application/json', ...(cookie ? jar(cookie) : {}) },
+    body: JSON.stringify(body),
+    redirect: 'manual',
+  });
+}
+
+// ─── Setup ────────────────────────────────────────────────────────────────────
+console.log('\n=== Cluster D Hazard Tests ===\n');
+
+const admin = await login('admin', 'TestPass123!');
+const staff = await login('teststaff', 'TestPass123!');
+const pulse = await login('testpulse', 'TestPass123!');
+
+result('Admin login', admin.ok && admin.cookie !== null, `status=${admin.status}`);
+result('Staff login', staff.ok && staff.cookie !== null, `status=${staff.status}`);
+result('Pulse login', pulse.ok && pulse.cookie !== null, `status=${pulse.status}`);
+
+// ─── D1: Token forge (bad HMAC) blocked by route handlers ────────────────────
+console.log('\n--- D1: Forged HMAC token blocked ---');
+const fakePayload = JSON.stringify({ u: 'hacker', r: 'admin', o: null, t: Date.now().toString() });
+const fakeB64 = Buffer.from(fakePayload).toString('base64');
+const fakeSig = 'deadbeef'.repeat(8);
+const forgedToken = `${fakeB64}.${fakeSig}`;
+
+const r_forged = await get('/api/users', forgedToken);
+result('Forged HMAC token blocked by /api/users', r_forged.status === 401, `got ${r_forged.status}`);
+
+// ─── D2: Role-based access control ───────────────────────────────────────────
+console.log('\n--- D2: Role access control ---');
+
+const r_pulse_users = await get('/api/users', pulse.cookie);
+result('Pulse blocked from /api/users (admin+staff only)', r_pulse_users.status === 403, `got ${r_pulse_users.status}`);
+
+const r_staff_users = await get('/api/users', staff.cookie);
+result('Staff can access /api/users', r_staff_users.status === 200, `got ${r_staff_users.status}`);
+
+const r_pulse_settings = await get('/api/settings/credentials', pulse.cookie);
+result('Pulse blocked from /api/settings/credentials', r_pulse_settings.status === 403, `got ${r_pulse_settings.status}`);
+
+const r_staff_settings = await get('/api/settings/credentials', staff.cookie);
+result('Staff blocked from /api/settings/credentials (admin only)', r_staff_settings.status === 403, `got ${r_staff_settings.status}`);
+
+// ─── D3: Cron routes guarded despite middleware bypass ────────────────────────
+console.log('\n--- D3: Cron routes guarded ---');
+
+const r_cron_noauth = await post('/api/cron/daily', {});
+result('Cron daily blocked without auth', r_cron_noauth.status === 401, `got ${r_cron_noauth.status}`);
+
+const r_cron_pulse = await post('/api/cron/daily', {}, pulse.cookie);
+result('Cron daily blocked for pulse role', r_cron_pulse.status === 401, `got ${r_cron_pulse.status}`);
+
+// ─── D4: Missing auth on social routes ───────────────────────────────────────
+console.log('\n--- D4: Social route auth gaps (HAZARDS) ---');
+
+// social/posts/approve — no requireRole
+const r_approve_pulse = await post('/api/social/posts/approve', {
+  post_id: '00000000-0000-0000-0000-000000000001',
+  action: 'approve',
+  approver: 'pulse_hacker',
+}, pulse.cookie);
+// 404 = got to DB. 403 = properly rejected. 400 = validation only.
+const approve_gated = r_approve_pulse.status === 403 || r_approve_pulse.status === 401;
+result('social/posts/approve gated by role', approve_gated, `got ${r_approve_pulse.status} — any auth user reaches DB`);
+
+// social/posts/pending — no requireRole
+const r_pending_pulse = await get('/api/social/posts/pending', pulse.cookie);
+result('social/posts/pending gated from pulse', r_pending_pulse.status === 403 || r_pending_pulse.status === 401, `got ${r_pending_pulse.status}`);
+
+// social/analytics/dashboard — no requireRole
+const r_analytics_pulse = await get('/api/social/analytics/dashboard', pulse.cookie);
+result('social/analytics/dashboard gated from pulse', r_analytics_pulse.status === 403 || r_analytics_pulse.status === 401, `got ${r_analytics_pulse.status}`);
+
+// social/bulk-schedule — no requireRole — confirmed write to DB
+const r_bulk_pulse = await post('/api/social/bulk-schedule', {
+  posts: [{ body: 'cody-test post' }],
+  target_platforms: ['twitter'],
+  distribution: 'even',
+  start_at: new Date(Date.now() + 3600000).toISOString(),
+  end_at: new Date(Date.now() + 7200000).toISOString(),
+}, pulse.cookie);
+result('social/bulk-schedule blocked from pulse (no auth)', r_bulk_pulse.status === 403 || r_bulk_pulse.status === 401,
+  `got ${r_bulk_pulse.status} — pulse created a post if 200`);
+
+// social/ai-image — no requireRole — calls paid Gemini API
+const r_ai_image_pulse = await post('/api/social/ai-image', { prompt: 'test' }, pulse.cookie);
+result('social/ai-image blocked from pulse (paid API, no auth)', 
+  r_ai_image_pulse.status === 403 || r_ai_image_pulse.status === 401,
+  `got ${r_ai_image_pulse.status}`);
+
+// ─── D5: Registry dual-decoder auth bypass ────────────────────────────────────
+console.log('\n--- D5: Registry dual-decoder auth bypass (CRITICAL) ---');
+
+// Token where middleware checks parts[0] (valid timestamp JSON), 
+// but registry reads parts[1] (our forged role claim)
+const mwPayload = Buffer.from(JSON.stringify({ t: Date.now() })).toString('base64');
+const rolePayload = Buffer.from(JSON.stringify({ role: 'admin', user: 'cody-test' })).toString('base64url');
+const hybridToken = `${mwPayload}.${rolePayload}`;
+
+const r_registry_hybrid = await get('/api/registry', hybridToken);
+const registryBody = await r_registry_hybrid.text();
+const registryHacked = r_registry_hybrid.status === 200 && registryBody.includes('SESSION_SECRET');
+result('Registry blocks dual-decoder hybrid token', 
+  r_registry_hybrid.status === 403,
+  `got ${r_registry_hybrid.status} — body length ${registryBody.length} chars, SESSION_SECRET exposed: ${registryBody.includes('SESSION_SECRET')}`);
+
+// ─── D6: v1 API not auth-bypassed by middleware ───────────────────────────────
+console.log('\n--- D6: v1 API requires API key (not cookie) ---');
+
+const r_v1_noauth = await get('/api/v1/health');
+result('v1/health blocks unauthenticated request', r_v1_noauth.status === 401, `got ${r_v1_noauth.status}`);
+
+const r_v1_cookie = await get('/api/v1/health', admin.cookie);
+result('v1/health rejects session cookie (needs API key not cookie)', r_v1_cookie.status === 401, `got ${r_v1_cookie.status}`);
+
+// ─── D7: Unauthenticated access to admin-only routes ─────────────────────────
+console.log('\n--- D7: Unauthenticated access blocked ---');
+
+const r_drafts_noauth = await get('/api/drafts');
+result('Unauthenticated /api/drafts redirects', r_drafts_noauth.status === 307 || r_drafts_noauth.status === 401, `got ${r_drafts_noauth.status}`);
+
+// ─── D8: Credential reveal param check ───────────────────────────────────────
+console.log('\n--- D8: Credential reveal ---');
+
+const r_reveal_admin = await get('/api/settings/credentials?reveal=smtp', admin.cookie);
+const revealData = await r_reveal_admin.json();
+result('Admin can access credential reveal (returns masked when empty)', r_reveal_admin.status === 200, `got ${r_reveal_admin.status}`);
+// Check that if there were real creds they'd be masked vs. revealed correctly
+result('Credential endpoint returns credentials and has_values keys', 
+  revealData && 'credentials' in revealData && 'has_values' in revealData,
+  `missing keys: ${JSON.stringify(Object.keys(revealData || {}))}`);
+
+// ─── Summary ──────────────────────────────────────────────────────────────────
+console.log(`\n=== Results: ${passed}/${total} passed, ${failed} failed ===\n`);
+
+if (failed > 0) process.exit(1);
diff --git a/tests/cluster-e-gotchas.mjs b/tests/cluster-e-gotchas.mjs
new file mode 100644
index 0000000..ef4a528
--- /dev/null
+++ b/tests/cluster-e-gotchas.mjs
@@ -0,0 +1,270 @@
+/**
+ * Norma Cluster E — Gotchas Adversarial Verification
+ * Run: node tests/cluster-e-gotchas.mjs
+ * Target: http://127.0.0.1:7411
+ */
+
+const BASE = 'http://127.0.0.1:7411';
+let results = [];
+const PASS = '\x1b[32mPASS\x1b[0m';
+const FAIL = '\x1b[31mFAIL\x1b[0m';
+const WARN = '\x1b[33mWARN\x1b[0m';
+
+function record(id, label, status, detail) {
+  results.push({ id, label, status, detail });
+  const tag = status === 'PASS' ? PASS : status === 'FAIL' ? FAIL : WARN;
+  console.log(`[${tag}] ${id}: ${label}`);
+  if (detail) console.log(`       ${detail}`);
+}
+
+async function login(username, password) {
+  const r = await fetch(`${BASE}/api/auth/login`, {
+    method: 'POST',
+    headers: { 'Content-Type': 'application/json' },
+    body: JSON.stringify({ username, password }),
+  });
+  const setCookie = r.headers.get('set-cookie') || '';
+  const token = (setCookie.match(/norma-auth=([^;]+)/) || [])[1] || '';
+  const body = await r.json().catch(() => ({}));
+  return { status: r.status, token, body };
+}
+
+// ── E1: Middleware sends API callers to HTML /login page (not JSON 401) ──
+async function t_E1() {
+  const r = await fetch(`${BASE}/api/contacts`, { redirect: 'manual' });
+  if (r.status === 307) {
+    const loc = r.headers.get('location');
+    // The API caller gets a 307 redirect to /login HTML — not a JSON error
+    record('E1', 'Unauthenticated API calls get 307 redirect (HTML) not JSON 401', 'FAIL',
+      `status=307 location=${loc} — API clients get HTML not machine-readable 401`);
+  } else if (r.status === 401) {
+    record('E1', 'Unauthenticated API calls get JSON 401', 'PASS', `status=401`);
+  } else {
+    record('E1', 'Unauthenticated API calls get 307 redirect (HTML) not JSON 401', 'WARN',
+      `status=${r.status} (unexpected)`);
+  }
+}
+
+// ── E2: DELETE /api/admin/impersonate has no role check — any auth user can call it ──
+async function t_E2() {
+  const { token: staffToken } = await login('teststaff', 'TestPass123!');
+  const { token: adminToken } = await login('admin', 'TestPass123!');
+
+  // staff user calls DELETE with admin's token as the norma-imp-by cookie
+  const r = await fetch(`${BASE}/api/admin/impersonate`, {
+    method: 'DELETE',
+    headers: {
+      'Cookie': `norma-auth=${staffToken}; norma-imp-by=${adminToken}`,
+    },
+    redirect: 'manual',
+  });
+  const body = await r.json().catch(() => ({}));
+  const newCookie = r.headers.get('set-cookie') || '';
+  const grantedToken = (newCookie.match(/norma-auth=([^;]+)/) || [])[1] || '';
+
+  if (r.status === 200 && body.success && grantedToken === adminToken) {
+    // Full escalation: staff got admin's token back via norma-imp-by
+    // Now verify the token actually authenticates as admin
+    const sessionR = await fetch(`${BASE}/api/auth/session`, {
+      headers: { 'Cookie': `norma-auth=${grantedToken}` },
+    });
+    const session = await sessionR.json().catch(() => ({}));
+    if (session.role === 'admin') {
+      record('E2', 'DELETE /api/admin/impersonate — no role check (staff escalates to admin)', 'FAIL',
+        `staff set norma-imp-by=${adminToken.slice(0,20)}… and received admin session back. role=${session.role}`);
+    } else {
+      record('E2', 'DELETE /api/admin/impersonate — no role check (staff escalates to admin)', 'FAIL',
+        `staff got 200+set-cookie but role check failed: role=${session.role}`);
+    }
+  } else if (r.status === 403) {
+    record('E2', 'DELETE /api/admin/impersonate — no role check (staff escalates to admin)', 'PASS',
+      'Got 403 — role is checked');
+  } else {
+    record('E2', 'DELETE /api/admin/impersonate — no role check (staff escalates to admin)', 'WARN',
+      `Unexpected: status=${r.status} body=${JSON.stringify(body)}`);
+  }
+}
+
+// ── E3: Pulse user can also escalate via DELETE impersonate ──
+async function t_E3() {
+  const { token: pulseToken } = await login('testpulse', 'TestPass123!');
+  const { token: adminToken } = await login('admin', 'TestPass123!');
+
+  const r = await fetch(`${BASE}/api/admin/impersonate`, {
+    method: 'DELETE',
+    headers: {
+      'Cookie': `norma-auth=${pulseToken}; norma-imp-by=${adminToken}`,
+    },
+    redirect: 'manual',
+  });
+  const body = await r.json().catch(() => ({}));
+  if (r.status === 200 && body.success) {
+    record('E3', 'Pulse user escalates to admin via DELETE impersonate', 'FAIL',
+      `pulse got 200+success — any authenticated role can steal admin session`);
+  } else if (r.status === 403) {
+    record('E3', 'Pulse user escalates to admin via DELETE impersonate', 'PASS', 'Got 403');
+  } else {
+    record('E3', 'Pulse user escalates to admin via DELETE impersonate', 'WARN',
+      `status=${r.status}`);
+  }
+}
+
+// ── E4: DELETE impersonate sets unvalidated token from imp-by cookie (no crypto check) ──
+async function t_E4() {
+  const { token: staffToken } = await login('teststaff', 'TestPass123!');
+
+  // Send a garbage string as norma-imp-by
+  const r = await fetch(`${BASE}/api/admin/impersonate`, {
+    method: 'DELETE',
+    headers: {
+      'Cookie': `norma-auth=${staffToken}; norma-imp-by=GARBAGE_NOT_A_VALID_TOKEN`,
+    },
+    redirect: 'manual',
+  });
+  const newCookie = r.headers.get('set-cookie') || '';
+  const grantedToken = (newCookie.match(/norma-auth=([^;]+)/) || [])[1] || '';
+
+  if (r.status === 200 && grantedToken === 'GARBAGE_NOT_A_VALID_TOKEN') {
+    record('E4', 'DELETE impersonate blindly sets imp-by content without crypto validation', 'FAIL',
+      `Server set-cookie: norma-auth=GARBAGE — no signature/format check on imp-by content`);
+  } else if (r.status === 400 || r.status === 403) {
+    record('E4', 'DELETE impersonate validates imp-by cookie content', 'PASS', `status=${r.status}`);
+  } else {
+    record('E4', 'DELETE impersonate blindly sets imp-by content without crypto validation', 'WARN',
+      `status=${r.status} cookie=${newCookie.slice(0,80)}`);
+  }
+}
+
+// ── E5: Public petition sign endpoint has no rate limiting ──
+async function t_E5() {
+  // Fire 15 POST requests to the sign endpoint with different emails from same IP
+  // There are no petitions in the test DB, but the rate limiter should trigger
+  // before the 404 if it exists
+  const testId = '00000000-0000-0000-0000-000000000001';
+  const requests = [];
+  for (let i = 0; i < 15; i++) {
+    requests.push(fetch(`${BASE}/api/pulse/petitions/${testId}/sign`, {
+      method: 'POST',
+      headers: { 'Content-Type': 'application/json' },
+      body: JSON.stringify({ name: `Test${i}`, email: `flood${i}@test.com` }),
+    }));
+  }
+  const responses = await Promise.all(requests);
+  const statuses = responses.map(r => r.status);
+  const has429 = statuses.some(s => s === 429);
+  if (has429) {
+    record('E5', 'Public petition sign endpoint rate-limits flood', 'PASS',
+      `Got 429 in: ${statuses.join(',')}`);
+  } else {
+    // All 404 = no rate limit applied (all hit the DB check)
+    record('E5', 'Public petition sign endpoint has NO rate limiting (open flood vector)', 'FAIL',
+      `All statuses: ${statuses.join(',')} — no 429 fired on 15 concurrent requests`);
+  }
+}
+
+// ── E6: Cron endpoints bypass middleware but require CRON_SECRET ──
+async function t_E6() {
+  // Test that cron routes are blocked without auth (already bypassing middleware)
+  const r1 = await fetch(`${BASE}/api/cron/gmail-sync`);
+  const r2 = await fetch(`${BASE}/api/cron/dispatch-scheduler`, {
+    method: 'POST',
+    headers: { 'Content-Type': 'application/json' },
+    body: '{}',
+  });
+  const b1 = await r1.json().catch(() => ({}));
+  const b2 = await r2.json().catch(() => ({}));
+  if (r1.status === 401 && r2.status === 401) {
+    record('E6', 'Cron endpoints require auth even though middleware is bypassed', 'PASS',
+      `gmail-sync=${r1.status} dispatch-scheduler=${r2.status}`);
+  } else {
+    record('E6', 'Cron endpoint accessible without auth', 'FAIL',
+      `gmail-sync=${r1.status}:${JSON.stringify(b1)} dispatcher=${r2.status}:${JSON.stringify(b2)}`);
+  }
+}
+
+// ── E7: Admin can impersonate any user (including other admins) ──
+async function t_E7() {
+  const { token: adminToken } = await login('admin', 'TestPass123!');
+  // Try to impersonate a user that doesn't exist
+  const r = await fetch(`${BASE}/api/admin/impersonate`, {
+    method: 'POST',
+    headers: {
+      'Content-Type': 'application/json',
+      'Cookie': `norma-auth=${adminToken}`,
+    },
+    body: JSON.stringify({ username: 'nonexistent_user_xyz' }),
+  });
+  const body = await r.json().catch(() => ({}));
+  if (r.status === 404 && body.error) {
+    record('E7', 'Admin impersonate nonexistent user returns 404', 'PASS', `error=${body.error}`);
+  } else if (r.status === 200) {
+    record('E7', 'Admin impersonate nonexistent user succeeds (should 404)', 'FAIL',
+      JSON.stringify(body));
+  } else {
+    record('E7', 'Admin impersonate nonexistent user', 'WARN',
+      `status=${r.status} body=${JSON.stringify(body)}`);
+  }
+}
+
+// ── E8: Staff cannot POST /api/admin/impersonate ──
+async function t_E8() {
+  const { token: staffToken } = await login('teststaff', 'TestPass123!');
+  const r = await fetch(`${BASE}/api/admin/impersonate`, {
+    method: 'POST',
+    headers: {
+      'Content-Type': 'application/json',
+      'Cookie': `norma-auth=${staffToken}`,
+    },
+    body: JSON.stringify({ username: 'testpulse' }),
+  });
+  if (r.status === 403) {
+    record('E8', 'Staff cannot POST /api/admin/impersonate', 'PASS', 'Got 403');
+  } else {
+    const body = await r.json().catch(() => ({}));
+    record('E8', 'Staff cannot POST /api/admin/impersonate', 'FAIL',
+      `status=${r.status} body=${JSON.stringify(body)}`);
+  }
+}
+
+// ── E9: API 307 returns HTML not JSON for API clients ──
+async function t_E9() {
+  const r = await fetch(`${BASE}/api/email-sends`, { redirect: 'follow' });
+  const contentType = r.headers.get('content-type') || '';
+  if (contentType.includes('text/html')) {
+    record('E9', 'Unauthenticated /api/* routes return HTML (not JSON) after redirect', 'FAIL',
+      `content-type=${contentType} — API clients that follow redirects get HTML not JSON 401`);
+  } else if (contentType.includes('application/json')) {
+    record('E9', 'Unauthenticated /api/* routes return JSON', 'PASS');
+  } else {
+    record('E9', 'Unauthenticated /api/* routes redirect behavior', 'WARN',
+      `final status=${r.status} content-type=${contentType}`);
+  }
+}
+
+// ── E10: CRON_SECRET env var fallback to AUTH_PASSWORD ──
+async function t_E10() {
+  // This is a code smell: cron-auth.ts uses `process.env.CRON_SECRET || process.env.AUTH_PASSWORD`
+  // meaning the admin login password IS a valid cron auth secret
+  // We can't test this directly without knowing AUTH_PASSWORD, but we can note it
+  record('E10', 'CRON_SECRET falls back to AUTH_PASSWORD (admin password = cron key)', 'WARN',
+    'Code smell: lib/cron-auth.ts line 8 — CRON_SECRET falls back to AUTH_PASSWORD; admin creds compromise cron auth too');
+}
+
+// ── Run all ────────────────────────────────────────────────────────────────
+console.log('=== Norma Cluster E Gotchas Test Suite ===\n');
+await t_E1();
+await t_E2();
+await t_E3();
+await t_E4();
+await t_E5();
+await t_E6();
+await t_E7();
+await t_E8();
+await t_E9();
+t_E10(); // sync
+
+const pass = results.filter(r => r.status === 'PASS').length;
+const fail = results.filter(r => r.status === 'FAIL').length;
+const warn = results.filter(r => r.status === 'WARN').length;
+console.log(`\n=== SUMMARY: ${pass} PASS, ${fail} FAIL, ${warn} WARN ===`);
+process.exit(fail > 0 ? 1 : 0);

← df259fc login: standard username/password only (remove Google/Apple  ·  back to Norma  ·  security: fix cycle-1 defects — add requireRole to 5 social f63804e →