Add path validation for chat directory operations

83a16d144c9a34171e7a2908fd585e540d126fce

Cohee <18619528+Cohee1207@users.noreply.github.com>

1 files changed, +24 -3Showing whitespace changes
src/endpoints/chats.js+24 -3
@@ -20,6 +20,7 @@ import {
2020 tryReadFileSync,
2121 tryDeleteFile,
2222 readFirstLine,
23+ isPathUnderParent,
2324} from '../util.js';
2425
2526const isBackupEnabled = !!getConfigValue('backups.chat.enabled', true, 'boolean');
@@ -473,6 +474,9 @@ router.post('/save', validateAvatarUrlMiddleware, async function (request, respo
473474 const chatData = request.body.chat;
474475 const chatFileName = `${String(request.body.file_name)}.jsonl`;
475476 const chatFilePath = path.join(request.user.directories.chats, cardName, sanitize(chatFileName));
477+ if (!isPathUnderParent(request.user.directories.chats, chatFilePath)) {
478+ return response.sendStatus(400);
479+ }
476480
477481 if (Array.isArray(chatData)) {
478482 await trySaveChat(chatData, chatFilePath, request.body.force, handle, cardName, request.user.directories.backups);
@@ -514,6 +518,9 @@ router.post('/get', validateAvatarUrlMiddleware, function (request, response) {
514518 try {
515519 const dirName = String(request.body.avatar_url).replace('.png', '');
516520 const directoryPath = path.join(request.user.directories.chats, dirName);
521+ if (!isPathUnderParent(request.user.directories.chats, directoryPath)) {
522+ return response.sendStatus(400);
523+ }
517524 const chatDirExists = fs.existsSync(directoryPath);
518525
519526 //if no chat dir for the character is found, make one with the character name
@@ -545,6 +552,9 @@ router.post('/rename', validateAvatarUrlMiddleware, async function (request, res
545552 const pathToFolder = request.body.is_group
546553 ? request.user.directories.groupChats
547554 : path.join(request.user.directories.chats, String(request.body.avatar_url).replace('.png', ''));
555+ if (!request.body.is_group && !isPathUnderParent(request.user.directories.chats, pathToFolder)) {
556+ return response.sendStatus(400);
557+ }
548558 const pathToOriginalFile = path.join(pathToFolder, sanitize(request.body.original_file));
549559 const pathToRenamedFile = path.join(pathToFolder, sanitize(request.body.renamed_file));
550560 const sanitizedFileName = path.parse(pathToRenamedFile).name;
@@ -575,6 +585,9 @@ router.post('/delete', validateAvatarUrlMiddleware, function (request, response)
575585 const dirName = String(request.body.avatar_url).replace('.png', '');
576586 const chatFileName = String(request.body.chatfile);
577587 const chatFilePath = path.join(request.user.directories.chats, dirName, sanitize(chatFileName));
588+ if (!isPathUnderParent(request.user.directories.chats, chatFilePath)) {
589+ return response.sendStatus(400);
590+ }
578591 //Return success if the file was deleted.
579592 if (tryDeleteFile(chatFilePath)) {
580593 return response.send({ ok: true });
@@ -595,7 +608,10 @@ router.post('/export', validateAvatarUrlMiddleware, async function (request, res
595608 const pathToFolder = request.body.is_group
596609 ? request.user.directories.groupChats
597610 : path.join(request.user.directories.chats, String(request.body.avatar_url).replace('.png', ''));
598611 letconst filename = path.join(pathToFolder, sanitize(request.body.file));
612+ if (!request.body.is_group && !isPathUnderParent(request.user.directories.chats, filename)) {
613+ return response.sendStatus(400);
614+ }
599615 let exportfilename = request.body.exportfilename;
600616 if (!fs.existsSync(filename)) {
601617 const errorMessage = {
@@ -690,6 +706,11 @@ router.post('/import', validateAvatarUrlMiddleware, function (request, response)
690706 return response.sendStatus(400);
691707 }
692708
709+ const directoryPath = path.join(request.user.directories.chats, avatarUrl);
710+ if (!isPathUnderParent(request.user.directories.chats, directoryPath)) {
711+ return response.sendStatus(400);
712+ }
713+
693714 try {
694715 const pathToUpload = path.join(request.file.destination, request.file.filename);
695716 const data = fs.readFileSync(pathToUpload, 'utf8');
@@ -718,7 +739,7 @@ router.post('/import', validateAvatarUrlMiddleware, function (request, response)
718739
719740 const handleChat = (chat) => {
720741 const fileName = `${characterName} - ${humanizedDateTime()} imported.jsonl`;
721742 const filePath = path.join(request.user.directories.chats, avatarUrldirectoryPath, fileName);
722743 fileNames.push(fileName);
723744 writeFileAtomicSync(filePath, chat, 'utf8');
724745 };
@@ -757,7 +778,7 @@ router.post('/import', validateAvatarUrlMiddleware, function (request, response)
757778 }
758779
759780 const fileName = `${characterName} - ${humanizedDateTime()} imported.jsonl`;
760781 const filePath = path.join(request.user.directories.chats, avatarUrldirectoryPath, fileName);
761782 fileNames.push(fileName);
762783 if (flattenedChat !== data) {
763784 writeFileAtomicSync(filePath, flattenedChat, 'utf8');