diff --git a/packages/core/upload/server/src/services/upload.ts b/packages/core/upload/server/src/services/upload.ts index 4a0403096d..2167ba3373 100644 --- a/packages/core/upload/server/src/services/upload.ts +++ b/packages/core/upload/server/src/services/upload.ts @@ -81,6 +81,31 @@ export default ({ strapi }: { strapi: Core.Strapi }) => { return tmpWorkingDirectory; }; + function filenameReservedRegex() { + // eslint-disable-next-line no-control-regex + return /[<>:"/\\|?*\u0000-\u001F]/g; + } + + function windowsReservedNameRegex() { + return /^(con|prn|aux|nul|com\d|lpt\d)$/i; + } + + /** + * Copied from https://github.com/sindresorhus/valid-filename package + */ + function isValidFilename(string: string) { + if (!string || string.length > 255) { + return false; + } + if (filenameReservedRegex().test(string) || windowsReservedNameRegex().test(string)) { + return false; + } + if (string === '.' || string === '..') { + return false; + } + return true; + } + async function emitEvent(event: string, data: Record) { const modelDef = strapi.getModel(FILE_MODEL_UID); const sanitizedData = await sanitize.sanitizers.defaultSanitizeOutput( @@ -109,6 +134,10 @@ export default ({ strapi }: { strapi: Core.Strapi }) => { ): Promise> { const fileService = getService('file'); + if (!isValidFilename(filename)) { + throw new ApplicationError('File name contains invalid characters'); + } + let ext = path.extname(filename); if (!ext) { ext = `.${extension(type)}`; @@ -116,6 +145,11 @@ export default ({ strapi }: { strapi: Core.Strapi }) => { const usedName = (fileInfo.name || filename).normalize(); const basename = path.basename(usedName, ext); + // Prevent null characters in file name + if (!isValidFilename(filename)) { + throw new ApplicationError('File name contains invalid characters'); + } + const entity: Omit = { name: usedName, alternativeText: fileInfo.alternativeText,