Area: Forums (re-run) (audit p2r) · Surface: POST /forums/{slug}/moderators/add (ForumController@addSubModerator) · Dimension: Security (OWASP) · Severity: critical
Forum ownership is an unprivileged, self-service capability (any registered user can create a community forum). The owner-controlled addSubModerator path side-effects a GLOBAL role promotion (Registered 2 → Moderator 3) via upgradeRoleIfNeeded. Role 3 is the site-wide Moderator tier that AdminMiddleware lets into the admin/moderation plane. A low-privilege user thus controls who becomes a site moderator — a broken-access-control / privilege-escalation chain (OWASP A01:2021). The admin path (AdminForumController@addModerator, requireRole(4)) calling the same helper is fine because an admin is trusted; the unprivileged-owner path is not.
Evidence
ForumController::storeForum (platform/src/Controllers/ForumController.php:3592-3667) lets ANY authenticated user create a community forum and makes them its owner: `ForumModerator::assign($forumId, (int) $user['id'], 'owner');` — no role gate beyond requireAuth(). addSubModerator (line 3738-3776) is gated ONLY on owner-or-admin: `$isOwner = ForumModerator::isOwner(...); if (!$isOwner && (int) $user['role'] < 4) { ... permission denied }` then `ForumModerator::assign(...); ForumModerator::upgradeRoleIfNeeded((int) $target['id']);`. upgradeRoleIfNeeded (platform/src/Models/ForumModerator.php:96-104) does `if ((int) $user['role'] !== 2) return; User::update($userId, ['role' => 3], true);` — it bumps the target's GLOBAL site role to 3. AdminMiddleware (platform/src/Middleware/AdminMiddleware.php:56) admits `role >= 3`, and dozens of admin/moderation routes use requireRole(3). So: register → create a community forum (free) → add any victim as sub-mod → victim is now a site-wide Moderator with admin-panel + moderation-queue access across the whole tenant.
Suggested fix. Per-forum sub-moderators granted by a non-admin owner must NOT receive a global role bump. Either: (a) skip upgradeRoleIfNeeded entirely in addSubModerator and represent forum-mod authority purely via the forum_moderators row (hasModAuthority already consults that table), or (b) restrict global-role promotion to assignments made by an actor with role >= 4. Keep upgradeRoleIfNeeded only on the admin-initiated path.
Filed by the automated tenant-app audit and adversarially evidence-verified. Status: verified. Open — not yet actioned.
Patrick Bass
@mobieus