diff --git a/packages/plugin-cloudflare/src/install-cloudflared.test.ts b/packages/plugin-cloudflare/src/install-cloudflared.test.ts index a72f73ae6db..7b3ce7f91b5 100644 --- a/packages/plugin-cloudflare/src/install-cloudflared.test.ts +++ b/packages/plugin-cloudflare/src/install-cloudflared.test.ts @@ -1,5 +1,6 @@ import install, {CURRENT_CLOUDFLARE_VERSION, versionIsGreaterThan} from './install-cloudflared.js' import * as http from '@shopify/cli-kit/node/http' +import * as system from '@shopify/cli-kit/node/system' import {inTemporaryDirectory, readFile, writeFile, fileExists} from '@shopify/cli-kit/node/fs' import {joinPath} from '@shopify/cli-kit/node/path' import {describe, expect, test, vi} from 'vitest' @@ -9,6 +10,7 @@ import {writeFileSync} from 'fs' import * as childProcess from 'child_process' vi.mock('@shopify/cli-kit/node/http') +vi.mock('@shopify/cli-kit/node/system') vi.mock('child_process') @@ -42,11 +44,11 @@ describe('install-cloudflare', () => { const binPath = joinPath(tmpDir, 'cloudflared') const env = {SHOPIFY_CLI_CLOUDFLARED_PATH: binPath} mockFetch() - vi.mocked(childProcess.execSync).mockImplementation((_command, options) => { + vi.mocked(system.exec).mockImplementation((_command, _args, options) => { // Simulate tar extracting the file const cwd = options?.cwd as string writeFileSync(joinPath(cwd, 'cloudflared'), 'extracted binary') - return Buffer.from('') + return Promise.resolve() }) // When @@ -69,10 +71,10 @@ describe('install-cloudflare', () => { const binPath = joinPath(tmpDir, 'cloudflared') const env = {SHOPIFY_CLI_CLOUDFLARED_PATH: binPath} mockFetch() - vi.mocked(childProcess.execSync).mockImplementation((_command, options) => { + vi.mocked(system.exec).mockImplementation((_command, _args, options) => { const cwd = options?.cwd as string writeFileSync(joinPath(cwd, 'cloudflared'), 'extracted binary') - return Buffer.from('') + return Promise.resolve() }) // When diff --git a/packages/plugin-cloudflare/src/install-cloudflared.ts b/packages/plugin-cloudflare/src/install-cloudflared.ts index 37664c67290..86f026a44da 100644 --- a/packages/plugin-cloudflare/src/install-cloudflared.ts +++ b/packages/plugin-cloudflare/src/install-cloudflared.ts @@ -2,6 +2,7 @@ import {basename, dirname, joinPath} from '@shopify/cli-kit/node/path' import {outputDebug} from '@shopify/cli-kit/node/output' import {fetch} from '@shopify/cli-kit/node/http' +import {exec} from '@shopify/cli-kit/node/system' import { chmod, fileExistsSync, @@ -14,7 +15,7 @@ import {fileURLToPath} from 'url' import util from 'util' import {pipeline} from 'stream' // eslint-disable-next-line no-restricted-imports -import {execSync, execFileSync} from 'child_process' +import {execFileSync} from 'child_process' export const CURRENT_CLOUDFLARE_VERSION = '2024.8.2' const CLOUDFLARE_REPO = `https://github.com/cloudflare/cloudflared/releases/download/${CURRENT_CLOUDFLARE_VERSION}/` @@ -132,7 +133,8 @@ async function installWindows(file: string, binTarget: string) { async function installMacos(file: string, binTarget: string) { await downloadFile(file, `${binTarget}.tgz`) const filename = basename(`${binTarget}.tgz`) - execSync(`tar -xzf ${filename}`, {cwd: dirname(binTarget)}) + // Security: Use a non-shell execution to prevent command injection + await exec('tar', ['-xzf', filename], {cwd: dirname(binTarget)}) unlinkFileSync(`${binTarget}.tgz`) await renameFile(`${dirname(binTarget)}/cloudflared`, binTarget) }