Return filename validation messages

This commit is contained in:
valadaptive 2023-12-05 17:38:23 -05:00
parent 41d427f4a8
commit 795ca2247b
2 changed files with 34 additions and 27 deletions

View File

@ -12,31 +12,39 @@ const VALID_CATEGORIES = ['bgm', 'ambient', 'blip', 'live2d'];
/** /**
* Validates the input filename for the asset. * Validates the input filename for the asset.
* @param {string} inputFilename Input filename * @param {string} inputFilename Input filename
* @returns {string} Normalized or empty path if invalid * @returns {{error: boolean, message?: string}} Whether validation failed, and why if so
*/ */
function validateAssetFileName(inputFilename) { function validateAssetFileName(inputFilename) {
if (!/^[a-zA-Z0-9_\-.]+$/.test(inputFilename)) { if (!/^[a-zA-Z0-9_\-.]+$/.test(inputFilename)) {
console.debug('Bad request: illegal character in filename, only alphanumeric, \'_\', \'-\' are accepted.'); return {
return ''; error: true,
message: 'Illegal character in filename; only alphanumeric, \'_\', \'-\' are accepted.',
};
} }
const inputExtension = path.extname(inputFilename).toLowerCase(); const inputExtension = path.extname(inputFilename).toLowerCase();
if (UNSAFE_EXTENSIONS.some(ext => ext === inputExtension)) { if (UNSAFE_EXTENSIONS.some(ext => ext === inputExtension)) {
console.debug('Bad request: forbidden file extension.'); return {
return ''; error: true,
message: 'Forbidden file extension.',
};
} }
if (inputFilename.startsWith('.')) { if (inputFilename.startsWith('.')) {
console.debug('Bad request: filename cannot start with \'.\''); return {
return ''; error: true,
message: 'Filename cannot start with \'.\'',
};
} }
if (sanitize(inputFilename) !== inputFilename) { if (sanitize(inputFilename) !== inputFilename) {
console.debug('Bad request: reserved or long filename'); return {
return ''; error: true,
message: 'Reserved or long filename.',
};
} }
return inputFilename; return { error: false };
} }
// Recursive function to get files // Recursive function to get files
@ -141,13 +149,13 @@ router.post('/download', jsonParser, async (request, response) => {
return response.sendStatus(400); return response.sendStatus(400);
} }
// Sanitize filename // Validate filename
const safe_input = validateAssetFileName(request.body.filename); const validation = validateAssetFileName(request.body.filename);
if (safe_input == '') if (validation.error)
return response.sendStatus(400); return response.status(400).send(validation.message);
const temp_path = path.join(DIRECTORIES.assets, 'temp', safe_input); const temp_path = path.join(DIRECTORIES.assets, 'temp', request.body.filename);
const file_path = path.join(DIRECTORIES.assets, category, safe_input); const file_path = path.join(DIRECTORIES.assets, category, request.body.filename);
console.debug('Request received to download', url, 'to', file_path); console.debug('Request received to download', url, 'to', file_path);
try { try {
@ -199,12 +207,12 @@ router.post('/delete', jsonParser, async (request, response) => {
return response.sendStatus(400); return response.sendStatus(400);
} }
// Sanitize filename // Validate filename
const safe_input = validateAssetFileName(request.body.filename); const validation = validateAssetFileName(request.body.filename);
if (safe_input == '') if (validation.error)
return response.sendStatus(400); return response.status(400).send(validation.message);
const file_path = path.join(DIRECTORIES.assets, category, safe_input); const file_path = path.join(DIRECTORIES.assets, category, request.body.filename);
console.debug('Request received to delete', category, file_path); console.debug('Request received to delete', category, file_path);
try { try {

View File

@ -16,13 +16,12 @@ router.post('/upload', jsonParser, async (request, response) => {
return response.status(400).send('No upload data specified'); return response.status(400).send('No upload data specified');
} }
const safeInput = validateAssetFileName(request.body.name); // Validate filename
const validation = validateAssetFileName(request.body.name);
if (validation.error)
return response.status(400).send(validation.message);
if (!safeInput) { const pathToUpload = path.join(DIRECTORIES.files, request.body.name);
return response.status(400).send('Invalid upload name');
}
const pathToUpload = path.join(DIRECTORIES.files, safeInput);
writeFileSyncAtomic(pathToUpload, request.body.data, 'base64'); writeFileSyncAtomic(pathToUpload, request.body.data, 'base64');
const url = path.normalize(pathToUpload.replace('public' + path.sep, '')); const url = path.normalize(pathToUpload.replace('public' + path.sep, ''));
return response.send({ path: url }); return response.send({ path: url });