diff --git a/apps/api/src/routes/lists.ts b/apps/api/src/routes/lists.ts index 58497aa..50efe75 100644 --- a/apps/api/src/routes/lists.ts +++ b/apps/api/src/routes/lists.ts @@ -331,6 +331,12 @@ listsRouter.patch('/:id', zValidator('json', patchListSchema), async (c) => { return c.json({ error: 'Access denied' }, 403) } + // T-04-08 / T-04-05: owner-only guard for isShared mutations. + // A sharee may rename a list (patch.name) but must never mutate list_shares. + if (patch.isShared !== undefined && !access.isOwner) { + return c.json({ error: 'Only the list owner can change sharing settings' }, 403) + } + const prevIsShared = access.listRow.isShared const newIsShared = patch.isShared ?? prevIsShared @@ -341,7 +347,7 @@ listsRouter.patch('/:id', zValidator('json', patchListSchema), async (c) => { await db.update(lists).set(updateValues).where(eq(lists.id, listId)) - // Reconcile list_shares on visibility change (owner only affects shares) + // Owner-only: reconcile list_shares on visibility change if (patch.isShared !== undefined && patch.isShared !== prevIsShared) { if (newIsShared) { // false → true: insert shares for all other members (except owner)