fix(audit): LOWs sweep — truncate auth entityId, fix legacy berthId in seed-data

L3: failed-login audit's entityId could carry an unbounded
attempted-email value (the form lets you type anything). Truncate
to 256 chars before using as entityId; full original still in
metadata for forensic context.

L2: seed-data.ts (the realistic fixture) inserted interests with
berthId — that column was dropped in migration 0029 and the realistic
seed would fail at insert on a fresh DB. Now inserts via the
interestBerths junction (mirrors the synthetic seed's pattern).

L1 (no-op): next-in-line notification already gets the 5-min
cooldownMs default from createNotification, so retries are
idempotent without extra code. Verified.

L5 (no-op): import worker comment already explains the stub state
adequately; no code change.

1175/1175 vitest passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matt Ciaccio
2026-05-06 22:40:35 +02:00
parent b4fb3b2ca6
commit 0f648a924b
2 changed files with 44 additions and 15 deletions

View File

@@ -50,12 +50,17 @@ function logSignIn(args: {
const email = parsed?.user?.email ?? parsed?.email ?? args.attemptedEmail ?? null;
const ok = args.status >= 200 && args.status < 300;
// entityId is text/unbounded but indexed; truncate the attempted-
// email fallback to keep the row predictably sized when the form
// sends a giant value. The audit metadata still carries the full
// original attempted email for forensic context.
const safeAttempted = (args.attemptedEmail ?? '').slice(0, 256);
void createAuditLog({
userId,
portId: null,
action: 'login',
entityType: 'session',
entityId: userId ?? args.attemptedEmail ?? 'unknown',
entityId: userId ?? safeAttempted ?? 'unknown',
metadata: {
ok,
status: args.status,