From 39f20ef0602e58fa2a88e9af887e9c8a7c47bc2d Mon Sep 17 00:00:00 2001 From: gonzaloriestra <14979109+gonzaloriestra@users.noreply.github.com> Date: Tue, 5 May 2026 00:12:31 +0000 Subject: [PATCH] [Security] Add command safety check to execCommand Ensure that execCommand validates that the binary is not located in the current working directory, preventing PATH hijacking. Added security-focused comments and unit tests. --- packages/cli-kit/src/public/node/system.test.ts | 11 +++++++++++ packages/cli-kit/src/public/node/system.ts | 13 +++++++++++++ 2 files changed, 24 insertions(+) diff --git a/packages/cli-kit/src/public/node/system.test.ts b/packages/cli-kit/src/public/node/system.test.ts index f61a9318991..48c638d01ca 100644 --- a/packages/cli-kit/src/public/node/system.test.ts +++ b/packages/cli-kit/src/public/node/system.test.ts @@ -266,6 +266,17 @@ describe('execCommand', () => { // Then expect(execaCommand).toHaveBeenCalledWith('cat', expect.objectContaining({stdin: 'inherit'})) }) + + test('raises an error if the command to run is found in the current directory', async () => { + // Given + vi.mocked(which.sync).mockReturnValueOnce('/currentDirectory/command') + + // When + const got = system.execCommand('command', {cwd: '/currentDirectory'}) + + // Then + await expect(got).rejects.toThrowError('Skipped run of unsecure binary command found in the current directory.') + }) }) describe('isStdinPiped', () => { diff --git a/packages/cli-kit/src/public/node/system.ts b/packages/cli-kit/src/public/node/system.ts index a532dce70a0..77a66b7f314 100644 --- a/packages/cli-kit/src/public/node/system.ts +++ b/packages/cli-kit/src/public/node/system.ts @@ -206,6 +206,11 @@ export async function execCommand(command: string, options?: ExecOptions): Promi env.FORCE_COLOR = '1' } const executionCwd = options?.cwd ?? cwd() + const [cmd] = parseCommand(command) + if (cmd) { + // Security: Prevent execution of binaries from the current working directory to avoid PATH hijacking. + checkCommandSafety(cmd, {cwd: executionCwd}) + } try { await execaCommand(command, { env, @@ -321,6 +326,14 @@ function buildExec( return commandProcess } +/** + * Check if the command is safe to run. + * It prevents the execution of binaries found in the current working directory, + * which is a security risk (PATH hijacking). + * + * @param command - Command to check. + * @param _options - Options containing the current working directory. + */ function checkCommandSafety(command: string, _options: {cwd: string}): void { const pathIncludingLocal = `${_options.cwd}${delimiter}${process.env.PATH}` const commandPath = which.sync(command, {