diff --git a/packages/safe-chain/src/utils/safeSpawn.js b/packages/safe-chain/src/utils/safeSpawn.js index b4602d2..18f7eb1 100644 --- a/packages/safe-chain/src/utils/safeSpawn.js +++ b/packages/safe-chain/src/utils/safeSpawn.js @@ -107,8 +107,30 @@ export async function safeSpawn(command, args, options = {}) { * TL;DR: add support for shell::false */ export async function safeSpawnPy(command, args, options = {}) { + // The command is always one of our supported package managers. + // It should always be alphanumeric or _ or - + // Reject any command names with suspicious characters + if (!/^[a-zA-Z0-9_-]+$/.test(command)) { + throw new Error(`Invalid command name: ${command}`); + } + return new Promise((resolve) => { - const child = spawn(command, args, { ...options, shell: false }); + // On Unix/macOS resolve to full path to avoid PATH ambiguity; keep shell disabled everywhere + let cmdToRun = command; + if (os.platform() !== "win32") { + try { + cmdToRun = resolveCommandPath(command); + } catch (e) { + if (options.stdio === "inherit") { + process.stderr.write( + `Error: Command '${command}' not found. Please ensure it is installed and available in your PATH.\n` + ); + } + return resolve({ status: 1, stdout: "", stderr: e.message || String(e) }); + } + } + + const child = spawn(cmdToRun, args, { ...options, shell: false }); let stdout = ""; let stderr = ""; diff --git a/packages/safe-chain/src/utils/safeSpawn.spec.js b/packages/safe-chain/src/utils/safeSpawn.spec.js index 9e25997..40d9847 100644 --- a/packages/safe-chain/src/utils/safeSpawn.spec.js +++ b/packages/safe-chain/src/utils/safeSpawn.spec.js @@ -273,8 +273,9 @@ describe("safeSpawnPy", () => { assert.strictEqual(result.status, 0); // Verify spawn signature - assert.strictEqual(spawnCalls.length, 1); - assert.strictEqual(spawnCalls[0].command, "pip3"); + assert.strictEqual(spawnCalls.length, 1); + // Allow either bare command or resolved full path + assert.match(spawnCalls[0].command, /(^|\/)pip3$/); assert.deepStrictEqual(spawnCalls[0].args, ["install", "Jinja2>=3.1,<3.2"]); assert.strictEqual(spawnCalls[0].options.shell, false); assert.strictEqual(spawnCalls[0].options.stdio, "inherit");