From 3825b94a0987219475d4ea68c9c0c71188227176 Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Thu, 17 Jul 2025 16:59:01 +0200 Subject: [PATCH 1/3] Fix command injection --- src/shell-integration/helpers.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/shell-integration/helpers.js b/src/shell-integration/helpers.js index eeae002..b47d3d6 100644 --- a/src/shell-integration/helpers.js +++ b/src/shell-integration/helpers.js @@ -1,4 +1,4 @@ -import { execSync } from "child_process"; +import { execSync, spawnSync } from "child_process"; import * as os from "os"; import fs from "fs"; @@ -13,9 +13,9 @@ export const knownAikidoTools = [ export function doesExecutableExistOnSystem(executableName) { try { if (os.platform() === "win32") { - execSync(`where ${executableName}`, { stdio: "ignore" }); + spawnSync("where", [executableName], { stdio: "ignore" }); } else { - execSync(`which ${executableName}`, { stdio: "ignore" }); + spawnSync("which", [executableName], { stdio: "ignore" }); } return true; } catch { @@ -46,6 +46,7 @@ export function addLineToFile(filePath, line) { if (!fs.existsSync(filePath)) { fs.writeFileSync(filePath, "", "utf-8"); } + const fileContent = fs.readFileSync(filePath, "utf-8"); const updatedContent = fileContent + os.EOL + line; fs.writeFileSync(filePath, updatedContent, "utf-8"); From 87bb095d4fbb1a4bc7f7ab9843d811ffc6877c1b Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Thu, 17 Jul 2025 17:03:27 +0200 Subject: [PATCH 2/3] Fixes the broken shell detection --- src/shell-integration/helpers.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/shell-integration/helpers.js b/src/shell-integration/helpers.js index b47d3d6..0295858 100644 --- a/src/shell-integration/helpers.js +++ b/src/shell-integration/helpers.js @@ -13,11 +13,12 @@ export const knownAikidoTools = [ export function doesExecutableExistOnSystem(executableName) { try { if (os.platform() === "win32") { - spawnSync("where", [executableName], { stdio: "ignore" }); + const result = spawnSync("where", [executableName], { stdio: "ignore" }); + return result.status === 0; } else { - spawnSync("which", [executableName], { stdio: "ignore" }); + const result = spawnSync("which", [executableName], { stdio: "ignore" }); + return result.status === 0; } - return true; } catch { return false; } From 9476927b877674370c78083a42db4cc152495b8e Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Thu, 17 Jul 2025 17:15:46 +0200 Subject: [PATCH 3/3] Fix tests --- src/shell-integration/supported-shells/bash.js | 4 ++-- .../supported-shells/bash.spec.js | 15 +++++++++++---- src/shell-integration/supported-shells/fish.js | 4 ++-- .../supported-shells/fish.spec.js | 15 +++++++++++---- src/shell-integration/supported-shells/zsh.js | 4 ++-- .../supported-shells/zsh.spec.js | 15 +++++++++++---- 6 files changed, 39 insertions(+), 18 deletions(-) diff --git a/src/shell-integration/supported-shells/bash.js b/src/shell-integration/supported-shells/bash.js index b53d401..35d23c1 100644 --- a/src/shell-integration/supported-shells/bash.js +++ b/src/shell-integration/supported-shells/bash.js @@ -27,10 +27,10 @@ function setup(tools) { const startupFile = execAndGetOutput(startupFileCommand, executableName); teardown(); - for (const tool of tools) { + for (const { tool, aikidoCommand } of tools) { addLineToFile( startupFile, - `alias ${tool}="aikido-${tool}" # Safe-chain alias for ${tool}` + `alias ${tool}="${aikidoCommand}" # Safe-chain alias for ${tool}` ); } diff --git a/src/shell-integration/supported-shells/bash.spec.js b/src/shell-integration/supported-shells/bash.spec.js index a7cc1bd..21be84e 100644 --- a/src/shell-integration/supported-shells/bash.spec.js +++ b/src/shell-integration/supported-shells/bash.spec.js @@ -60,7 +60,11 @@ describe("Bash shell integration", () => { describe("setup", () => { it("should add aliases for all provided tools", () => { - const tools = ["npm", "npx", "yarn"]; + const tools = [ + { tool: "npm", aikidoCommand: "aikido-npm" }, + { tool: "npx", aikidoCommand: "aikido-npx" }, + { tool: "yarn", aikidoCommand: "aikido-yarn" } + ]; const result = bash.setup(tools); assert.strictEqual(result, true); @@ -85,7 +89,7 @@ describe("Bash shell integration", () => { "utf-8" ); - const tools = ["npm"]; + const tools = [{ tool: "npm", aikidoCommand: "aikido-npm" }]; bash.setup(tools); const content = fs.readFileSync(mockStartupFile, "utf-8"); @@ -171,7 +175,10 @@ describe("Bash shell integration", () => { describe("integration tests", () => { it("should handle complete setup and teardown cycle", () => { - const tools = ["npm", "yarn"]; + const tools = [ + { tool: "npm", aikidoCommand: "aikido-npm" }, + { tool: "yarn", aikidoCommand: "aikido-yarn" } + ]; // Setup bash.setup(tools); @@ -187,7 +194,7 @@ describe("Bash shell integration", () => { }); it("should handle multiple setup calls", () => { - const tools = ["npm"]; + const tools = [{ tool: "npm", aikidoCommand: "aikido-npm" }]; bash.setup(tools); bash.setup(tools); diff --git a/src/shell-integration/supported-shells/fish.js b/src/shell-integration/supported-shells/fish.js index b8c3bb3..429d351 100644 --- a/src/shell-integration/supported-shells/fish.js +++ b/src/shell-integration/supported-shells/fish.js @@ -27,10 +27,10 @@ function setup(tools) { const startupFile = execAndGetOutput(startupFileCommand, executableName); teardown(); - for (const tool of tools) { + for (const { tool, aikidoCommand } of tools) { addLineToFile( startupFile, - `alias ${tool} "aikido-${tool}" # Safe-chain alias for ${tool}` + `alias ${tool} "${aikidoCommand}" # Safe-chain alias for ${tool}` ); } diff --git a/src/shell-integration/supported-shells/fish.spec.js b/src/shell-integration/supported-shells/fish.spec.js index af4834d..15344a3 100644 --- a/src/shell-integration/supported-shells/fish.spec.js +++ b/src/shell-integration/supported-shells/fish.spec.js @@ -60,7 +60,11 @@ describe("Fish shell integration", () => { describe("setup", () => { it("should add aliases for all provided tools", () => { - const tools = ["npm", "npx", "yarn"]; + const tools = [ + { tool: "npm", aikidoCommand: "aikido-npm" }, + { tool: "npx", aikidoCommand: "aikido-npx" }, + { tool: "yarn", aikidoCommand: "aikido-yarn" } + ]; const result = fish.setup(tools); assert.strictEqual(result, true); @@ -75,7 +79,7 @@ describe("Fish shell integration", () => { // Pre-populate file with existing aliases fs.writeFileSync(mockStartupFile, 'alias npm "old-npm"\nalias npx "old-npx"\n', "utf-8"); - const tools = ["npm"]; + const tools = [{ tool: "npm", aikidoCommand: "aikido-npm" }]; fish.setup(tools); const content = fs.readFileSync(mockStartupFile, "utf-8"); @@ -161,7 +165,10 @@ describe("Fish shell integration", () => { describe("integration tests", () => { it("should handle complete setup and teardown cycle", () => { - const tools = ["npm", "yarn"]; + const tools = [ + { tool: "npm", aikidoCommand: "aikido-npm" }, + { tool: "yarn", aikidoCommand: "aikido-yarn" } + ]; // Setup fish.setup(tools); @@ -177,7 +184,7 @@ describe("Fish shell integration", () => { }); it("should handle multiple setup calls", () => { - const tools = ["npm"]; + const tools = [{ tool: "npm", aikidoCommand: "aikido-npm" }]; fish.setup(tools); fish.setup(tools); diff --git a/src/shell-integration/supported-shells/zsh.js b/src/shell-integration/supported-shells/zsh.js index 9943634..d0b179b 100644 --- a/src/shell-integration/supported-shells/zsh.js +++ b/src/shell-integration/supported-shells/zsh.js @@ -27,10 +27,10 @@ function setup(tools) { const startupFile = execAndGetOutput(startupFileCommand, executableName); teardown(); - for (const tool of tools) { + for (const { tool, aikidoCommand } of tools) { addLineToFile( startupFile, - `alias ${tool}="aikido-${tool}" # Safe-chain alias for ${tool}` + `alias ${tool}="${aikidoCommand}" # Safe-chain alias for ${tool}` ); } diff --git a/src/shell-integration/supported-shells/zsh.spec.js b/src/shell-integration/supported-shells/zsh.spec.js index 1e2f0bd..37cc20f 100644 --- a/src/shell-integration/supported-shells/zsh.spec.js +++ b/src/shell-integration/supported-shells/zsh.spec.js @@ -60,7 +60,11 @@ describe("Zsh shell integration", () => { describe("setup", () => { it("should add aliases for all provided tools", () => { - const tools = ["npm", "npx", "yarn"]; + const tools = [ + { tool: "npm", aikidoCommand: "aikido-npm" }, + { tool: "npx", aikidoCommand: "aikido-npx" }, + { tool: "yarn", aikidoCommand: "aikido-yarn" } + ]; const result = zsh.setup(tools); assert.strictEqual(result, true); @@ -85,7 +89,7 @@ describe("Zsh shell integration", () => { "utf-8" ); - const tools = ["npm"]; + const tools = [{ tool: "npm", aikidoCommand: "aikido-npm" }]; zsh.setup(tools); const content = fs.readFileSync(mockStartupFile, "utf-8"); @@ -171,7 +175,10 @@ describe("Zsh shell integration", () => { describe("integration tests", () => { it("should handle complete setup and teardown cycle", () => { - const tools = ["npm", "yarn"]; + const tools = [ + { tool: "npm", aikidoCommand: "aikido-npm" }, + { tool: "yarn", aikidoCommand: "aikido-yarn" } + ]; // Setup zsh.setup(tools); @@ -187,7 +194,7 @@ describe("Zsh shell integration", () => { }); it("should handle multiple setup calls", () => { - const tools = ["npm"]; + const tools = [{ tool: "npm", aikidoCommand: "aikido-npm" }]; zsh.setup(tools); zsh.setup(tools);