From ef484af86af92d7d4d43bb484b04d5580b88b256 Mon Sep 17 00:00:00 2001 From: Dhruv Parekh Date: Mon, 31 Aug 2026 11:59:04 +0530 Subject: [PATCH 1/7] fix(deps): bump adm-zip to 0.6.0 and override qs to 6.16.0 Co-Authored-By: Claude Opus 5 (1M context) --- package-lock.json | 23 ++++++++--------------- package.json | 5 ++--- src/adapters/file-upload.ts | 8 ++++---- 3 files changed, 14 insertions(+), 22 deletions(-) diff --git a/package-lock.json b/package-lock.json index f288658..a184ab5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,7 @@ "@rollup/plugin-typescript": "^12.1.2", "@types/express": "^4.17.21", "@types/express-serve-static-core": "^4.17.34", - "adm-zip": "^0.5.18", + "adm-zip": "^0.6.0", "chalk": "^4.1.2", "cross-fetch": "^4.1.0", "dotenv": "^16.4.7", @@ -38,7 +38,6 @@ }, "devDependencies": { "@oclif/test": "^4.1.3", - "@types/adm-zip": "^0.5.7", "@types/chai": "^4.3.20", "@types/ini": "^1.3.34", "@types/jest": "^29.5.14", @@ -3377,14 +3376,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/adm-zip": { - "version": "0.5.8", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/babel__core": { "version": "7.20.5", "dev": true, @@ -3951,12 +3942,12 @@ } }, "node_modules/adm-zip": { - "version": "0.5.18", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.18.tgz", - "integrity": "sha512-ufJnssQGbxzLNS1Ho9bCtX4rQKCCvoVuDLHoJyc3F9dOGDB4BkWs2Ci0kv53lqocAEQ/Cbi+I2XCsNYGqVYqng==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.6.0.tgz", + "integrity": "sha512-XleryMhbuksdKtofnWZ9Sk+4CUTbms4Mb/EU32SZwToAyZ5RgVos/ki8n+yr0LWHOGKuakbXTuuYNHLQjhddgg==", "license": "MIT", "engines": { - "node": ">=12.0" + "node": ">=14.0" } }, "node_modules/agent-base": { @@ -11653,7 +11644,9 @@ "license": "MIT" }, "node_modules/qs": { - "version": "6.15.3", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.0.tgz", + "integrity": "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==", "license": "BSD-3-Clause", "dependencies": { "es-define-property": "^1.0.1", diff --git a/package.json b/package.json index 6a1e726..4e20ba3 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "@rollup/plugin-typescript": "^12.1.2", "@types/express": "^4.17.21", "@types/express-serve-static-core": "^4.17.34", - "adm-zip": "^0.5.18", + "adm-zip": "^0.6.0", "chalk": "^4.1.2", "cross-fetch": "^4.1.0", "dotenv": "^16.4.7", @@ -46,7 +46,6 @@ }, "devDependencies": { "@oclif/test": "^4.1.3", - "@types/adm-zip": "^0.5.7", "@types/chai": "^4.3.20", "@types/ini": "^1.3.34", "@types/jest": "^29.5.14", @@ -115,7 +114,7 @@ "@eslint/eslintrc": { "ajv": "^6.12.6" }, - "qs": "^6.15.2", + "qs": "^6.16.0", "tmp": "^0.2.4", "fast-uri": "^3.1.6", "js-yaml": "^4.3.1", diff --git a/src/adapters/file-upload.ts b/src/adapters/file-upload.ts index 7b2fd09..28f2006 100755 --- a/src/adapters/file-upload.ts +++ b/src/adapters/file-upload.ts @@ -336,12 +336,12 @@ export default class FileUpload extends BaseClass { } } - const status = await zip.writeZipPromise(zipPath).catch(() => { - this.log('Zipping project process failed! Please try again.'); - this.exit(1); + let zipped = true; + await zip.writeZipPromise(zipPath).catch(() => { + zipped = false; }); - if (!status) { + if (!zipped) { this.log('Zipping project process failed! Please try again.'); this.exit(1); } From c6f144943fdd5d44133127116ca748aba362a191 Mon Sep 17 00:00:00 2001 From: Dhruv Parekh Date: Mon, 31 Aug 2026 13:00:34 +0530 Subject: [PATCH 2/7] fix(deps): allowlist file-upload.ts in talisman (+Claude) Co-Authored-By: Claude Opus 5 (1M context) --- .talismanrc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.talismanrc b/.talismanrc index 1038777..fc2fdd3 100644 --- a/.talismanrc +++ b/.talismanrc @@ -17,4 +17,6 @@ fileignoreconfig: checksum: b3d3d3a3c14adde103152d2ac4e1c4b7121a5f7533a87c3cc600f6d25fb59d1b - filename: src/adapters/file-upload.test.ts checksum: 962fd8b45578914926d20857ddc3d1bc2fa19e8b192c6dfdc03d214ae1898b59 -version: "1.0" \ No newline at end of file +- filename: src/adapters/file-upload.ts + checksum: 09cb04f6b9174eaccad6771142a0afd2c55b96ee8ea9c7e5fae30640ef27dc82 +version: "1.0" From 76f1779c4f9d4ece08f2479d2ca724f2075bcab1 Mon Sep 17 00:00:00 2001 From: Dhruv Parekh Date: Mon, 31 Aug 2026 14:13:07 +0530 Subject: [PATCH 3/7] test(file-upload): cover the archive() zip-failure path (+Claude) --- .talismanrc | 6 +++- src/adapters/file-upload.test.ts | 62 ++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/.talismanrc b/.talismanrc index fc2fdd3..b35457d 100644 --- a/.talismanrc +++ b/.talismanrc @@ -18,5 +18,9 @@ fileignoreconfig: - filename: src/adapters/file-upload.test.ts checksum: 962fd8b45578914926d20857ddc3d1bc2fa19e8b192c6dfdc03d214ae1898b59 - filename: src/adapters/file-upload.ts - checksum: 09cb04f6b9174eaccad6771142a0afd2c55b96ee8ea9c7e5fae30640ef27dc82 + allowed_patterns: + - "const \\{ formFieldKey, formFieldValue \\} of fields" + - "formData\\.append\\(formFieldKey, formFieldValue\\)" + - "headers: \\{ key: string; value: string \\}\\[\\]" + - "headers\\?\\.reduce\\(\\(acc, \\{ key, value \\}\\)" version: "1.0" diff --git a/src/adapters/file-upload.test.ts b/src/adapters/file-upload.test.ts index 7b0bd27..60aa10c 100644 --- a/src/adapters/file-upload.test.ts +++ b/src/adapters/file-upload.test.ts @@ -1,10 +1,26 @@ +import AdmZip from 'adm-zip'; import { fileUploadAdapter as cliUtilitiesJestMock } from '../test/mocks/cli-utilities'; import FileUpload from './file-upload'; import BaseClass from './base-class'; import { cliux } from '@contentstack/cli-utilities'; import { DeploymentStatus } from '../types/launch'; +import { getFileList } from '../util/fs'; jest.mock('@contentstack/cli-utilities', () => cliUtilitiesJestMock); +jest.mock('../util/fs', () => ({ getFileList: jest.fn() })); + +// adm-zip defines its methods as own properties on each instance, not on the prototype, +// so the constructor is mocked rather than spied on. esModuleInterop resolves the source's +// default import to `.default`, hence the __esModule shape. +const writeZipPromiseMock = jest.fn(); +jest.mock('adm-zip', () => ({ + __esModule: true, + default: jest.fn().mockImplementation(() => ({ + addLocalFile: jest.fn(), + addLocalFolder: jest.fn(), + writeZipPromise: writeZipPromiseMock, + })), +})); describe('FileUpload Adapter', () => { let logMock: jest.Mock; @@ -827,5 +843,51 @@ describe('FileUpload Adapter', () => { handleEnvImportFlowMock.mockRestore(); }); }); + + describe('archive', () => { + const createInstance = () => + new FileUpload({ + config: { + projectBasePath: '/tmp/launch-project', + fileUploadConfig: { exclude: [] }, + }, + log: logMock, + exit: exitMock, + } as any); + + beforeEach(() => { + (getFileList as jest.Mock).mockResolvedValue([]); + // the suite-wide afterEach calls jest.resetAllMocks(), which strips the + // constructor implementation registered in the jest.mock factory above. + (AdmZip as unknown as jest.Mock).mockImplementation(() => ({ + addLocalFile: jest.fn(), + addLocalFolder: jest.fn(), + writeZipPromise: writeZipPromiseMock, + })); + }); + + it('should log the failure once and exit when zipping rejects', async () => { + writeZipPromiseMock.mockRejectedValue(new Error('disk full')); + const fileUploadInstance = createInstance(); + + await expect(fileUploadInstance.archive()).rejects.toThrow('1'); + + expect(logMock).toHaveBeenCalledTimes(1); + expect(logMock).toHaveBeenCalledWith('Zipping project process failed! Please try again.'); + expect(exitMock).toHaveBeenCalledWith(1); + }); + + it('should return the zip details and not exit when zipping resolves', async () => { + writeZipPromiseMock.mockResolvedValue(undefined); + const fileUploadInstance = createInstance(); + + const result = await fileUploadInstance.archive(); + + expect(result.projectName).toBe('launch-project'); + expect(result.zipName).toMatch(/^\d+_launch-project\.zip$/); + expect(exitMock).not.toHaveBeenCalled(); + expect(logMock).not.toHaveBeenCalled(); + }); + }); }); From 6ca86deb39a8097dab2820424e600addd017422e Mon Sep 17 00:00:00 2001 From: Dhruv Parekh Date: Mon, 31 Aug 2026 14:16:33 +0530 Subject: [PATCH 4/7] test(file-upload): scope the scanner exception to the auth-flag test names (+Claude) --- .talismanrc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.talismanrc b/.talismanrc index b35457d..e3f6bb3 100644 --- a/.talismanrc +++ b/.talismanrc @@ -16,7 +16,8 @@ fileignoreconfig: - filename: src/adapters/github.test.ts checksum: b3d3d3a3c14adde103152d2ac4e1c4b7121a5f7533a87c3cc600f6d25fb59d1b - filename: src/adapters/file-upload.test.ts - checksum: 962fd8b45578914926d20857ddc3d1bc2fa19e8b192c6dfdc03d214ae1898b59 + allowed_patterns: + - "--(disable|enable)-cs-auth is passed" - filename: src/adapters/file-upload.ts allowed_patterns: - "const \\{ formFieldKey, formFieldValue \\} of fields" From 56285781db4b8f8705c466c5f584acf116f840de Mon Sep 17 00:00:00 2001 From: Dhruv Parekh Date: Mon, 31 Aug 2026 14:19:43 +0530 Subject: [PATCH 5/7] test(file-upload): extend scanner exceptions to remaining pre-existing lines (+Claude) --- .talismanrc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.talismanrc b/.talismanrc index e3f6bb3..6a03fb1 100644 --- a/.talismanrc +++ b/.talismanrc @@ -24,4 +24,6 @@ fileignoreconfig: - "formData\\.append\\(formFieldKey, formFieldValue\\)" - "headers: \\{ key: string; value: string \\}\\[\\]" - "headers\\?\\.reduce\\(\\(acc, \\{ key, value \\}\\)" + - "environmentVariables: map\\(this\\.envVariables" + - "const \\{ token, apiKey \\} = configHandler\\.get" version: "1.0" From cf3b8ac60e96a019c0b086bf5ca78041fab983c2 Mon Sep 17 00:00:00 2001 From: Dhruv Parekh Date: Mon, 31 Aug 2026 14:22:37 +0530 Subject: [PATCH 6/7] test(file-upload): match the map callback fragment talisman reports (+Claude) --- .talismanrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.talismanrc b/.talismanrc index 6a03fb1..52db5da 100644 --- a/.talismanrc +++ b/.talismanrc @@ -24,6 +24,6 @@ fileignoreconfig: - "formData\\.append\\(formFieldKey, formFieldValue\\)" - "headers: \\{ key: string; value: string \\}\\[\\]" - "headers\\?\\.reduce\\(\\(acc, \\{ key, value \\}\\)" - - "environmentVariables: map\\(this\\.envVariables" + - "\\(\\{ key, value \\}\\) => \\(\\{ key, value \\}\\)\\)" - "const \\{ token, apiKey \\} = configHandler\\.get" version: "1.0" From c6a583036f538e7de7708c99b2088a487ae032c0 Mon Sep 17 00:00:00 2001 From: Dhruv Parekh Date: Mon, 31 Aug 2026 14:28:44 +0530 Subject: [PATCH 7/7] refactor(file-upload): use try/catch for the zip failure path (+Claude) --- src/adapters/file-upload.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/adapters/file-upload.ts b/src/adapters/file-upload.ts index 28f2006..7959500 100755 --- a/src/adapters/file-upload.ts +++ b/src/adapters/file-upload.ts @@ -336,12 +336,9 @@ export default class FileUpload extends BaseClass { } } - let zipped = true; - await zip.writeZipPromise(zipPath).catch(() => { - zipped = false; - }); - - if (!zipped) { + try { + await zip.writeZipPromise(zipPath); + } catch { this.log('Zipping project process failed! Please try again.'); this.exit(1); }