import {execSync, ExecSyncOptions, spawnSync} from 'child_process' import {als} from './context' /** * Executes a shell command using the current context's working directory. * Thread-safe for Probot. * @deprecated Use prixSpawn for new code to avoid RCE risks */ export const prixExec = ( command: string, options: ExecSyncOptions = {} ): string => { const context = als.getStore() const cwd = options.cwd || context?.workingDir || process.cwd() const result = execSync(command, { ...options, cwd, encoding: 'utf8' }) return result ? result.toString() : '' } /** * SAFER: Executes a command using spawn with argument array. * Prevents shell injection by not using shell string interpolation. * Thread-safe for Probot. */ export const prixSpawn = ( cmd: string, args: string[] = [], options: {cwd?: string; env?: NodeJS.ProcessEnv} = {} ): {stdout: string; stderr: string; status: number | null} => { const context = als.getStore() const cwd = options.cwd || context?.workingDir || process.cwd() const result = spawnSync(cmd, args, { cwd, env: {...process.env, ...options.env}, encoding: 'utf8', maxBuffer: 10 * 1024 * 1024 // 10MB buffer }) return { stdout: result.stdout || '', stderr: result.stderr || '', status: result.status } } export const sleep = (ms: number): Promise => { return new Promise(resolve => setTimeout(resolve, ms)) } /** * Safely quotes a string for use in a shell command. */ export const quote = (s: string): string => { if (process.platform === 'win32') { // Windows PowerShell/CMD quoting return `"${s.replace(/"/g, '""')}"` } // POSIX shell quoting return `'${s.replace(/'/g, "'\\''")}'` }