-
Notifications
You must be signed in to change notification settings - Fork 388
Description
Summary
The addReaction and removeReaction methods in server/services/core/chat/message.service.ts bypass the checkConversePermission() check that all other message operations use. This allows any authenticated user to add/remove reactions on messages in conversations they are not a member of, by specifying the target message's MongoDB ObjectID.
Vulnerable Code
In message.service.ts, the addReaction method (around line 492) and removeReaction method (around line 533) perform a direct findById(messageId) without any permission verification:
// addReaction - NO permission check
const message = await this.adapter.model.findById(messageId);
// removeReaction - NO permission check
const message = await this.adapter.model.findById(messageId);Secure Comparison
All other message operations correctly call checkConversePermission() before accessing messages:
sendMessage(line ~201):await this.checkConversePermission(ctx, converseId, groupId)getMessage/deleteMessage/recallMessage/editMessage/fetchConverseLastMessages/fetchNearbyMessage: All callcheckConversePermission()
The checkConversePermission() method (line ~577) validates that the user is either a member of the group's panel, or a participant in the DM conversation.
Impact
- Severity: Medium - Requires valid MongoDB ObjectID (not easily guessable), but allows cross-conversation reaction manipulation
- Any authenticated user can add emoji reactions to messages in private groups/DMs they don't belong to
- Any authenticated user can remove other users' reactions from messages they can't access
- Reveals message existence (side-channel information disclosure)
Suggested Fix
Add checkConversePermission() call at the start of both addReaction and removeReaction:
async addReaction(ctx, messageId, emoji) {
const message = await this.adapter.model.findById(messageId);
if (!message) throw new Error('Message not found');
// Add this permission check:
await this.checkConversePermission(ctx, String(message.converseId), message.groupId ? String(message.groupId) : undefined);
// ... rest of the method
}Apply the same pattern to removeReaction.
Discovery
Found through automated security research comparing permission patterns across message operation endpoints.