Harden local file storage driver path resolution (#17783)

## Summary

- Normalize all file paths with `path.resolve` instead of `join` to
properly handle `..` segments in file path inputs
- Add `assertPathIsWithinStorage` guard on all write, delete, move,
copy, and existence-check operations
- Introduce `ACCESS_DENIED` exception code with i18n-ready user-friendly
message
- Read path already had realpath-based validation; updated its error
code to `ACCESS_DENIED` for consistency

## Test plan

- [x] Typecheck passes
- [x] Lint passes
- [x] Manual: verify file upload/download still works with valid paths
- [x] Manual: verify `../` in file paths is rejected with ACCESS_DENIED


Made with [Cursor](https://cursor.com)

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Etienne <etiennejouan@users.noreply.github.com>
This commit is contained in:
Félix Malfait
2026-02-09 10:54:10 +01:00
committed by GitHub
parent 15f09736b2
commit ece265c6e4
30 changed files with 614 additions and 246 deletions
@@ -150,14 +150,14 @@ export class LambdaDriver implements LogicFunctionExecutorDriver {
inMemoryLayerFolderPath: string;
}) {
await Promise.all([
this.fileStorageService.downloadFile_v2({
this.fileStorageService.downloadFile({
workspaceId,
applicationUniversalIdentifier,
fileFolder: FileFolder.Dependencies,
resourcePath: 'package.json',
localPath: join(inMemoryLayerFolderPath, 'package.json'),
}),
this.fileStorageService.downloadFile_v2({
this.fileStorageService.downloadFile({
workspaceId,
applicationUniversalIdentifier,
fileFolder: FileFolder.Dependencies,
@@ -368,7 +368,7 @@ export class LambdaDriver implements LogicFunctionExecutorDriver {
const compiledCode = (
await streamToBuffer(
await this.fileStorageService.readFile_v2({
await this.fileStorageService.readFile({
workspaceId: flatLogicFunction.workspaceId,
applicationUniversalIdentifier,
fileFolder: FileFolder.BuiltLogicFunction,
@@ -47,14 +47,14 @@ export class LocalDriver implements LogicFunctionExecutorDriver {
inMemoryLayerFolderPath: string;
}) {
await Promise.all([
this.fileStorageService.downloadFile_v2({
this.fileStorageService.downloadFile({
workspaceId,
applicationUniversalIdentifier,
fileFolder: FileFolder.Dependencies,
resourcePath: 'package.json',
localPath: join(inMemoryLayerFolderPath, 'package.json'),
}),
this.fileStorageService.downloadFile_v2({
this.fileStorageService.downloadFile({
workspaceId,
applicationUniversalIdentifier,
fileFolder: FileFolder.Dependencies,
@@ -122,7 +122,7 @@ export class LocalDriver implements LogicFunctionExecutorDriver {
const baseFolderPath = dirname(flatLogicFunction.builtHandlerPath);
await this.fileStorageService.downloadFolder_v2({
await this.fileStorageService.downloadFolder({
workspaceId: flatLogicFunction.workspaceId,
applicationUniversalIdentifier,
fileFolder: FileFolder.BuiltLogicFunction,
@@ -281,13 +281,13 @@ export class LogicFunctionExecutorService
flatApplication: FlatApplication;
applicationUniversalIdentifier: string;
}): Promise<boolean> {
const packageJsonExists = await this.fileStorageService.checkFileExists_v2({
const packageJsonExists = await this.fileStorageService.checkFileExists({
workspaceId: flatApplication.workspaceId,
applicationUniversalIdentifier,
fileFolder: FileFolder.Dependencies,
resourcePath: 'package.json',
});
const yarnLockExists = await this.fileStorageService.checkFileExists_v2({
const yarnLockExists = await this.fileStorageService.checkFileExists({
workspaceId: flatApplication.workspaceId,
applicationUniversalIdentifier,
fileFolder: FileFolder.Dependencies,
@@ -128,7 +128,7 @@ export class LogicFunctionSourceBuilderService {
const sourceFile = sourceFiles[0];
const builtFile = builtFiles[0];
await this.fileStorageService.writeFile_v2({
await this.fileStorageService.writeFile({
workspaceId,
applicationUniversalIdentifier,
fileFolder: FileFolder.Source,
@@ -141,7 +141,7 @@ export class LogicFunctionSourceBuilderService {
},
});
await this.fileStorageService.writeFile_v2({
await this.fileStorageService.writeFile({
workspaceId,
applicationUniversalIdentifier,
fileFolder: FileFolder.BuiltLogicFunction,
@@ -181,7 +181,7 @@ export class LogicFunctionSourceBuilderService {
const baseFolderPath = getLogicFunctionBaseFolderPath(sourceHandlerPath);
await this.fileStorageService.uploadFolder_v2({
await this.fileStorageService.uploadFolder({
workspaceId,
applicationUniversalIdentifier,
fileFolder: FileFolder.Source,
@@ -206,7 +206,7 @@ export class LogicFunctionSourceBuilderService {
const baseFolderPath = getLogicFunctionBaseFolderPath(sourceHandlerPath);
await this.fileStorageService.downloadFolder_v2({
await this.fileStorageService.downloadFolder({
workspaceId,
applicationUniversalIdentifier,
fileFolder: FileFolder.Source,
@@ -231,7 +231,7 @@ export class LogicFunctionSourceBuilderService {
const builtFile = await fs.readFile(builtBundleFilePath, 'utf-8');
await this.fileStorageService.writeFile_v2({
await this.fileStorageService.writeFile({
workspaceId,
applicationUniversalIdentifier,
fileFolder: FileFolder.BuiltLogicFunction,
@@ -260,7 +260,7 @@ export class LogicFunctionSourceBuilderService {
const baseFolderPath = getLogicFunctionBaseFolderPath(sourceHandlerPath);
try {
return await this.fileStorageService.readFolder_v2({
return await this.fileStorageService.readFolder({
workspaceId,
applicationUniversalIdentifier,
fileFolder: FileFolder.Source,
@@ -297,7 +297,7 @@ export class LogicFunctionSourceBuilderService {
const toBuiltBaseFolderPath =
getLogicFunctionBaseFolderPath(toBuiltHandlerPath);
await this.fileStorageService.copy_v2({
await this.fileStorageService.copy({
from: {
workspaceId,
applicationUniversalIdentifier,
@@ -312,7 +312,7 @@ export class LogicFunctionSourceBuilderService {
},
});
await this.fileStorageService.copy_v2({
await this.fileStorageService.copy({
from: {
workspaceId,
applicationUniversalIdentifier,