Area: Files/photos (re-run) (audit p5r) · Surface: POST /photos/item/{id}/reaction, GET /photos/item/{id}/comments · Dimension: security · Severity: minor
Like addComment, these endpoints take the item id straight from the URL and act on it without confirming the photo_album_item exists or is live. A user can seed photo_reactions rows against deleted or non-existent items (counter pollution that reappears on restore) and listComments will return comment bodies for any item id. The viewableBy gap is already filed; the additional defect here is that neither method verifies the target item row exists or is undeleted before reading/writing, which is the same integrity hole as addComment across the reaction and comment-list surfaces.
Evidence
platform/src/Controllers/PhotoGalleryController.php:509-537 toggleReaction(): inserts/updates/deletes rows in photo_reactions keyed on `(int)$id` with no PhotoAlbumItem lookup and no is_deleted check. Lines 734-746 listComments(): `SELECT ... FROM photo_comments c JOIN users u ... WHERE c.item_id = :i AND c.is_deleted = 0 ... LIMIT 50` with `'i' => (int)$id` — no item existence/deleted check and no auth on the item at all beyond the route group.
Suggested fix. In both methods, resolve the item via PhotoAlbumItem::findById((int)$id), return 404 when missing or is_deleted=1, before touching photo_reactions / photo_comments.
Filed by the automated tenant-app audit and adversarially evidence-verified. Status: verified. Open — not yet actioned.
Patrick Bass
@mobieus