From bfc981d73640425f60980f4b18e8a9b4ba6eb855 Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Thu, 25 Dec 2025 12:59:48 -0500 Subject: [PATCH] =?UTF-8?q?=E2=9C=8D=EF=B8=8F=20fix:=20Validation=20for=20?= =?UTF-8?q?Conversation=20Title=20Updates=20(#11099)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ✍️ fix: Validation for Conversation Title Updates * fix: Add validateConvoAccess middleware mock in tests --- api/server/middleware/validate/convoAccess.js | 11 +++++- api/server/routes/__tests__/convos.spec.js | 1 + api/server/routes/convos.js | 35 +++++++++++++++---- 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/api/server/middleware/validate/convoAccess.js b/api/server/middleware/validate/convoAccess.js index ffee70ae61..127bfdc530 100644 --- a/api/server/middleware/validate/convoAccess.js +++ b/api/server/middleware/validate/convoAccess.js @@ -6,6 +6,15 @@ const { logViolation, getLogStores } = require('~/cache'); const { USE_REDIS, CONVO_ACCESS_VIOLATION_SCORE: score = 0 } = process.env ?? {}; +/** + * Helper function to get conversationId from different request body structures. + * @param {Object} body - The request body. + * @returns {string|undefined} The conversationId. + */ +const getConversationId = (body) => { + return body.conversationId ?? body.arg?.conversationId; +}; + /** * Middleware to validate user's authorization for a conversation. * @@ -24,7 +33,7 @@ const validateConvoAccess = async (req, res, next) => { const namespace = ViolationTypes.CONVO_ACCESS; const cache = getLogStores(namespace); - const conversationId = req.body.conversationId; + const conversationId = getConversationId(req.body); if (!conversationId || conversationId === Constants.NEW_CONVO) { return next(); diff --git a/api/server/routes/__tests__/convos.spec.js b/api/server/routes/__tests__/convos.spec.js index e1f9469bef..ce43155cb0 100644 --- a/api/server/routes/__tests__/convos.spec.js +++ b/api/server/routes/__tests__/convos.spec.js @@ -59,6 +59,7 @@ jest.mock('~/server/middleware', () => ({ forkUserLimiter: (req, res, next) => next(), })), configMiddleware: (req, res, next) => next(), + validateConvoAccess: (req, res, next) => next(), })); jest.mock('~/server/utils/import/fork', () => ({ diff --git a/api/server/routes/convos.js b/api/server/routes/convos.js index 90ef13b52d..e862f99ab0 100644 --- a/api/server/routes/convos.js +++ b/api/server/routes/convos.js @@ -6,6 +6,7 @@ const { logger } = require('@librechat/data-schemas'); const { CacheKeys, EModelEndpoint } = require('librechat-data-provider'); const { createImportLimiters, + validateConvoAccess, createForkLimiters, configMiddleware, } = require('~/server/middleware'); @@ -151,17 +152,39 @@ router.delete('/all', async (req, res) => { } }); -router.post('/update', async (req, res) => { - const update = req.body.arg; +/** Maximum allowed length for conversation titles */ +const MAX_CONVO_TITLE_LENGTH = 1024; - if (!update.conversationId) { +/** + * Updates a conversation's title. + * @route POST /update + * @param {string} req.body.arg.conversationId - The conversation ID to update. + * @param {string} req.body.arg.title - The new title for the conversation. + * @returns {object} 201 - The updated conversation object. + */ +router.post('/update', validateConvoAccess, async (req, res) => { + const { conversationId, title } = req.body.arg ?? {}; + + if (!conversationId) { return res.status(400).json({ error: 'conversationId is required' }); } + if (title === undefined) { + return res.status(400).json({ error: 'title is required' }); + } + + if (typeof title !== 'string') { + return res.status(400).json({ error: 'title must be a string' }); + } + + const sanitizedTitle = title.trim().slice(0, MAX_CONVO_TITLE_LENGTH); + try { - const dbResponse = await saveConvo(req, update, { - context: `POST /api/convos/update ${update.conversationId}`, - }); + const dbResponse = await saveConvo( + req, + { conversationId, title: sanitizedTitle }, + { context: `POST /api/convos/update ${conversationId}` }, + ); res.status(201).json(dbResponse); } catch (error) { logger.error('Error updating conversation', error);