diff --git a/src/endpoints/characters.js b/src/endpoints/characters.js index 061547f3f..8e2a9dc55 100644 --- a/src/endpoints/characters.js +++ b/src/endpoints/characters.js @@ -772,10 +772,13 @@ async function importFromCharX(uploadPath, { request }, preservedFileName) { const { card, avatar, auxiliaryAssets, extractedBuffers } = await parser.parse(); // Apply standard character transformations + if (card.data?.name) { + card.data.name = sanitize(card.data.name); + } + card.name = sanitize(card.data?.name || card.name); let processedCard = readFromV2(card); unsetPrivateFields(processedCard); processedCard.create_date = new Date().toISOString(); - processedCard.name = sanitize(processedCard.name); const fileName = preservedFileName || getPngName(processedCard.name, request.user.directories); // Use the actual character name for asset folders, not the unique filename @@ -887,9 +890,13 @@ async function importFromJson(uploadPath, { request }, preservedFileName) { console.info(`Importing from ${jsonData.spec} json`); importRisuSprites(request.user.directories, jsonData); unsetPrivateFields(jsonData); + if (jsonData.data?.name) { + jsonData.data.name = sanitize(jsonData.data.name); + } + jsonData.name = sanitize(jsonData.data?.name || jsonData.name); jsonData = readFromV2(jsonData); jsonData.create_date = new Date().toISOString(); - const pngName = preservedFileName || getPngName(jsonData.data?.name || jsonData.name, request.user.directories); + const pngName = preservedFileName || getPngName(jsonData.name, request.user.directories); const char = JSON.stringify(jsonData); const result = await writeCharacterData(DEFAULT_AVATAR_PATH, char, pngName, request); return result ? pngName : ''; @@ -964,6 +971,9 @@ async function importFromPng(uploadPath, { request }, preservedFileName) { let jsonData = JSON.parse(imgData); + if (jsonData.data?.name) { + jsonData.data.name = sanitize(jsonData.data.name); + } jsonData.name = sanitize(jsonData.data?.name || jsonData.name); const pngName = preservedFileName || getPngName(jsonData.name, request.user.directories); @@ -1529,13 +1539,9 @@ router.post('/chats', validateAvatarUrlMiddleware, async function (request, resp * @returns {string} - The name for the uploaded PNG file */ function getPngName(file, directories) { - let i = 1; - const baseName = file; - while (fs.existsSync(path.join(directories.characters, `${file}.png`))) { - file = baseName + i; - i++; - } - return file; + file = sanitize(file); + return getUniqueName(file, (name) => fs.existsSync(path.join(directories.characters, `${name}.png`)), + { nameBuilder: (base, i) => i === 0 ? base : `${base}${i}`, startIndex: 0, maxTries: 10000 }) ?? file; } /** diff --git a/src/util.js b/src/util.js index ea3e50b21..98c1e9d21 100644 --- a/src/util.js +++ b/src/util.js @@ -1387,7 +1387,7 @@ export function isPathUnderParent(parentPath, childPath) { const relativePath = path.relative(normalizedParent, normalizedChild); - return !relativePath.startsWith('..') && !path.isAbsolute(relativePath); + return relativePath !== '..' && !relativePath.startsWith('..' + path.sep) && !path.isAbsolute(relativePath); } /**