From c2681b8b52d0f5f0182e28850db1ec5ed79aed53 Mon Sep 17 00:00:00 2001 From: Fabrizio Date: Wed, 26 Jun 2024 09:31:57 +0200 Subject: [PATCH] fix: allow creating folders even if the mime type validation is set (#509) --- src/storage/limits.ts | 4 ++++ src/storage/uploader.ts | 9 +++++++-- src/test/object.test.ts | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/storage/limits.ts b/src/storage/limits.ts index b0d4804f..cff8627d 100644 --- a/src/storage/limits.ts +++ b/src/storage/limits.ts @@ -115,3 +115,7 @@ export function parseFileSizeToBytes(valueWithUnit: string) { export function isUuid(value: string) { return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value) } + +export function isEmptyFolder(object: string) { + return object.endsWith('.emptyFolderPlaceholder') +} diff --git a/src/storage/uploader.ts b/src/storage/uploader.ts index bc23107d..663272c2 100644 --- a/src/storage/uploader.ts +++ b/src/storage/uploader.ts @@ -5,7 +5,7 @@ import { ERRORS } from '@internal/errors' import { FileUploadedSuccess, FileUploadStarted } from '@internal/monitoring/metrics' import { ObjectMetadata, StorageBackendAdapter } from './backend' -import { getFileSizeLimit } from './limits' +import { getFileSizeLimit, isEmptyFolder } from './limits' import { Database } from './database' import { ObjectAdminDelete, ObjectCreatedPostEvent, ObjectCreatedPutEvent } from './events' import { getConfig } from '../config' @@ -81,10 +81,15 @@ export class Uploader { async upload(request: FastifyRequest, options: UploaderOptions) { const version = await this.prepareUpload(options) + // When is an empty folder we restrict it to 0 bytes + if (isEmptyFolder(options.objectName)) { + options.fileSizeLimit = 0 + } + try { const file = await this.incomingFileInfo(request, options) - if (options.allowedMimeTypes) { + if (options.allowedMimeTypes && !isEmptyFolder(options.objectName)) { this.validateMimeType(file.mimeType, options.allowedMimeTypes) } diff --git a/src/test/object.test.ts b/src/test/object.test.ts index 5fde5cff..d380c70e 100644 --- a/src/test/object.test.ts +++ b/src/test/object.test.ts @@ -380,6 +380,43 @@ describe('testing POST object via multipart upload', () => { expect(S3Backend.prototype.uploadObject).not.toHaveBeenCalled() }) + test('can create an empty folder when mime-type is set', async () => { + const form = new FormData() + const headers = Object.assign({}, form.getHeaders(), { + authorization: `Bearer ${serviceKey}`, + 'x-upsert': 'true', + }) + + form.append('file', Buffer.alloc(0)) + + const response = await app().inject({ + method: 'POST', + url: '/object/public-limit-mime-types/nested/.emptyFolderPlaceholder', + headers, + payload: form, + }) + expect(response.statusCode).toBe(200) + expect(S3Backend.prototype.uploadObject).toHaveBeenCalled() + }) + + test('cannot create an empty folder with more than 0kb', async () => { + const form = new FormData() + const headers = Object.assign({}, form.getHeaders(), { + authorization: `Bearer ${serviceKey}`, + 'x-upsert': 'true', + }) + + form.append('file', Buffer.alloc(1)) + + const response = await app().inject({ + method: 'POST', + url: '/object/public-limit-mime-types/nested-2/.emptyFolderPlaceholder', + headers, + payload: form, + }) + expect(response.statusCode).toBe(400) + }) + test('return 422 when uploading an object with a malformed mime-type', async () => { const form = new FormData() form.append('file', fs.createReadStream(`./src/test/assets/sadcat.jpg`))