From b702b8e69f85f8e998b1d25ad9e66b65b05b61b1 Mon Sep 17 00:00:00 2001 From: Shahanur Islam Shagor Date: Wed, 19 Aug 2026 11:50:56 +0300 Subject: [PATCH 1/4] Add canonical chat room access guard --- backend/server/middleware/roomAccess.js | 98 +++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 backend/server/middleware/roomAccess.js diff --git a/backend/server/middleware/roomAccess.js b/backend/server/middleware/roomAccess.js new file mode 100644 index 00000000..f058e23f --- /dev/null +++ b/backend/server/middleware/roomAccess.js @@ -0,0 +1,98 @@ +const InboxModel = require('../db/models/inbox'); +const GroupModel = require('../db/models/group'); +const ChannelModel = require('../db/models/channel'); +const { addToSet, asArray, toPlain } = require('../db/utils'); +const response = require('../helpers/response'); +const logger = require('../helpers/logger'); + +const getRoomId = (req) => + String(req?.params?.roomId || req?.body?.roomId || '').trim(); + +const isCanonicalGroupMember = async ({ roomId, userId }) => { + const [channelRow, groupRow] = await Promise.all([ + ChannelModel.findOne({ + where: { roomId }, + attributes: ['participantsId'], + }), + GroupModel.findOne({ + where: { roomId }, + attributes: ['participantsId'], + }), + ]); + + const channel = toPlain(channelRow); + const group = toPlain(groupRow); + const entity = channel || group; + + return !!entity && asArray(entity.participantsId).includes(userId); +}; + +/** + * Authorize access to a room by the authenticated user. + * + * Private rooms continue to trust inbox.ownersId only. Group/channel rooms use + * their canonical participantsId list as a fallback so legacy/stale inbox + * ownership cannot reject a valid member. When that drift is detected, repair + * inbox.ownersId before the existing controller-level access checks run. + */ +const roomAccess = async (req, res, next) => { + try { + const roomId = getRoomId(req); + const userId = String(req?.user?._id || '').trim(); + + if (!roomId || !userId) { + response({ + res, + statusCode: 400, + success: false, + message: 'Room and authenticated user are required', + }); + return; + } + + const inbox = await InboxModel.findOne({ where: { roomId } }); + if (!inbox) { + response({ + res, + statusCode: 404, + success: false, + message: 'Room not found', + }); + return; + } + + const inboxPlain = toPlain(inbox) || {}; + const ownersId = asArray(inboxPlain.ownersId); + if (ownersId.includes(userId)) { + next(); + return; + } + + if (inboxPlain.roomType === 'group') { + const canonicalMember = await isCanonicalGroupMember({ roomId, userId }); + if (canonicalMember) { + const repairedOwnersId = addToSet(ownersId, [userId]); + await inbox.update({ ownersId: repairedOwnersId }); + logger.warn('ROOM_ACCESS_MEMBERSHIP_REPAIRED', { + roomId, + userId, + previousOwnerCount: ownersId.length, + repairedOwnerCount: repairedOwnersId.length, + }); + next(); + return; + } + } + + response({ + res, + statusCode: 403, + success: false, + message: 'Forbidden', + }); + } catch (error0) { + next(error0); + } +}; + +module.exports = roomAccess; From 0056f227cf8bc65a7f40615c58a9198f4fcde51c Mon Sep 17 00:00:00 2001 From: Shahanur Islam Shagor Date: Wed, 19 Aug 2026 11:51:10 +0300 Subject: [PATCH 2/4] Guard room chat endpoints with canonical access --- backend/server/routes/chat.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/backend/server/routes/chat.js b/backend/server/routes/chat.js index 6cc77a46..8cd096d4 100644 --- a/backend/server/routes/chat.js +++ b/backend/server/routes/chat.js @@ -2,6 +2,7 @@ const router = require('express').Router(); const authenticate = require('../middleware/auth'); const upload = require('../middleware/upload'); const chatSendIdempotency = require('../middleware/chatSendIdempotency'); +const roomAccess = require('../middleware/roomAccess'); const ctrl = require('../controllers/chat'); const chatUpload = require('../controllers/chatUpload'); const chatDeletion = require('../controllers/chatDeletion'); @@ -16,10 +17,10 @@ router.get('/chats/media', authenticate, ctrl.findMedia); router.get('/chats/calls', authenticate, ctrl.findCalls); router.get('/chats/starred', authenticate, ctrl.findStarred); router.patch('/chats/:chatId/star', authenticate, ctrl.toggleStar); -router.get('/chats/:roomId/pins', authenticate, ctrl.findPinned); -router.post('/chats/:chatId/pin', authenticate, ctrl.pinMessage); -router.delete('/chats/:chatId/pin', authenticate, ctrl.unpinMessage); -router.get('/chats/:roomId', authenticate, ctrl.findByRoomId); +router.get('/chats/:roomId/pins', authenticate, roomAccess, ctrl.findPinned); +router.post('/chats/:chatId/pin', authenticate, roomAccess, ctrl.pinMessage); +router.delete('/chats/:chatId/pin', authenticate, roomAccess, ctrl.unpinMessage); +router.get('/chats/:roomId', authenticate, roomAccess, ctrl.findByRoomId); router.delete('/chats/:roomId', authenticate, chatDeletion.deleteByRoomId); module.exports = router; From 9805e56fe9679d08e07ba6f29f0fe14333dd79be Mon Sep 17 00:00:00 2001 From: Shahanur Islam Shagor Date: Wed, 19 Aug 2026 11:51:25 +0300 Subject: [PATCH 3/4] Repair room membership before chat v2 access checks --- backend/server/routes/chatV2.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/backend/server/routes/chatV2.js b/backend/server/routes/chatV2.js index 9b418991..d674a253 100644 --- a/backend/server/routes/chatV2.js +++ b/backend/server/routes/chatV2.js @@ -1,6 +1,7 @@ const express = require('express'); const router = express.Router(); const authenticate = require('../middleware/auth'); +const roomAccess = require('../middleware/roomAccess'); const ctrl = require('../controllers/chatV2'); const suggestions = require('../controllers/chatSuggestions'); const resumable = require('../controllers/chatResumableUpload'); @@ -13,14 +14,15 @@ router.get('/chat-v2/messages/:chatId/receipts', authenticate, ctrl.getMessageRe router.get('/chat-v2/messages/:chatId/history', authenticate, ctrl.getEditHistory); router.get('/chat-v2/drafts', authenticate, ctrl.listDrafts); -router.get('/chat-v2/drafts/:roomId', authenticate, ctrl.getDraft); -router.put('/chat-v2/drafts/:roomId', authenticate, ctrl.saveDraft); -router.delete('/chat-v2/drafts/:roomId', authenticate, ctrl.deleteDraft); +router.get('/chat-v2/drafts/:roomId', authenticate, roomAccess, ctrl.getDraft); +router.put('/chat-v2/drafts/:roomId', authenticate, roomAccess, ctrl.saveDraft); +router.delete('/chat-v2/drafts/:roomId', authenticate, roomAccess, ctrl.deleteDraft); router.get('/chat-v2/mentions', authenticate, ctrl.listMentions); router.get( '/chat-v2/mention-suggestions/:roomId', authenticate, + roomAccess, suggestions.mentionSuggestions ); router.get('/chat-v2/search', authenticate, ctrl.searchMessages); @@ -32,8 +34,8 @@ router.post( ctrl.actionMessageRequest ); -router.get('/chat-v2/topics/:roomId', authenticate, ctrl.listTopics); -router.post('/chat-v2/topics/:roomId', authenticate, ctrl.createTopic); +router.get('/chat-v2/topics/:roomId', authenticate, roomAccess, ctrl.listTopics); +router.post('/chat-v2/topics/:roomId', authenticate, roomAccess, ctrl.createTopic); router.patch('/chat-v2/topics/item/:topicId', authenticate, ctrl.updateTopic); router.delete('/chat-v2/topics/item/:topicId', authenticate, ctrl.deleteTopic); @@ -47,12 +49,14 @@ router.get( router.get( '/chat-v2/e2ee/rooms/:roomId', authenticate, + roomAccess, cleanupRoomE2eeKeys, ctrl.getRoomE2ee ); router.post( '/chat-v2/e2ee/rooms/:roomId', authenticate, + roomAccess, cleanupRoomE2eeKeys, ctrl.setRoomE2ee ); From d1ba013cfcd455e1e59661d478701a9440cc9bdb Mon Sep 17 00:00:00 2001 From: Shahanur Islam Shagor Date: Wed, 19 Aug 2026 11:52:05 +0300 Subject: [PATCH 4/4] Validate room access middleware in backend build --- backend/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/package.json b/backend/package.json index 4d0dbe5e..57cddbaa 100644 --- a/backend/package.json +++ b/backend/package.json @@ -6,7 +6,7 @@ "scripts": { "predev": "node scripts/free-ports.js 8080", "dev": "nodemon --watch server --watch .env --ignore logs --ignore uploads server", - "build": "node --check server/index.js && node --check server/server.js && node --check server/bootstrap.js && node --check server/config.js && node --check server/db/sequelize.js && node --check server/db/connect.js && node --check server/db/models/inbox.js && node --check server/db/models/chat.js && node --check server/db/models/chatRoomCounter.js && node --check server/db/models/messageReceipt.js && node --check server/db/models/chatDraft.js && node --check server/db/models/messageRequest.js && node --check server/db/models/chatTopic.js && node --check server/db/models/e2eeDeviceKey.js && node --check server/db/models/resumableUpload.js && node --check server/db/models/chatAiConfig.js && node --check server/db/models/adminSocialAuthConfig.js && node --check server/db/models/profile.js && node --check server/db/models/group.js && node --check server/db/models/channel.js && node --check server/db/models/adminStorageConfig.js && node --check server/db/models/adminCallConfig.js && node --check server/db/models/callHistory.js && node --check server/db/models/profilePhoto.js && node --check server/db/models/nativePushDevice.js && node --check server/helpers/storageConfig.js && node --check server/helpers/callConfig.js && node --check server/helpers/callHistory.js && node --check server/helpers/callState.js && node --check server/helpers/avatarDefaults.js && node --check server/helpers/ensureAvatarDefaults.js && node --check server/helpers/ensureProfile.js && node --check server/helpers/privacy.js && node --check server/helpers/profilePhotos.js && node --check server/helpers/livekit.js && node --check server/helpers/nativePushConfig.js && node --check server/helpers/nativePush.js && node --check server/helpers/storage.js && node --check server/helpers/videoPipeline.js && node --check server/helpers/accountArchive.js && node --check server/helpers/accountExport.js && node --check server/helpers/logger.js && node --check server/helpers/chatAbuse.js && node --check server/helpers/chatMentions.js && node --check server/helpers/messageRequests.js && node --check server/helpers/chatReliability.js && node --check server/helpers/chatAiConfig.js && node --check server/helpers/socialAuthConfig.js && node --check server/helpers/e2eeKeyDirectory.js && node --check server/helpers/chatMaintenance.js && node --check server/middleware/chatSendIdempotency.js && node --check server/controllers/avatar.js && node --check server/controllers/profile.js && node --check server/controllers/chatUpload.js && node --check server/controllers/chatSecurity.js && node --check server/controllers/chatDeletion.js && node --check server/controllers/chatV2.js && node --check server/controllers/chatAiAdmin.js && node --check server/controllers/chatSuggestions.js && node --check server/controllers/chatResumableUpload.js && node --check server/controllers/socialAuth.js && node --check server/controllers/socialAuthAdmin.js && node --check server/controllers/storageAdmin.js && node --check server/controllers/callingAdmin.js && node --check server/controllers/callingPushAdmin.js && node --check server/controllers/callingConfig.js && node --check server/controllers/nativePush.js && node --check server/controllers/accountStorage.js && node --check server/routes/profile.js && node --check server/routes/inbox.js && node --check server/routes/chat.js && node --check server/routes/chatV2.js && node --check server/routes/chatAiAdmin.js && node --check server/routes/socialAuthAdmin.js && node --check server/routes/storageAdmin.js && node --check server/routes/callingAdmin.js && node --check server/routes/callingPushAdmin.js && node --check server/routes/callingConfig.js && node --check server/routes/setting.js && node --check server/helpers/socketAdapter.js && node --check server/socket/auth.js && node --check server/socket/index.js && node --check server/socket/events/chatV2.js && node --check server/socket/events/calling.js && node --check server/socket/events/callRealtime.js && node --check server/socket/events/backgroundCalling.js && node --check server/socket/events/groupModeration.js && node --check server/socket/events/room.js && node --check server/routes/cron.js && node --check api/index.js", + "build": "node --check server/index.js && node --check server/server.js && node --check server/bootstrap.js && node --check server/config.js && node --check server/db/sequelize.js && node --check server/db/connect.js && node --check server/db/models/inbox.js && node --check server/db/models/chat.js && node --check server/db/models/chatRoomCounter.js && node --check server/db/models/messageReceipt.js && node --check server/db/models/chatDraft.js && node --check server/db/models/messageRequest.js && node --check server/db/models/chatTopic.js && node --check server/db/models/e2eeDeviceKey.js && node --check server/db/models/resumableUpload.js && node --check server/db/models/chatAiConfig.js && node --check server/db/models/adminSocialAuthConfig.js && node --check server/db/models/profile.js && node --check server/db/models/group.js && node --check server/db/models/channel.js && node --check server/db/models/adminStorageConfig.js && node --check server/db/models/adminCallConfig.js && node --check server/db/models/callHistory.js && node --check server/db/models/profilePhoto.js && node --check server/db/models/nativePushDevice.js && node --check server/helpers/storageConfig.js && node --check server/helpers/callConfig.js && node --check server/helpers/callHistory.js && node --check server/helpers/callState.js && node --check server/helpers/avatarDefaults.js && node --check server/helpers/ensureAvatarDefaults.js && node --check server/helpers/ensureProfile.js && node --check server/helpers/privacy.js && node --check server/helpers/profilePhotos.js && node --check server/helpers/livekit.js && node --check server/helpers/nativePushConfig.js && node --check server/helpers/nativePush.js && node --check server/helpers/storage.js && node --check server/helpers/videoPipeline.js && node --check server/helpers/accountArchive.js && node --check server/helpers/accountExport.js && node --check server/helpers/logger.js && node --check server/helpers/chatAbuse.js && node --check server/helpers/chatMentions.js && node --check server/helpers/messageRequests.js && node --check server/helpers/chatReliability.js && node --check server/helpers/chatAiConfig.js && node --check server/helpers/socialAuthConfig.js && node --check server/helpers/e2eeKeyDirectory.js && node --check server/helpers/chatMaintenance.js && node --check server/middleware/chatSendIdempotency.js && node --check server/middleware/roomAccess.js && node --check server/controllers/avatar.js && node --check server/controllers/profile.js && node --check server/controllers/chatUpload.js && node --check server/controllers/chatSecurity.js && node --check server/controllers/chatDeletion.js && node --check server/controllers/chatV2.js && node --check server/controllers/chatAiAdmin.js && node --check server/controllers/chatSuggestions.js && node --check server/controllers/chatResumableUpload.js && node --check server/controllers/socialAuth.js && node --check server/controllers/socialAuthAdmin.js && node --check server/controllers/storageAdmin.js && node --check server/controllers/callingAdmin.js && node --check server/controllers/callingPushAdmin.js && node --check server/controllers/callingConfig.js && node --check server/controllers/nativePush.js && node --check server/controllers/accountStorage.js && node --check server/routes/profile.js && node --check server/routes/inbox.js && node --check server/routes/chat.js && node --check server/routes/chatV2.js && node --check server/routes/chatAiAdmin.js && node --check server/routes/socialAuthAdmin.js && node --check server/routes/storageAdmin.js && node --check server/routes/callingAdmin.js && node --check server/routes/callingPushAdmin.js && node --check server/routes/callingConfig.js && node --check server/routes/setting.js && node --check server/helpers/socketAdapter.js && node --check server/socket/auth.js && node --check server/socket/index.js && node --check server/socket/events/chatV2.js && node --check server/socket/events/calling.js && node --check server/socket/events/callRealtime.js && node --check server/socket/events/backgroundCalling.js && node --check server/socket/events/groupModeration.js && node --check server/socket/events/room.js && node --check server/routes/cron.js && node --check api/index.js", "start": "node server" }, "dependencies": {