fix(04-07): add owner-only guard for isShared on PATCH /api/lists/:id (T-04-08)
- Immediately after access check, return 403 if patch.isShared !== undefined
and !access.isOwner — blocks sharees from mutating list_shares
- Guard message: 'Only the list owner can change sharing settings'
- Sharees may still PATCH { name } (rename test stays green)
- Update stale comment: 'Reconcile list_shares on visibility change (owner only)'
- Closes T-04-08 (elevation of privilege) and T-04-05 (shared root cause)
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user