Files
flux-srl/prisma/migrations/20260609120000_add_client_user/migration.sql
T
davidherran 18d5ed87c8
Deploy to VPS / deploy (push) Has been cancelled
fix(security+db): close the real audit findings (SEC-04/05/01, DB-01)
Acts on the verified findings from the 2026-06 audit (docs/AUDIT_2026-06_
VERIFIED.md). The audit's #1 "middleware never runs" was a false positive
(verified in prod: /hq-command redirects to login). These are the genuine
gaps:

- SEC-04 (HIGH): /api/assets (GET/POST/PUT/DELETE/PATCH) and
  /api/branding/favicon (POST) had NO auth. The middleware matcher excludes
  /api, so they were world-reachable — anyone could list/upload/rename/
  delete CMS files or regenerate the favicon. Added a new getAdminSession()
  helper (src/lib/session.ts) and a requireAdmin() guard on every handler.

- DB-01 (HIGH): the ClientUser table (B2B client portal) was defined in the
  schema but NEVER created by any migration, and OperationsSignal.clientId +
  its FK were missing too. B2B register/login failed at runtime; the
  dashboard silently showed 0 clients. New additive migration
  20260609120000_add_client_user creates the table, the unique email index,
  the clientId column (IF NOT EXISTS), and the FK (duplicate-object guarded).

- SEC-05 (MED-HIGH): operations.ts generateRichEmailHtml() interpolated
  item.title/sku/quantity, clientName/Company/Email/Phone and the free-text
  message straight into HTML — stored XSS into the team's internal inbox.
  Now escaped via escapeHtml/escapeAttr/safeMailto; file links validated to
  internal paths only.

- SEC-01 (MED): removed the hardcoded SESSION_SECRET fallback in src/proxy.ts;
  it now validates lazily and throws if the secret is missing (mirrors
  session.ts), so a runtime env failure can't fall back to a public key.

Verified: next build compiles with SESSION_SECRET unset (Docker parity),
TypeScript clean, prisma schema valid, golden tests 17/17.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 22:40:20 -05:00

41 lines
2.0 KiB
SQL

-- ─────────────────────────────────────────────────────────────────────────
-- ADDITIVE MIGRATION — creates the ClientUser table (B2B client portal) and
-- wires OperationsSignal.clientId to it. The Prisma schema has defined this
-- model + relation for a while, but no migration ever created the table, so
-- the B2B register/login flow (src/app/actions/clientAuth.ts) and the
-- dashboard client counts were failing at runtime. This backfills it.
--
-- Idempotent: IF NOT EXISTS / duplicate-object guards make it safe to re-run
-- and safe for `migrate deploy` against the existing production DB.
-- ─────────────────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS "ClientUser" (
"id" TEXT NOT NULL,
"email" TEXT NOT NULL,
"passwordHash" TEXT NOT NULL,
"fullName" TEXT NOT NULL,
"companyName" TEXT NOT NULL,
"phone" TEXT,
"isApproved" BOOLEAN NOT NULL DEFAULT false,
"lastLoginAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "ClientUser_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX IF NOT EXISTS "ClientUser_email_key" ON "ClientUser" ("email");
-- OperationsSignal.clientId — the FK column the schema references. Add it if
-- a prior schema state never created it (nullable, so existing rows are fine).
ALTER TABLE "OperationsSignal" ADD COLUMN IF NOT EXISTS "clientId" TEXT;
-- Foreign key OperationsSignal.clientId -> ClientUser.id
DO $$ BEGIN
ALTER TABLE "OperationsSignal"
ADD CONSTRAINT "OperationsSignal_clientId_fkey"
FOREIGN KEY ("clientId") REFERENCES "ClientUser"("id")
ON DELETE SET NULL ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;