Area: Forums (audit p2) · Surface: POST /forum/post/{postId}/image/{imageId}/alt-text (ForumController@setImageAltText) · Dimension: Broken Access Control / IDOR (OWASP A01:2021) · Severity: minor
A user who is a moderator of one forum (forum_moderators row, which corresponds to platform role 3 'Tenant Mod' per the role taxonomy) can set/clear the alt_text on any image attached to any post in any forum, including forums they do not moderate, because the check is the global role tier (>=3) rather than the per-forum canModerate() used by every other post-mutation endpoint. Impact is low (alt_text only), but it is an authorization inconsistency that violates the forum-scoped moderation model and could be widened if the endpoint grows.
Evidence
platform/src/Controllers/ForumController.php:3082-3086 — $isOwner = (int)$post['user_id']===(int)$user['id']; $isMod = (int)($user['role'] ?? 0) >= 3; if (!$isOwner && !$isMod) { $this->json(['ok'=>false,'error'=>'forbidden'],403); }. Contrast with the sibling write paths editPost/updatePost/deletePost (ForumController.php:3047, 3119, 2795) which gate on $this->canModerate((int)$user['id'],(int)$user['role'],(int)$thread['forum_id']) — a FORUM-scoped check (Forum.php:5548 canModerate = role>=4 OR ForumModerator::isModerator OR CategoryModerator::isSuperModForForum).
Suggested fix. Replace `$isMod = (int)($user['role'] ?? 0) >= 3;` with a forum-scoped check: load the thread via $post['thread_id'], then $isMod = $this->canModerate((int)$user['id'],(int)$user['role'],(int)$thread['forum_id']); and add the same Forum::canView/isPreviewOnly guard the other post endpoints use so alt-text edits can't touch posts in forums the actor can't see.
Filed by the automated tenant-app audit and adversarially evidence-verified. Status: verified. Open — not yet actioned.
Patrick Bass
@mobieus