From 9fcb8c0bd9542a1228779130545d10251186d94a Mon Sep 17 00:00:00 2001 From: Luis Henrique Mulinari Date: Mon, 2 Dec 2024 15:29:38 -0300 Subject: [PATCH 1/4] Replce adm-zip by node-stream-zip Currently, adm-zip does not support files bigger than 2GB (https://github.com/cthackers/adm-zip/issues/334). To fix it, the ZIP library was replaced by node-stream-zip, which supports files bigger than 2GB. --- __fixtures__/custom-deploy/invalid-file.zip | Bin 0 -> 520 bytes __fixtures__/custom-deploy/no-root-folder.zip | Bin 0 -> 186 bytes .../custom-deploy/no-themes-folder.zip | Bin 0 -> 164 bytes __fixtures__/custom-deploy/valid-zip.zip | Bin 0 -> 320 bytes __tests__/bin/vip-app-deploy-validate.e2e.js | 38 +++++++++++++ npm-shrinkwrap.json | 51 +++++++----------- package.json | 3 +- src/bin/vip-app-deploy-validate.ts | 2 +- src/lib/validations/custom-deploy.ts | 43 +++++++-------- 9 files changed, 80 insertions(+), 57 deletions(-) create mode 100644 __fixtures__/custom-deploy/invalid-file.zip create mode 100644 __fixtures__/custom-deploy/no-root-folder.zip create mode 100644 __fixtures__/custom-deploy/no-themes-folder.zip create mode 100644 __fixtures__/custom-deploy/valid-zip.zip create mode 100644 __tests__/bin/vip-app-deploy-validate.e2e.js diff --git a/__fixtures__/custom-deploy/invalid-file.zip b/__fixtures__/custom-deploy/invalid-file.zip new file mode 100644 index 0000000000000000000000000000000000000000..fefd0f260b830f99d3e7ff8b5cbae891896512fb GIT binary patch literal 520 zcmWIWW@h1H00Hl&rbsXYO0YA?FyvMiXO^VuhlX%6F#l!uOaBkVr4`%^j4WRn85meV zDg)pK1fv6LW?ZI8zztv02x4M0n-yXrbsXYO0YA?FyvMiXO^VuhlX%6FrR1lOMe2yr4`%^j4WRn85meV qDg(S3ndF#p86g2ROn~97BZvt%kQHJehGDF1AZ3g|=nJI5dKdudCKl)b literal 0 HcmV?d00001 diff --git a/__fixtures__/custom-deploy/valid-zip.zip b/__fixtures__/custom-deploy/valid-zip.zip new file mode 100644 index 0000000000000000000000000000000000000000..3b6779296cdb13ab363469329de31137da4e923f GIT binary patch literal 320 zcmWIWW@h1H00H)9FG7G~TMkYCCTuzaII!b`yts{sDcNi {} ); + +describe( 'vip-app-deploy-validate e2e', () => { + beforeEach( async () => { + jest.clearAllMocks(); + } ); + + describe( 'validateZipFile', () => { + it( 'should not throw error for valid zip file', async () => { + await validateZipFile( '__fixtures__/custom-deploy/valid-zip.zip' ); + + expect( exitSpy ).not.toHaveBeenCalled(); + } ); + + it.each( [ + { + file: '__fixtures__/custom-deploy/invalid-file.zip', + error: `Filename invalid-file-name?.txt contains disallowed characters: [!/:*?"<>|'/^..]+`, + }, + { + file: '__fixtures__/custom-deploy/no-root-folder.zip', + error: `The compressed file must contain a single root directory.`, + }, + { + file: '__fixtures__/custom-deploy/no-themes-folder.zip', + error: `Missing \`themes\` directory from root folder.`, + }, + ] )( 'should throw an error for invalid zip file - $file', async ( { file, error }) => { + await validateZipFile( file ); + + expect( exitSpy ).toHaveBeenCalledWith( error ); + } ); + } ); +} ); diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index e564e55ba..bc90d5798 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -14,7 +14,6 @@ "@automattic/vip-go-preflight-checks": "^2.0.16", "@automattic/vip-search-replace": "^1.1.1", "@json2csv/plainjs": "^7.0.3", - "adm-zip": "^0.5.14", "args": "5.0.3", "chalk": "4.1.2", "check-disk-space": "3.4.0", @@ -34,6 +33,7 @@ "jwt-decode": "4.0.0", "lando": "github:automattic/lando-cli.git#6ca2668", "node-fetch": "^2.6.1", + "node-stream-zip": "1.15.0", "open": "^10.0.0", "proxy-from-env": "^1.1.0", "semver": "7.6.3", @@ -115,7 +115,6 @@ "@babel/preset-typescript": "7.26.0", "@jest/globals": "^29.7.0", "@jest/test-sequencer": "^29.7.0", - "@types/adm-zip": "^0.5.5", "@types/args": "^5.0.3", "@types/cli-table": "^0.3.4", "@types/configstore": "5.0.1", @@ -3428,15 +3427,6 @@ "resolved": "https://registry.npmjs.org/@streamparser/json/-/json-0.0.20.tgz", "integrity": "sha512-VqAAkydywPpkw63WQhPVKCD3SdwXuihCUVZbbiY3SfSTGQyHmwRoq27y4dmJdZuJwd5JIlQoMPyGvMbUPY0RKQ==" }, - "node_modules/@types/adm-zip": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.5.6.tgz", - "integrity": "sha512-lRlcSLg5Yoo7C2H2AUiAoYlvifWoCx/se7iUNiCBTfEVVYFVn+Tr9ZGed4K73tYgLe9O4PjdJvbxlkdAOx/qiw==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/args": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/@types/args/-/args-5.0.3.tgz", @@ -4024,14 +4014,6 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/adm-zip": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz", - "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==", - "engines": { - "node": ">=12.0" - } - }, "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -10182,6 +10164,18 @@ "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", "dev": true }, + "node_modules/node-stream-zip": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/node-stream-zip/-/node-stream-zip-1.15.0.tgz", + "integrity": "sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw==", + "engines": { + "node": ">=0.12.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/antelle" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -15688,15 +15682,6 @@ "resolved": "https://registry.npmjs.org/@streamparser/json/-/json-0.0.20.tgz", "integrity": "sha512-VqAAkydywPpkw63WQhPVKCD3SdwXuihCUVZbbiY3SfSTGQyHmwRoq27y4dmJdZuJwd5JIlQoMPyGvMbUPY0RKQ==" }, - "@types/adm-zip": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.5.6.tgz", - "integrity": "sha512-lRlcSLg5Yoo7C2H2AUiAoYlvifWoCx/se7iUNiCBTfEVVYFVn+Tr9ZGed4K73tYgLe9O4PjdJvbxlkdAOx/qiw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, "@types/args": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/@types/args/-/args-5.0.3.tgz", @@ -16179,11 +16164,6 @@ "dev": true, "requires": {} }, - "adm-zip": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz", - "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==" - }, "agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -20661,6 +20641,11 @@ "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", "dev": true }, + "node-stream-zip": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/node-stream-zip/-/node-stream-zip-1.15.0.tgz", + "integrity": "sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw==" + }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", diff --git a/package.json b/package.json index 1fa4c311b..8792b2108 100644 --- a/package.json +++ b/package.json @@ -114,7 +114,6 @@ "@babel/preset-typescript": "7.26.0", "@jest/globals": "^29.7.0", "@jest/test-sequencer": "^29.7.0", - "@types/adm-zip": "^0.5.5", "@types/args": "^5.0.3", "@types/cli-table": "^0.3.4", "@types/configstore": "5.0.1", @@ -145,7 +144,6 @@ "@automattic/vip-go-preflight-checks": "^2.0.16", "@automattic/vip-search-replace": "^1.1.1", "@json2csv/plainjs": "^7.0.3", - "adm-zip": "^0.5.14", "args": "5.0.3", "chalk": "4.1.2", "check-disk-space": "3.4.0", @@ -165,6 +163,7 @@ "jwt-decode": "4.0.0", "lando": "github:automattic/lando-cli.git#6ca2668", "node-fetch": "^2.6.1", + "node-stream-zip": "1.15.0", "open": "^10.0.0", "proxy-from-env": "^1.1.0", "semver": "7.6.3", diff --git a/src/bin/vip-app-deploy-validate.ts b/src/bin/vip-app-deploy-validate.ts index b1233aac8..51fe31638 100755 --- a/src/bin/vip-app-deploy-validate.ts +++ b/src/bin/vip-app-deploy-validate.ts @@ -41,7 +41,7 @@ export async function appDeployValidateCmd( const ext = extname( fileName ); if ( ext === '.zip' ) { - validateZipFile( fileName ); + await validateZipFile( fileName ); } else { await validateTarFile( fileName ); } diff --git a/src/lib/validations/custom-deploy.ts b/src/lib/validations/custom-deploy.ts index 8b3efe8b4..f6851f4c4 100644 --- a/src/lib/validations/custom-deploy.ts +++ b/src/lib/validations/custom-deploy.ts @@ -1,4 +1,4 @@ -import AdmZip from 'adm-zip'; +import StreamZip, { StreamZipAsync, ZipEntry } from 'node-stream-zip'; import { constants } from 'node:fs'; import path from 'path'; import * as tar from 'tar'; @@ -77,14 +77,14 @@ export function validateName( name: string, isDirectory: boolean ) { /** * Validate the existence of a symlink in a zip file. Ignores symlinks in node_modules/.bin/ * - * @param {IZipEntry} entry The zip entry to validate + * @param {ZipEntry} entry The zip entry to validate */ -function validateZipSymlink( entry: AdmZip.IZipEntry ) { - if ( symlinkIgnorePattern.test( entry.entryName ) ) { +function validateZipSymlink( entry: ZipEntry ) { + if ( symlinkIgnorePattern.test( entry.name ) ) { return; } - const madeBy = entry.header.made >> 8; // eslint-disable-line no-bitwise + const madeBy = entry.verMade >> 8; // eslint-disable-line no-bitwise const errorMsg = errorMessages.symlink + entry.name; // DOS @@ -104,25 +104,25 @@ function validateZipSymlink( entry: AdmZip.IZipEntry ) { * Validate a zip entry for disallowed characters and symlinks. * Ignores __MACOSX directories. * - * @param {IZipEntry} entry The zip entry to validate + * @param {ZipEntry} entry The zip entry to validate */ -function validateZipEntry( entry: AdmZip.IZipEntry ) { - if ( entry.entryName.startsWith( macosxDir ) ) { +function validateZipEntry( entry: ZipEntry ) { + if ( entry.name.startsWith( macosxDir ) ) { return; } - validateName( entry.isDirectory ? entry.entryName : entry.name, entry.isDirectory ); + validateName( entry.isDirectory ? entry.name : path.basename( entry.name ), entry.isDirectory ); validateZipSymlink( entry ); } /** * Validate the existence of a themes directory in the root folder. * - * @param {IZipEntry[]} zipEntries The zip entries to validate + * @param {ZipEntry[]} zipEntries The zip entries to validate */ -function validateZipThemes( rootFolder: string, zipEntries: AdmZip.IZipEntry[] ) { +function validateZipThemes( rootFolder: string, zipEntries: ZipEntry[] ) { const hasThemesDir = zipEntries.some( - entry => entry.isDirectory && entry.entryName.startsWith( path.join( rootFolder, 'themes/' ) ) + entry => entry.isDirectory && entry.name.startsWith( path.join( rootFolder, 'themes/' ) ) ); if ( ! hasThemesDir ) { @@ -135,25 +135,26 @@ function validateZipThemes( rootFolder: string, zipEntries: AdmZip.IZipEntry[] ) * * @param {string} filePath The path to the zip file */ -export function validateZipFile( filePath: string ) { +export async function validateZipFile( filePath: string ) { try { - const zipFile = new AdmZip( filePath ); - const zipEntries = zipFile.getEntries(); + const zipFile = new StreamZip.async( { file: filePath } ); - const rootDirs = zipEntries.filter( + const zipEntries = await zipFile.entries(); + + const rootDirs = Object.values( zipEntries ).filter( entry => entry.isDirectory && - ! entry.entryName.startsWith( macosxDir ) && - ( entry.entryName.match( /\//g ) || [] ).length === 1 + ! entry.name.startsWith( macosxDir ) && + ( entry.name.match( /\//g ) || [] ).length === 1 ); if ( rootDirs.length !== 1 ) { exit.withError( errorMessages.singleRootDir ); } - const rootFolder = rootDirs[ 0 ].entryName; - validateZipThemes( rootFolder, zipEntries ); + const rootFolder = rootDirs[ 0 ].name; + validateZipThemes( rootFolder, Object.values( zipEntries ) ); - zipEntries.forEach( entry => validateZipEntry( entry ) ); + Object.values( zipEntries ).forEach( entry => validateZipEntry( entry ) ); } catch ( error ) { const err = error as Error; exit.withError( `Error reading file: ${ err.message }` ); From 6ccf5977538b317ee6f4d32a3ea60bab42852d63 Mon Sep 17 00:00:00 2001 From: Luis Henrique Mulinari Date: Mon, 2 Dec 2024 16:54:08 -0300 Subject: [PATCH 2/4] Update npm-shrinkwrap.json --- __tests__/bin/vip-app-deploy-validate.e2e.js | 2 +- npm-shrinkwrap.json | 51 +++++++------------- src/lib/validations/custom-deploy.ts | 2 +- 3 files changed, 20 insertions(+), 35 deletions(-) diff --git a/__tests__/bin/vip-app-deploy-validate.e2e.js b/__tests__/bin/vip-app-deploy-validate.e2e.js index bba7188c0..a6856f37c 100644 --- a/__tests__/bin/vip-app-deploy-validate.e2e.js +++ b/__tests__/bin/vip-app-deploy-validate.e2e.js @@ -29,7 +29,7 @@ describe( 'vip-app-deploy-validate e2e', () => { file: '__fixtures__/custom-deploy/no-themes-folder.zip', error: `Missing \`themes\` directory from root folder.`, }, - ] )( 'should throw an error for invalid zip file - $file', async ( { file, error }) => { + ] )( 'should throw an error for invalid zip file - $file', async ( { file, error } ) => { await validateZipFile( file ); expect( exitSpy ).toHaveBeenCalledWith( error ); diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index a40c9b034..a89a575c2 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -14,7 +14,6 @@ "@automattic/vip-go-preflight-checks": "^2.0.16", "@automattic/vip-search-replace": "^1.1.1", "@json2csv/plainjs": "^7.0.3", - "adm-zip": "^0.5.14", "args": "5.0.3", "chalk": "4.1.2", "check-disk-space": "3.4.0", @@ -34,6 +33,7 @@ "jwt-decode": "4.0.0", "lando": "github:automattic/lando-cli.git#6ca2668", "node-fetch": "^2.6.1", + "node-stream-zip": "1.15.0", "open": "^10.0.0", "proxy-from-env": "^1.1.0", "semver": "7.6.3", @@ -115,7 +115,6 @@ "@babel/preset-typescript": "7.26.0", "@jest/globals": "^29.7.0", "@jest/test-sequencer": "^29.7.0", - "@types/adm-zip": "^0.5.5", "@types/args": "^5.0.3", "@types/cli-table": "^0.3.4", "@types/configstore": "5.0.1", @@ -3428,15 +3427,6 @@ "resolved": "https://registry.npmjs.org/@streamparser/json/-/json-0.0.20.tgz", "integrity": "sha512-VqAAkydywPpkw63WQhPVKCD3SdwXuihCUVZbbiY3SfSTGQyHmwRoq27y4dmJdZuJwd5JIlQoMPyGvMbUPY0RKQ==" }, - "node_modules/@types/adm-zip": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.5.7.tgz", - "integrity": "sha512-DNEs/QvmyRLurdQPChqq0Md4zGvPwHerAJYWk9l2jCbD1VPpnzRJorOdiq4zsw09NFbYnhfsoEhWtxIzXpn2yw==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/args": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/@types/args/-/args-5.0.3.tgz", @@ -4024,14 +4014,6 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/adm-zip": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz", - "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==", - "engines": { - "node": ">=12.0" - } - }, "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -10182,6 +10164,18 @@ "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", "dev": true }, + "node_modules/node-stream-zip": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/node-stream-zip/-/node-stream-zip-1.15.0.tgz", + "integrity": "sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw==", + "engines": { + "node": ">=0.12.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/antelle" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -15688,15 +15682,6 @@ "resolved": "https://registry.npmjs.org/@streamparser/json/-/json-0.0.20.tgz", "integrity": "sha512-VqAAkydywPpkw63WQhPVKCD3SdwXuihCUVZbbiY3SfSTGQyHmwRoq27y4dmJdZuJwd5JIlQoMPyGvMbUPY0RKQ==" }, - "@types/adm-zip": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.5.7.tgz", - "integrity": "sha512-DNEs/QvmyRLurdQPChqq0Md4zGvPwHerAJYWk9l2jCbD1VPpnzRJorOdiq4zsw09NFbYnhfsoEhWtxIzXpn2yw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, "@types/args": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/@types/args/-/args-5.0.3.tgz", @@ -16179,11 +16164,6 @@ "dev": true, "requires": {} }, - "adm-zip": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz", - "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==" - }, "agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -20661,6 +20641,11 @@ "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", "dev": true }, + "node-stream-zip": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/node-stream-zip/-/node-stream-zip-1.15.0.tgz", + "integrity": "sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw==" + }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", diff --git a/src/lib/validations/custom-deploy.ts b/src/lib/validations/custom-deploy.ts index f6851f4c4..48af9433b 100644 --- a/src/lib/validations/custom-deploy.ts +++ b/src/lib/validations/custom-deploy.ts @@ -1,4 +1,4 @@ -import StreamZip, { StreamZipAsync, ZipEntry } from 'node-stream-zip'; +import StreamZip, { ZipEntry } from 'node-stream-zip'; import { constants } from 'node:fs'; import path from 'path'; import * as tar from 'tar'; From e97a9e15c167abc7932dbbf335d10bee2c885641 Mon Sep 17 00:00:00 2001 From: Luis Henrique Mulinari Date: Mon, 2 Dec 2024 19:28:58 -0300 Subject: [PATCH 3/4] Fix win32/posix path comparison --- __tests__/bin/vip-app-deploy-validate.e2e.js | 1 + src/lib/validations/custom-deploy.ts | 11 ++++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/__tests__/bin/vip-app-deploy-validate.e2e.js b/__tests__/bin/vip-app-deploy-validate.e2e.js index a6856f37c..ece7934e7 100644 --- a/__tests__/bin/vip-app-deploy-validate.e2e.js +++ b/__tests__/bin/vip-app-deploy-validate.e2e.js @@ -3,6 +3,7 @@ import { validateZipFile } from '../../src/lib/validations/custom-deploy'; const exitSpy = jest.spyOn( exit, 'withError' ); jest.spyOn( process, 'exit' ).mockImplementation( () => {} ); +console.error = jest.fn(); describe( 'vip-app-deploy-validate e2e', () => { beforeEach( async () => { diff --git a/src/lib/validations/custom-deploy.ts b/src/lib/validations/custom-deploy.ts index 48af9433b..b65a64cc1 100644 --- a/src/lib/validations/custom-deploy.ts +++ b/src/lib/validations/custom-deploy.ts @@ -118,12 +118,17 @@ function validateZipEntry( entry: ZipEntry ) { /** * Validate the existence of a themes directory in the root folder. * + * @param rootFolder The root folder of the zip file * @param {ZipEntry[]} zipEntries The zip entries to validate */ function validateZipThemes( rootFolder: string, zipEntries: ZipEntry[] ) { - const hasThemesDir = zipEntries.some( - entry => entry.isDirectory && entry.name.startsWith( path.join( rootFolder, 'themes/' ) ) - ); + const hasThemesDir = zipEntries.some( entry => { + // Convert win32 path separators to posix path separators + const posixPath = entry.name.replace( /\\/g, '/' ); + const requiredPosixPath = path.join( rootFolder, 'themes/' ).replace( /\\/g, '/' ); + + return entry.isDirectory && posixPath.startsWith( requiredPosixPath ); + } ); if ( ! hasThemesDir ) { exit.withError( errorMessages.missingThemes ); From 2a12396f17a13a74cf1776e08057303a2e089f89 Mon Sep 17 00:00:00 2001 From: Luis Henrique Mulinari Date: Tue, 3 Dec 2024 09:49:07 -0300 Subject: [PATCH 4/4] Improve tests --- ...nvalid-file.zip => invalid-file-chars.zip} | Bin .../custom-deploy/valid-zip-posix.zip | Bin 0 -> 1312 bytes .../custom-deploy/valid-zip-win32.zip | Bin 0 -> 801 bytes __fixtures__/custom-deploy/valid-zip.zip | Bin 320 -> 0 bytes __tests__/bin/vip-app-deploy-validate.e2e.js | 32 ++++++++++++++++-- 5 files changed, 29 insertions(+), 3 deletions(-) rename __fixtures__/custom-deploy/{invalid-file.zip => invalid-file-chars.zip} (100%) create mode 100644 __fixtures__/custom-deploy/valid-zip-posix.zip create mode 100644 __fixtures__/custom-deploy/valid-zip-win32.zip delete mode 100644 __fixtures__/custom-deploy/valid-zip.zip diff --git a/__fixtures__/custom-deploy/invalid-file.zip b/__fixtures__/custom-deploy/invalid-file-chars.zip similarity index 100% rename from __fixtures__/custom-deploy/invalid-file.zip rename to __fixtures__/custom-deploy/invalid-file-chars.zip diff --git a/__fixtures__/custom-deploy/valid-zip-posix.zip b/__fixtures__/custom-deploy/valid-zip-posix.zip new file mode 100644 index 0000000000000000000000000000000000000000..67a1ab64f487b203549bc667ef19f3b205cd9ba3 GIT binary patch literal 1312 zcmb8u&q@MO6bA4!&RAI>LmLyabt~KS07>mC6DFZn1Hqs!hK3v=+f@WX&(O|S=mC0& z9>Zl2wdtIhJNNJ0hLj^N^v8FOpMzu9TL*ePpFCb>Z`jK!Y(qSsPSR*^c-{c_;HN)& z!kOJ}fa85S03LmEUVI*`v3-Wc9pc|G<1GFo4B9X$+H2SFM_LD^!a3B2;O4 zR7;B|7S}7&m2WH;+s p;B=3716Pk-T2z6XRh9A08py@PLzI@bzJfmrtl{$wzc9oB_ysyK^&tQN literal 0 HcmV?d00001 diff --git a/__fixtures__/custom-deploy/valid-zip-win32.zip b/__fixtures__/custom-deploy/valid-zip-win32.zip new file mode 100644 index 0000000000000000000000000000000000000000..39ddcbbe6a37aa5d60f83f241a8c19f2252194a3 GIT binary patch literal 801 zcmWIWW@Zs#009sG=14FDO0Y91FyvMiXO^VuhlcPnuxB#(r|)O>Pp|s#mtI=I&A`Y4 zQV1pj;D)pWpc=x5Fr*|SH8-^wV%7;ph*_yXGjW*y2fe8260G+Rh(@dz@dIcE;VDB#jdL4wZ7{7U={t1l-m#$xW6m%h|K*Pi5oS%<> zD93qyFHQ|FT|KsLvFU0DRcf?Fos5EtgBu_HieU)wX6I=8$<1sGbOXp80p5&EBFwlW z0;mKG{yTywqN4?2D7J`!7|Fo!-_aFeDACb`FdJJ$A9FG7G~TMkYCCTuzaII!b`yts{sDcNi { } ); describe( 'validateZipFile', () => { - it( 'should not throw error for valid zip file', async () => { - await validateZipFile( '__fixtures__/custom-deploy/valid-zip.zip' ); + it.each( [ + // Archive: __fixtures__/custom-deploy/valid-zip-posix.zip + // __MACOSX/ + // mysite/ + // mysite/.DS_Store + // mysite/__MACOSX + // mysite/themes + // mysite/themes/.DS_Store + // mysite/themes/__MACOSX + // mysite/themes/mytheme.php + '__fixtures__/custom-deploy/valid-zip-posix.zip', + + // Archive: __fixtures__/custom-deploy/valid-zip-win32.zip + // mysite/ + // mysite/themes + // mysite/themes/mytheme.php + '__fixtures__/custom-deploy/valid-zip-win32.zip', + ] )( 'should not throw error for valid zip file: %s', async file => { + await validateZipFile( file ); expect( exitSpy ).not.toHaveBeenCalled(); } ); it.each( [ { - file: '__fixtures__/custom-deploy/invalid-file.zip', + // Archive: __fixtures__/custom-deploy/invalid-file-chars.zip + // mysite/ + // mysite/themes + // mysite/themes/invalid-file-name?.txt + file: '__fixtures__/custom-deploy/invalid-file-chars.zip', error: `Filename invalid-file-name?.txt contains disallowed characters: [!/:*?"<>|'/^..]+`, }, { + // Archive: __fixtures__/custom-deploy/no-root-folder.zip + // no-root-folder.txt file: '__fixtures__/custom-deploy/no-root-folder.zip', error: `The compressed file must contain a single root directory.`, }, { + // Archive: __fixtures__/custom-deploy/no-themes-folder.zip + // mysite/ + // mysite/file file: '__fixtures__/custom-deploy/no-themes-folder.zip', error: `Missing \`themes\` directory from root folder.`, },