From 251d49eea94834cf351bb395808f4a56fb4dbb44 Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Sun, 18 Jan 2026 19:13:14 +0200 Subject: [PATCH] Security Fix 3: Checklist REST Bleed. Thanks to [Joshua Rogers](https://joshua.hu) of [Aisle Research](https://aisle.com) and xet7. --- models/checklistItems.js | 89 +++++++++++++++++++++++++++++++--------- 1 file changed, 70 insertions(+), 19 deletions(-) diff --git a/models/checklistItems.js b/models/checklistItems.js index 95e29d23b..db2aa55bd 100644 --- a/models/checklistItems.js +++ b/models/checklistItems.js @@ -271,17 +271,26 @@ if (Meteor.isServer) { '/api/boards/:boardId/cards/:cardId/checklists/:checklistId/items/:itemId', function(req, res) { const paramBoardId = req.params.boardId; + const paramCardId = req.params.cardId; + const paramChecklistId = req.params.checklistId; const paramItemId = req.params.itemId; Authentication.checkBoardAccess(req.userId, paramBoardId); const checklistItem = ReactiveCache.getChecklistItem(paramItemId); - if (checklistItem) { - JsonRoutes.sendResult(res, { - code: 200, - data: checklistItem, - }); + if (checklistItem && checklistItem.cardId === paramCardId && checklistItem.checklistId === paramChecklistId) { + const card = ReactiveCache.getCard(checklistItem.cardId); + if (card && card.boardId === paramBoardId) { + JsonRoutes.sendResult(res, { + code: 200, + data: checklistItem, + }); + } else { + JsonRoutes.sendResult(res, { + code: 404, + }); + } } else { JsonRoutes.sendResult(res, { - code: 500, + code: 404, }); } }, @@ -311,19 +320,26 @@ if (Meteor.isServer) { cardId: paramCardId, }); if (checklist) { - const id = ChecklistItems.insert({ - cardId: paramCardId, - checklistId: paramChecklistId, - title: req.body.title, - isFinished: false, - sort: 0, - }); - JsonRoutes.sendResult(res, { - code: 200, - data: { - _id: id, - }, - }); + const card = ReactiveCache.getCard(paramCardId); + if (card && card.boardId === paramBoardId) { + const id = ChecklistItems.insert({ + cardId: paramCardId, + checklistId: paramChecklistId, + title: req.body.title, + isFinished: false, + sort: 0, + }); + JsonRoutes.sendResult(res, { + code: 200, + data: { + _id: id, + }, + }); + } else { + JsonRoutes.sendResult(res, { + code: 404, + }); + } } else { JsonRoutes.sendResult(res, { code: 404, @@ -350,9 +366,26 @@ if (Meteor.isServer) { '/api/boards/:boardId/cards/:cardId/checklists/:checklistId/items/:itemId', function(req, res) { const paramBoardId = req.params.boardId; + const paramCardId = req.params.cardId; + const paramChecklistId = req.params.checklistId; const paramItemId = req.params.itemId; Authentication.checkBoardAccess(req.userId, paramBoardId); + const checklistItem = ReactiveCache.getChecklistItem(paramItemId); + if (!checklistItem || checklistItem.cardId !== paramCardId || checklistItem.checklistId !== paramChecklistId) { + JsonRoutes.sendResult(res, { + code: 404, + }); + return; + } + const card = ReactiveCache.getCard(checklistItem.cardId); + if (!card || card.boardId !== paramBoardId) { + JsonRoutes.sendResult(res, { + code: 404, + }); + return; + } + function isTrue(data) { try { return data.toLowerCase() === 'true'; @@ -401,8 +434,26 @@ if (Meteor.isServer) { '/api/boards/:boardId/cards/:cardId/checklists/:checklistId/items/:itemId', function(req, res) { const paramBoardId = req.params.boardId; + const paramCardId = req.params.cardId; + const paramChecklistId = req.params.checklistId; const paramItemId = req.params.itemId; Authentication.checkBoardAccess(req.userId, paramBoardId); + + const checklistItem = ReactiveCache.getChecklistItem(paramItemId); + if (!checklistItem || checklistItem.cardId !== paramCardId || checklistItem.checklistId !== paramChecklistId) { + JsonRoutes.sendResult(res, { + code: 404, + }); + return; + } + const card = ReactiveCache.getCard(checklistItem.cardId); + if (!card || card.boardId !== paramBoardId) { + JsonRoutes.sendResult(res, { + code: 404, + }); + return; + } + ChecklistItems.direct.remove({ _id: paramItemId }); JsonRoutes.sendResult(res, { code: 200,