From 64f0e0a1b821965b8eca1178f4986110ab2a65a7 Mon Sep 17 00:00:00 2001 From: Matt Ciaccio Date: Wed, 6 May 2026 14:58:47 +0200 Subject: [PATCH] fix(security): brochures.service UPDATE/DELETE WHERE includes portId audit-pass-#2 flagged that updateBrochure and archiveBrochure validate portId in their preceding SELECT but omit it from the subsequent UPDATE WHERE clause. Currently safe (the SELECT throws NotFoundError first), but a refactor that drops the SELECT or a TOCTOU race would silently allow a cross-tenant write. Defense-in-depth: add and(eq(id), eq(portId)) to both UPDATE WHERE clauses so the safety property doesn't depend on caller discipline. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/lib/services/brochures.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/services/brochures.service.ts b/src/lib/services/brochures.service.ts index 9df5be3..c13204d 100644 --- a/src/lib/services/brochures.service.ts +++ b/src/lib/services/brochures.service.ts @@ -178,7 +178,7 @@ export async function updateBrochure( const [row] = await tx .update(brochures) .set(updates) - .where(eq(brochures.id, brochureId)) + .where(and(eq(brochures.id, brochureId), eq(brochures.portId, portId))) .returning(); if (!row) throw new CodedError('INSERT_RETURNING_EMPTY', { @@ -196,7 +196,7 @@ export async function archiveBrochure(portId: string, brochureId: string): Promi await db .update(brochures) .set({ archivedAt: new Date(), isDefault: false }) - .where(eq(brochures.id, brochureId)); + .where(and(eq(brochures.id, brochureId), eq(brochures.portId, portId))); } // ─── Versions ────────────────────────────────────────────────────────────────