From a94ec2fea318597646ba1c44d8e44eb1c9196d20 Mon Sep 17 00:00:00 2001 From: chenyc-hust Date: Thu, 12 Mar 2026 17:50:43 +0800 Subject: [PATCH] fix: prevent command injection vulnerabilities in install_dependencies tool This change replaces the unsafe use of child_process.execAsync in the install_dependencies tool with execFileAsync and passes command arguments as arrays instead of constructing shell command strings. Previously, the install_dependencies tool built package installation commands by concatenating user-controlled packages into shell command strings such as conda install, pip install, or uv pip install, and then executed them via child_process.execAsync. Because exec invokes commands through a system shell, specially crafted package names could potentially be interpreted as additional commands, leading to command injection. This update: replaces child_process.execAsync with execFileAsync passes installation arguments as arrays instead of interpolated shell command strings removes shell-based package list construction in install_dependencies preserves the existing dependency installation behavior across supported environment types By avoiding shell interpretation of user-controlled package names, this change prevents potential command injection vulnerabilities while maintaining the original functionality of the install_dependencies tool. --- src/index.ts | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/src/index.ts b/src/index.ts index 6539974..1b3a8be 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,7 +9,7 @@ import { import { randomBytes } from 'crypto'; import { join } from 'path'; import { mkdir, writeFile, appendFile, readFile, access } from 'fs/promises'; -import { exec, ExecOptions } from 'child_process'; +import { exec, execFile, ExecOptions } from 'child_process'; import { promisify } from 'util'; import { platform } from 'os'; @@ -52,6 +52,7 @@ if (ENV_CONFIG.type === 'conda' && !ENV_CONFIG.conda_name) { await mkdir(CODE_STORAGE_DIR, { recursive: true }); const execAsync = promisify(exec); +const execFileAsync = promisify(execFile); /** * Get platform-specific command for environment activation and execution @@ -324,38 +325,41 @@ async function installDependencies(packages: string[]) { }; } - // Build the install command based on environment type - let installCmd = ''; - const packageList = packages.join(' '); - + let file = ''; + let args: string[] = []; + const isWindows = platform() === 'win32'; + switch (ENV_CONFIG.type) { case 'conda': if (!ENV_CONFIG.conda_name) { throw new Error("conda_name is required for conda environment"); } - installCmd = `conda install -y -n ${ENV_CONFIG.conda_name} ${packageList}`; + file = 'conda'; + args = ['install', '-y', '-n', ENV_CONFIG.conda_name, ...packages]; break; case 'venv': - installCmd = `pip install ${packageList}`; + if (!ENV_CONFIG.venv_path) { + throw new Error("venv_path is required for virtualenv"); + } + file = isWindows + ? join(ENV_CONFIG.venv_path, 'Scripts', 'pip.exe') + : join(ENV_CONFIG.venv_path, 'bin', 'pip'); + args = ['install', ...packages]; break; case 'venv-uv': - installCmd = `uv pip install ${packageList}`; + file = 'uv'; + args = ['pip', 'install', ...packages]; break; default: throw new Error(`Unsupported environment type: ${ENV_CONFIG.type}`); } - // Get platform-specific command - const { command, options } = getPlatformSpecificCommand(installCmd); - - // Execute installation with unbuffered Python - const { stdout, stderr } = await execAsync(command, { + const { stdout, stderr } = await execFileAsync(file, args, { cwd: CODE_STORAGE_DIR, - env: { ...process.env, PYTHONUNBUFFERED: '1' }, - ...options + env: { ...process.env, PYTHONUNBUFFERED: '1' } }); const response = {