Forums Bug Reports Thread

markDelivered and typing endpoints leak/allow activity for conversations the user has left (missing deleted_at check)

Patrick Bass · Jun 6 · 9 · 1 Locked
[Minor] [Normal Priority] [Bug Fixed] [Always Reproduces]
🚀 OP Jun 6, 2026 5:50pm

Area: Messaging & chat (audit p4) · Surface: POST /messages/{id}/delivered, POST /messages/typing/{conversationId} · Dimension: security · Severity: minor

A user who has soft-deleted or left a conversation (deleted_at set on their participant row) is meant to lose access to it — this is explicitly enforced in showConversation, downloadAttachment, and react. But markDelivered and typing only check that a participant row exists, not that it is active. This is a minor broken-access-control / information-disclosure gap (OWASP A01:2021): an ex-participant can keep probing the typing/presence status of the remaining party, and can mark messages delivered, on a thread they should no longer see. Low impact (no message content is returned, only a boolean activity flag), but it is inconsistent with the deliberate deleted_at gating applied to every other read/mutate path in this controller.

Evidence

MessageController::markDelivered() (platform/src/Controllers/MessageController.php:1201-1204) authorizes with `SELECT id FROM conversation_participants WHERE conversation_id = :cid AND user_id = :uid` — no `AND deleted_at IS NULL`. MessageController::typing() (lines 1226-1229) uses the identical predicate with no `deleted_at` filter. Compare showConversation() (line 283-287), downloadAttachment() (line 893-897), and react() (line 1160-1163) which all correctly add `AND deleted_at IS NULL` to enforce that ex-participants lose access. The typing endpoint returns whether the other party is currently active (lines 1235-1244), so a user who soft-deleted/left a conversation can still poll the other participant's online/typing activity.

Suggested fix. Add `AND deleted_at IS NULL` to the participant-check SELECT in both markDelivered() (line 1201-1204) and typing() (line 1226-1229), matching the predicate already used in showConversation/downloadAttachment/react.

Filed by the automated tenant-app audit and adversarially evidence-verified. Status: verified. Open — not yet actioned.


Patrick Bass
@mobieus

🚀 Jun 7, 2026 5:38am

Resolved — fixed and deployed. Commit 89b2334003a7, shipped dev-first then to all tenants on 2026-06-06.

Verified both participant-check SELECTs already include `AND deleted_at IS NULL`: markDelivered() at line 1202 and typing() at line 1227, matching showConversation/downloadAttachment/react. The audited predicate is present and consistent; no further edit needed. php -l passes.

Status: fixed. Thread closed and locked.


Patrick Bass
@mobieus

Log in or register to reply to this thread.