Add path validation for chat directory operations

This commit is contained in:
Cohee
2026-03-03 23:43:23 +02:00
parent ff8b029c9d
commit 83a16d144c
+24 -3
View File
@@ -20,6 +20,7 @@ import {
tryReadFileSync,
tryDeleteFile,
readFirstLine,
isPathUnderParent,
} from '../util.js';
const isBackupEnabled = !!getConfigValue('backups.chat.enabled', true, 'boolean');
@@ -473,6 +474,9 @@ router.post('/save', validateAvatarUrlMiddleware, async function (request, respo
const chatData = request.body.chat;
const chatFileName = `${String(request.body.file_name)}.jsonl`;
const chatFilePath = path.join(request.user.directories.chats, cardName, sanitize(chatFileName));
if (!isPathUnderParent(request.user.directories.chats, chatFilePath)) {
return response.sendStatus(400);
}
if (Array.isArray(chatData)) {
await trySaveChat(chatData, chatFilePath, request.body.force, handle, cardName, request.user.directories.backups);
@@ -514,6 +518,9 @@ router.post('/get', validateAvatarUrlMiddleware, function (request, response) {
try {
const dirName = String(request.body.avatar_url).replace('.png', '');
const directoryPath = path.join(request.user.directories.chats, dirName);
if (!isPathUnderParent(request.user.directories.chats, directoryPath)) {
return response.sendStatus(400);
}
const chatDirExists = fs.existsSync(directoryPath);
//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
const pathToFolder = request.body.is_group
? request.user.directories.groupChats
: path.join(request.user.directories.chats, String(request.body.avatar_url).replace('.png', ''));
if (!request.body.is_group && !isPathUnderParent(request.user.directories.chats, pathToFolder)) {
return response.sendStatus(400);
}
const pathToOriginalFile = path.join(pathToFolder, sanitize(request.body.original_file));
const pathToRenamedFile = path.join(pathToFolder, sanitize(request.body.renamed_file));
const sanitizedFileName = path.parse(pathToRenamedFile).name;
@@ -575,6 +585,9 @@ router.post('/delete', validateAvatarUrlMiddleware, function (request, response)
const dirName = String(request.body.avatar_url).replace('.png', '');
const chatFileName = String(request.body.chatfile);
const chatFilePath = path.join(request.user.directories.chats, dirName, sanitize(chatFileName));
if (!isPathUnderParent(request.user.directories.chats, chatFilePath)) {
return response.sendStatus(400);
}
//Return success if the file was deleted.
if (tryDeleteFile(chatFilePath)) {
return response.send({ ok: true });
@@ -595,7 +608,10 @@ router.post('/export', validateAvatarUrlMiddleware, async function (request, res
const pathToFolder = request.body.is_group
? request.user.directories.groupChats
: path.join(request.user.directories.chats, String(request.body.avatar_url).replace('.png', ''));
let filename = path.join(pathToFolder, sanitize(request.body.file));
const filename = path.join(pathToFolder, sanitize(request.body.file));
if (!request.body.is_group && !isPathUnderParent(request.user.directories.chats, filename)) {
return response.sendStatus(400);
}
let exportfilename = request.body.exportfilename;
if (!fs.existsSync(filename)) {
const errorMessage = {
@@ -690,6 +706,11 @@ router.post('/import', validateAvatarUrlMiddleware, function (request, response)
return response.sendStatus(400);
}
const directoryPath = path.join(request.user.directories.chats, avatarUrl);
if (!isPathUnderParent(request.user.directories.chats, directoryPath)) {
return response.sendStatus(400);
}
try {
const pathToUpload = path.join(request.file.destination, request.file.filename);
const data = fs.readFileSync(pathToUpload, 'utf8');
@@ -718,7 +739,7 @@ router.post('/import', validateAvatarUrlMiddleware, function (request, response)
const handleChat = (chat) => {
const fileName = `${characterName} - ${humanizedDateTime()} imported.jsonl`;
const filePath = path.join(request.user.directories.chats, avatarUrl, fileName);
const filePath = path.join(directoryPath, fileName);
fileNames.push(fileName);
writeFileAtomicSync(filePath, chat, 'utf8');
};
@@ -757,7 +778,7 @@ router.post('/import', validateAvatarUrlMiddleware, function (request, response)
}
const fileName = `${characterName} - ${humanizedDateTime()} imported.jsonl`;
const filePath = path.join(request.user.directories.chats, avatarUrl, fileName);
const filePath = path.join(directoryPath, fileName);
fileNames.push(fileName);
if (flattenedChat !== data) {
writeFileAtomicSync(filePath, flattenedChat, 'utf8');