diff --git a/packages/twenty-server/src/engine/core-modules/file-storage/drivers/local.driver.ts b/packages/twenty-server/src/engine/core-modules/file-storage/drivers/local.driver.ts index 80df5abb89..c908212ece 100644 --- a/packages/twenty-server/src/engine/core-modules/file-storage/drivers/local.driver.ts +++ b/packages/twenty-server/src/engine/core-modules/file-storage/drivers/local.driver.ts @@ -1,4 +1,4 @@ -import { createReadStream, existsSync } from 'fs'; +import { createReadStream, existsSync, realpathSync } from 'fs'; import * as fs from 'fs/promises'; import path, { dirname, join } from 'path'; import { type Readable } from 'stream'; @@ -78,18 +78,30 @@ export class LocalDriver implements StorageDriver { folderPath: string; filename: string; }): Promise { - const filePath = join( + const joinedPath = join( `${this.options.storagePath}/`, params.folderPath, params.filename, ); + let filePath: string; - if (!existsSync(filePath)) { + try { + filePath = realpathSync(path.resolve(joinedPath)); + } catch { throw new FileStorageException( 'File not found', FileStorageExceptionCode.FILE_NOT_FOUND, ); } + const storageRoot = realpathSync(path.resolve(this.options.storagePath)); + + if (!filePath.startsWith(storageRoot + path.sep)) { + // Prevent directory traversal + throw new FileStorageException( + 'Access denied', + FileStorageExceptionCode.FILE_NOT_FOUND, + ); + } try { return createReadStream(filePath);