From ac68449b2783886ab0df792b16257a788017ec8d Mon Sep 17 00:00:00 2001 From: Anton Vikulov Date: Fri, 4 Sep 2026 14:04:43 +0300 Subject: [PATCH] fix(s3-upload): revert path normalization --- src/common/s3-upload/upload.test.ts | 31 ----------------------------- src/common/s3-upload/upload.ts | 31 +++++------------------------ 2 files changed, 5 insertions(+), 57 deletions(-) diff --git a/src/common/s3-upload/upload.test.ts b/src/common/s3-upload/upload.test.ts index 56eb6d4..9abda53 100644 --- a/src/common/s3-upload/upload.test.ts +++ b/src/common/s3-upload/upload.test.ts @@ -97,37 +97,6 @@ describe('uploadFiles', () => { expect(uploadFile).toHaveBeenCalledTimes(1); }); - it('builds an S3 key from the target prefix and a relative file path', async () => { - headObject.mockRejectedValueOnce({name: 'NotFound'}); - - await uploadFiles(['/build/assets/file.txt'], { - ...createConfig(), - options: { - ...createConfig().options, - targetPath: 'releases/current', - }, - }); - - expect(headObject).toHaveBeenCalledWith('bucket', 'releases/current/assets/file.txt'); - expect(uploadFile).toHaveBeenCalledWith( - 'bucket', - '/build/assets/file.txt', - 'releases/current/assets/file.txt', - {cacheControl: undefined}, - ); - }); - - it.each(['../outside.txt', '/outside.txt'])( - 'rejects a file outside of sourcePath: %s', - async (filePath) => { - await expect(uploadFiles([filePath], createConfig())).rejects.toThrow( - `File ${filePath} is outside of source path /build`, - ); - expect(headObject).not.toHaveBeenCalled(); - expect(uploadFile).not.toHaveBeenCalled(); - }, - ); - it.each([ ['file.js.gz', 'gzip'], ['file.js.br', 'br'], diff --git a/src/common/s3-upload/upload.ts b/src/common/s3-upload/upload.ts index 13e4b54..fd4e47e 100644 --- a/src/common/s3-upload/upload.ts +++ b/src/common/s3-upload/upload.ts @@ -38,7 +38,9 @@ export async function uploadFiles(files: string[], config: UploadFilesOptions) { return Promise.all( files.flatMap((filePath) => { - const relativeFilePath = getRelativeFilePath(config.options.sourcePath, filePath); + const relativeFilePath = path.isAbsolute(filePath) + ? path.relative(config.options.sourcePath, filePath) + : filePath; return processFile(relativeFilePath); }), ); @@ -69,11 +71,8 @@ export async function uploadFiles(files: string[], config: UploadFilesOptions) { function fileUploader(options: UploadOptions) { return async (relativeFilePath: string) => { - const sourceFilePath = path.resolve(options.sourcePath, relativeFilePath); - const targetFilePath = path.posix.join( - toPosixPath(options.targetPath ?? ''), - toPosixPath(relativeFilePath), - ); + const sourceFilePath = path.join(options.sourcePath, relativeFilePath); + const targetFilePath = path.join(options.targetPath || '', relativeFilePath); log.verbose(`Uploading file ${relativeFilePath} ...`); const existsBehavior = options.existsBehavior ?? 'ignore'; @@ -156,26 +155,6 @@ export async function uploadFiles(files: string[], config: UploadFilesOptions) { const NOT_COMPRESS = ['png', 'zip', 'gz', 'br']; -function getRelativeFilePath(sourcePath: string, filePath: string) { - const absoluteSourcePath = path.resolve(sourcePath); - const absoluteFilePath = path.resolve(absoluteSourcePath, filePath); - const relativeFilePath = path.relative(absoluteSourcePath, absoluteFilePath); - - if ( - relativeFilePath === '..' || - relativeFilePath.startsWith(`..${path.sep}`) || - path.isAbsolute(relativeFilePath) - ) { - throw new Error(`File ${filePath} is outside of source path ${sourcePath}`); - } - - return relativeFilePath; -} - -function toPosixPath(filePath: string) { - return filePath.split(path.sep).join(path.posix.sep); -} - function getContentEncoding(filePath: string) { switch (path.extname(filePath).toLowerCase()) { case '.gz':