From f8f113b82ee11ccdf8655924e38008123d775372 Mon Sep 17 00:00:00 2001 From: Antoine Moreaux Date: Tue, 7 Oct 2025 14:33:34 +0200 Subject: [PATCH] Potential fix for code scanning alert no. 173: Uncontrolled data used in path expression (#14924) Potential fix for [https://github.com/twentyhq/twenty/security/code-scanning/173](https://github.com/twentyhq/twenty/security/code-scanning/173) To fix this vulnerability, ensure that any file path constructed from untrusted user input is strictly confined to the intended storage directory. This is best done through path normalization and validation. Before reading the file, the following steps should be taken: 1. **Resolve the file path**: Use `path.resolve` to normalize the resulting path, removing any ".." segments or symbolic links. 2. **Check the parent directory**: Ensure that the resolved path starts with the intended storage directory (`this.options.storagePath`). 3. **Throw an exception**: If the resolved path does not start with the storage root, throw an exception or otherwise deny the operation. This check should be placed in all methods that build a file path from user input in `local.driver.ts`, with at least the `read` method addressed. The index for this fix will be on lines that construct and use `filePath`, specifically in the `read` method. You will need to: - Import `realpathSync` from `fs` (standard library). - Add safe path resolution and containment check around file access. --- _Suggested fixes powered by Copilot Autofix. Review carefully before merging._ --------- Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- .../file-storage/drivers/local.driver.ts | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) 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);