From 09300eade6a4c16d30d8409853910f489f5c1b5d Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Thu, 17 Jul 2025 16:40:09 +0200 Subject: [PATCH 01/18] Zsh: check if safe-chain is installed before running it. --- src/shell-integration/setup.js | 26 +++++ .../startup-scripts/init-zsh.sh | 81 ++++++++++++++++ src/shell-integration/supported-shells/zsh.js | 18 ++-- .../supported-shells/zsh.spec.js | 97 +++++++++++-------- 4 files changed, 177 insertions(+), 45 deletions(-) create mode 100644 src/shell-integration/startup-scripts/init-zsh.sh diff --git a/src/shell-integration/setup.js b/src/shell-integration/setup.js index 85d319b..ce6a638 100644 --- a/src/shell-integration/setup.js +++ b/src/shell-integration/setup.js @@ -2,6 +2,10 @@ import chalk from "chalk"; import { ui } from "../environment/userInteraction.js"; import { detectShells } from "./shellDetection.js"; import { knownAikidoTools } from "./helpers.js"; +import fs from "fs"; +import os from "os"; +import path from "path"; +import { fileURLToPath } from "url"; /** * Loops over the detected shells and calls the setup function for each. @@ -13,6 +17,8 @@ export async function setup() { ); ui.emptyLine(); + copyStartupFiles(); + try { const shells = detectShells(); if (shells.length === 0) { @@ -72,3 +78,23 @@ function setupShell(shell) { return success; } + +function copyStartupFiles() { + const startupFiles = ["init-zsh.sh"]; + + for (const file of startupFiles) { + const targetPath = path.join(os.homedir(), ".safe-chain", "scripts", file); + + // Create target directory if it doesn't exist + const targetDir = targetPath.substring(0, targetPath.lastIndexOf("/")); + if (!fs.existsSync(targetDir)) { + fs.mkdirSync(targetDir, { recursive: true }); + } + + // Use absolute path for source + const __filename = fileURLToPath(import.meta.url); + const __dirname = path.dirname(__filename); + const sourcePath = path.resolve(__dirname, "startup-scripts", file); + fs.copyFileSync(sourcePath, targetPath); + } +} diff --git a/src/shell-integration/startup-scripts/init-zsh.sh b/src/shell-integration/startup-scripts/init-zsh.sh new file mode 100644 index 0000000..b936eeb --- /dev/null +++ b/src/shell-integration/startup-scripts/init-zsh.sh @@ -0,0 +1,81 @@ + +function installIfCommandNotFound() { + local cmd="$1" + + # Check if the command already exists + if command -v "$cmd" > /dev/null 2>&1; then + return 0 + fi + + # Check if Node.js version is below 18 + # Safe-chain requires Node.js 18 or higher + local node_version=$(node -v | sed 's/v//' | cut -d'.' -f1) + if [ "$node_version" -lt 18 ]; then + return 2 + fi + + # Command not found, ask user if they want to install safe-chain + printf "The command '%s' is not available. Do you want to install safe-chain to provide it? (y/N): " "$cmd" + read -r response + + if [[ "$response" =~ ^[Yy]$ ]]; then + printf "Installing safe-chain...\n" + installSafeChain + + if [ $? -ne 0 ]; then + printf "\nFailed to install safe-chain. Exiting.\n" + return 1 + fi + + return 0 + else + printf "Skipping safe-chain installation. Using original command instead.\n" + return 2 + fi +} + +function installSafeChain() { + command npm install -g @aikidosec/safe-chain + + if [ $? -ne 0 ]; then + return 1 + fi + + printf "------\n" +} + +function wrapCommand() { + local original_cmd="$1" + local aikido_cmd="$2" + + # Remove the first 2 arguments (original_cmd and aikido_cmd) from $@ + # so that "$@" now contains only the arguments passed to the original command + shift 2 + + installIfCommandNotFound "$aikido_cmd" + local install_result=$? + if [ $install_result -eq 2 ]; then + command "$original_cmd" "$@" + else + "$aikido_cmd" "$@" + fi +} + +function npx() { + wrapCommand "npx" "aikido-npx" "$@" +} + +function yarn() { + wrapCommand "yarn" "aikido-yarn" "$@" +} + +function npm() { + if [[ "$1" == "-v" || "$1" == "--version" ]] && [[ $# -eq 1 ]]; then + # If args is just -v or --version and nothing else, just run the npm version command + # This is because nvm uses this to check the version of npm + command npm "$@" + return + fi + + wrapCommand "npm" "aikido-npm" "$@" +} diff --git a/src/shell-integration/supported-shells/zsh.js b/src/shell-integration/supported-shells/zsh.js index 9943634..d9f63b0 100644 --- a/src/shell-integration/supported-shells/zsh.js +++ b/src/shell-integration/supported-shells/zsh.js @@ -20,19 +20,23 @@ function teardown() { // This will remove the safe-chain aliases for npm, npx, and yarn commands. removeLinesMatchingPattern(startupFile, /^alias\s+(npm|npx|yarn)=/); + // Removes the line that sources the safe-chain zsh initialization script (~/.aikido/scripts/init-zsh.sh) + removeLinesMatchingPattern( + startupFile, + /^source\s+~\/\.safe-chain\/scripts\/init-zsh\.sh/ + ); + return true; } -function setup(tools) { +function setup() { const startupFile = execAndGetOutput(startupFileCommand, executableName); teardown(); - for (const tool of tools) { - addLineToFile( - startupFile, - `alias ${tool}="aikido-${tool}" # Safe-chain alias for ${tool}` - ); - } + addLineToFile( + startupFile, + `source ~/.safe-chain/scripts/init-zsh.sh # Safe-chain Zsh initialization script` + ); return true; } diff --git a/src/shell-integration/supported-shells/zsh.spec.js b/src/shell-integration/supported-shells/zsh.spec.js index 1e2f0bd..327a914 100644 --- a/src/shell-integration/supported-shells/zsh.spec.js +++ b/src/shell-integration/supported-shells/zsh.spec.js @@ -59,49 +59,40 @@ describe("Zsh shell integration", () => { }); describe("setup", () => { - it("should add aliases for all provided tools", () => { - const tools = ["npm", "npx", "yarn"]; - - const result = zsh.setup(tools); + it("should add source line for zsh initialization script", () => { + const result = zsh.setup(); assert.strictEqual(result, true); const content = fs.readFileSync(mockStartupFile, "utf-8"); assert.ok( - content.includes('alias npm="aikido-npm" # Safe-chain alias for npm') - ); - assert.ok( - content.includes('alias npx="aikido-npx" # Safe-chain alias for npx') - ); - assert.ok( - content.includes('alias yarn="aikido-yarn" # Safe-chain alias for yarn') + content.includes( + "source ~/.safe-chain/scripts/init-zsh.sh # Safe-chain Zsh initialization script" + ) ); }); it("should call teardown before setup", () => { - // Pre-populate file with existing aliases + // Pre-populate file with existing source line fs.writeFileSync( mockStartupFile, - 'alias npm="old-npm"\nalias npx="old-npx"\n', + "source ~/.safe-chain/scripts/init-zsh.sh\n", "utf-8" ); - const tools = ["npm"]; - zsh.setup(tools); + zsh.setup(); const content = fs.readFileSync(mockStartupFile, "utf-8"); - assert.ok(!content.includes('alias npm="old-npm"')); - assert.ok(content.includes('alias npm="aikido-npm"')); + const sourceMatches = (content.match(/source.*init-zsh\.sh/g) || []) + .length; + assert.strictEqual(sourceMatches, 1, "Should not duplicate source lines"); }); - it("should handle empty tools array", () => { - const result = zsh.setup([]); + it("should handle empty startup file", () => { + const result = zsh.setup(); assert.strictEqual(result, true); - // File should be created during teardown call even if no tools are provided - if (fs.existsSync(mockStartupFile)) { - const content = fs.readFileSync(mockStartupFile, "utf-8"); - assert.strictEqual(content.trim(), ""); - } + const content = fs.readFileSync(mockStartupFile, "utf-8"); + assert.ok(content.includes("source ~/.safe-chain/scripts/init-zsh.sh")); }); }); @@ -129,6 +120,23 @@ describe("Zsh shell integration", () => { assert.ok(content.includes("alias grep=")); }); + it("should remove zsh initialization script source line", () => { + const initialContent = [ + "#!/bin/zsh", + "source ~/.safe-chain/scripts/init-zsh.sh", + "alias ls='ls --color=auto'", + ].join("\n"); + + fs.writeFileSync(mockStartupFile, initialContent, "utf-8"); + + const result = zsh.teardown(); + assert.strictEqual(result, true); + + const content = fs.readFileSync(mockStartupFile, "utf-8"); + assert.ok(!content.includes("source ~/.safe-chain/scripts/init-zsh.sh")); + assert.ok(content.includes("alias ls=")); + }); + it("should handle file that doesn't exist", () => { if (fs.existsSync(mockStartupFile)) { fs.unlinkSync(mockStartupFile); @@ -138,7 +146,7 @@ describe("Zsh shell integration", () => { assert.strictEqual(result, true); }); - it("should handle file with no relevant aliases", () => { + it("should handle file with no relevant aliases or source lines", () => { const initialContent = [ "#!/bin/zsh", "alias ls='ls --color=auto'", @@ -171,30 +179,43 @@ describe("Zsh shell integration", () => { describe("integration tests", () => { it("should handle complete setup and teardown cycle", () => { - const tools = ["npm", "yarn"]; - // Setup - zsh.setup(tools); + zsh.setup(); let content = fs.readFileSync(mockStartupFile, "utf-8"); - assert.ok(content.includes('alias npm="aikido-npm"')); - assert.ok(content.includes('alias yarn="aikido-yarn"')); + assert.ok(content.includes("source ~/.safe-chain/scripts/init-zsh.sh")); // Teardown zsh.teardown(); content = fs.readFileSync(mockStartupFile, "utf-8"); - assert.ok(!content.includes("alias npm=")); - assert.ok(!content.includes("alias yarn=")); + assert.ok(!content.includes("source ~/.safe-chain/scripts/init-zsh.sh")); }); it("should handle multiple setup calls", () => { - const tools = ["npm"]; - - zsh.setup(tools); - zsh.setup(tools); + zsh.setup(); + zsh.setup(); const content = fs.readFileSync(mockStartupFile, "utf-8"); - const npmMatches = (content.match(/alias npm="/g) || []).length; - assert.strictEqual(npmMatches, 1, "Should not duplicate aliases"); + const sourceMatches = (content.match(/source.*init-zsh\.sh/g) || []) + .length; + assert.strictEqual(sourceMatches, 1, "Should not duplicate source lines"); + }); + + it("should handle mixed content with aliases and source lines", () => { + const initialContent = [ + "#!/bin/zsh", + "alias npm='old-npm'", + "source ~/.safe-chain/scripts/init-zsh.sh", + "alias ls='ls --color=auto'", + ].join("\n"); + + fs.writeFileSync(mockStartupFile, initialContent, "utf-8"); + + // Teardown should remove both aliases and source line + zsh.teardown(); + const content = fs.readFileSync(mockStartupFile, "utf-8"); + assert.ok(!content.includes("alias npm=")); + assert.ok(!content.includes("source ~/.safe-chain/scripts/init-zsh.sh")); + assert.ok(content.includes("alias ls=")); }); }); }); From adc8ffedb6d165fcad336d3a279e04533c63b832 Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Fri, 18 Jul 2025 15:17:19 +0200 Subject: [PATCH 02/18] Fix tests --- package.json | 3 ++- src/shell-integration/supported-shells/zsh.spec.js | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 4ab4c95..7bd518b 100644 --- a/package.json +++ b/package.json @@ -42,5 +42,6 @@ "bugs": { "url": "https://github.com/AikidoSec/safe-chain/issues" }, - "homepage": "https://github.com/AikidoSec/safe-chain#readme" + "homepage": "https://github.com/AikidoSec/safe-chain#readme", + "packageManager": "npm@11.4.1+sha512.fcee43884166b6f9c5d04535fb95650e9708b6948a1f797eddf40e9778646778a518dfa32651b1c62ff36f4ac42becf177ca46ca27d53f24b539190c8d91802b" } diff --git a/src/shell-integration/supported-shells/zsh.spec.js b/src/shell-integration/supported-shells/zsh.spec.js index fbe8a96..4b5a0f1 100644 --- a/src/shell-integration/supported-shells/zsh.spec.js +++ b/src/shell-integration/supported-shells/zsh.spec.js @@ -120,7 +120,7 @@ describe("Zsh shell integration", () => { fs.writeFileSync(mockStartupFile, initialContent, "utf-8"); - const result = zsh.teardown(); + const result = zsh.teardown(knownAikidoTools); assert.strictEqual(result, true); const content = fs.readFileSync(mockStartupFile, "utf-8"); @@ -210,7 +210,7 @@ describe("Zsh shell integration", () => { fs.writeFileSync(mockStartupFile, initialContent, "utf-8"); // Teardown should remove both aliases and source line - zsh.teardown(); + zsh.teardown(knownAikidoTools); const content = fs.readFileSync(mockStartupFile, "utf-8"); assert.ok(!content.includes("alias npm=")); assert.ok(!content.includes("source ~/.safe-chain/scripts/init-zsh.sh")); From 4424ba2e5b2b9eda8b9cef2edf89dcf18f5c451a Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Fri, 18 Jul 2025 15:18:59 +0200 Subject: [PATCH 03/18] Fix package.json --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index 7bd518b..4ab4c95 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,5 @@ "bugs": { "url": "https://github.com/AikidoSec/safe-chain/issues" }, - "homepage": "https://github.com/AikidoSec/safe-chain#readme", - "packageManager": "npm@11.4.1+sha512.fcee43884166b6f9c5d04535fb95650e9708b6948a1f797eddf40e9778646778a518dfa32651b1c62ff36f4ac42becf177ca46ca27d53f24b539190c8d91802b" + "homepage": "https://github.com/AikidoSec/safe-chain#readme" } From 24d4862dfdfd552a5d3e79d088daee90e558aba4 Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Fri, 18 Jul 2025 15:55:58 +0200 Subject: [PATCH 04/18] Fix broken shell integration with nvm for Zsh and Bash --- src/shell-integration/setup.js | 2 +- .../{init-zsh.sh => init-posix.sh} | 0 .../supported-shells/bash.js | 18 ++++-- .../supported-shells/bash.spec.js | 64 +++++++++---------- src/shell-integration/supported-shells/zsh.js | 8 +-- .../supported-shells/zsh.spec.js | 24 ++++--- 6 files changed, 63 insertions(+), 53 deletions(-) rename src/shell-integration/startup-scripts/{init-zsh.sh => init-posix.sh} (100%) diff --git a/src/shell-integration/setup.js b/src/shell-integration/setup.js index 3f9bbb3..999f3d2 100644 --- a/src/shell-integration/setup.js +++ b/src/shell-integration/setup.js @@ -81,7 +81,7 @@ function setupShell(shell) { } function copyStartupFiles() { - const startupFiles = ["init-zsh.sh"]; + const startupFiles = ["init-posix.sh"]; for (const file of startupFiles) { const targetPath = path.join(os.homedir(), ".safe-chain", "scripts", file); diff --git a/src/shell-integration/startup-scripts/init-zsh.sh b/src/shell-integration/startup-scripts/init-posix.sh similarity index 100% rename from src/shell-integration/startup-scripts/init-zsh.sh rename to src/shell-integration/startup-scripts/init-posix.sh diff --git a/src/shell-integration/supported-shells/bash.js b/src/shell-integration/supported-shells/bash.js index 66b844d..3c4b1f9 100644 --- a/src/shell-integration/supported-shells/bash.js +++ b/src/shell-integration/supported-shells/bash.js @@ -21,18 +21,22 @@ function teardown(tools) { removeLinesMatchingPattern(startupFile, new RegExp(`^alias\\s+${tool}=`)); } + // Removes the line that sources the safe-chain bash initialization script (~/.aikido/scripts/init-posix.sh) + removeLinesMatchingPattern( + startupFile, + /^source\s+~\/\.safe-chain\/scripts\/init-posix\.sh/ + ); + return true; } -function setup(tools) { +function setup() { const startupFile = getStartupFile(); - for (const { tool, aikidoCommand } of tools) { - addLineToFile( - startupFile, - `alias ${tool}="${aikidoCommand}" # Safe-chain alias for ${tool}` - ); - } + addLineToFile( + startupFile, + `source ~/.safe-chain/scripts/init-posix.sh # Safe-chain bash initialization script` + ); return true; } diff --git a/src/shell-integration/supported-shells/bash.spec.js b/src/shell-integration/supported-shells/bash.spec.js index ce666e5..e23addb 100644 --- a/src/shell-integration/supported-shells/bash.spec.js +++ b/src/shell-integration/supported-shells/bash.spec.js @@ -66,37 +66,16 @@ describe("Bash shell integration", () => { }); describe("setup", () => { - it("should add aliases for all provided tools", () => { - const tools = [ - { tool: "npm", aikidoCommand: "aikido-npm" }, - { tool: "npx", aikidoCommand: "aikido-npx" }, - { tool: "yarn", aikidoCommand: "aikido-yarn" }, - ]; - - const result = bash.setup(tools); + it("should add source line for bash initialization script", () => { + const result = bash.setup(); assert.strictEqual(result, true); const content = fs.readFileSync(mockStartupFile, "utf-8"); assert.ok( - content.includes('alias npm="aikido-npm" # Safe-chain alias for npm') + content.includes( + "source ~/.safe-chain/scripts/init-posix.sh # Safe-chain bash initialization script" + ) ); - assert.ok( - content.includes('alias npx="aikido-npx" # Safe-chain alias for npx') - ); - assert.ok( - content.includes('alias yarn="aikido-yarn" # Safe-chain alias for yarn') - ); - }); - - it("should handle empty tools array", () => { - const result = bash.setup([]); - assert.strictEqual(result, true); - - // File should be created during teardown call even if no tools are provided - if (fs.existsSync(mockStartupFile)) { - const content = fs.readFileSync(mockStartupFile, "utf-8"); - assert.strictEqual(content.trim(), ""); - } }); }); @@ -174,14 +153,14 @@ describe("Bash shell integration", () => { // Setup bash.setup(tools); let content = fs.readFileSync(mockStartupFile, "utf-8"); - assert.ok(content.includes('alias npm="aikido-npm"')); - assert.ok(content.includes('alias yarn="aikido-yarn"')); + assert.ok(content.includes("source ~/.safe-chain/scripts/init-posix.sh")); // Teardown bash.teardown(tools); content = fs.readFileSync(mockStartupFile, "utf-8"); - assert.ok(!content.includes("alias npm=")); - assert.ok(!content.includes("alias yarn=")); + assert.ok( + !content.includes("source ~/.safe-chain/scripts/init-posix.sh") + ); }); it("should handle multiple setup calls", () => { @@ -192,8 +171,29 @@ describe("Bash shell integration", () => { bash.setup(tools); const content = fs.readFileSync(mockStartupFile, "utf-8"); - const npmMatches = (content.match(/alias npm="/g) || []).length; - assert.strictEqual(npmMatches, 1, "Should not duplicate aliases"); + const sourceMatches = (content.match(/source.*init-posix\.sh/g) || []) + .length; + assert.strictEqual(sourceMatches, 1, "Should not duplicate source lines"); + }); + + it("should handle mixed content with aliases and source lines", () => { + const initialContent = [ + "#!/bin/bash", + "alias npm='old-npm'", + "source ~/.safe-chain/scripts/init-posix.sh", + "alias ls='ls --color=auto'", + ].join("\n"); + + fs.writeFileSync(mockStartupFile, initialContent, "utf-8"); + + // Teardown should remove both aliases and source line + bash.teardown(knownAikidoTools); + const content = fs.readFileSync(mockStartupFile, "utf-8"); + assert.ok(!content.includes("alias npm=")); + assert.ok( + !content.includes("source ~/.safe-chain/scripts/init-posix.sh") + ); + assert.ok(content.includes("alias ls=")); }); }); }); diff --git a/src/shell-integration/supported-shells/zsh.js b/src/shell-integration/supported-shells/zsh.js index 82f03af..b5167fe 100644 --- a/src/shell-integration/supported-shells/zsh.js +++ b/src/shell-integration/supported-shells/zsh.js @@ -21,21 +21,21 @@ function teardown(tools) { removeLinesMatchingPattern(startupFile, new RegExp(`^alias\\s+${tool}=`)); } - // Removes the line that sources the safe-chain zsh initialization script (~/.aikido/scripts/init-zsh.sh) + // Removes the line that sources the safe-chain zsh initialization script (~/.aikido/scripts/init-posix.sh) removeLinesMatchingPattern( startupFile, - /^source\s+~\/\.safe-chain\/scripts\/init-zsh\.sh/ + /^source\s+~\/\.safe-chain\/scripts\/init-posix\.sh/ ); return true; } -function setup(tools) { +function setup() { const startupFile = getStartupFile(); addLineToFile( startupFile, - `source ~/.safe-chain/scripts/init-zsh.sh # Safe-chain Zsh initialization script` + `source ~/.safe-chain/scripts/init-posix.sh # Safe-chain Zsh initialization script` ); return true; diff --git a/src/shell-integration/supported-shells/zsh.spec.js b/src/shell-integration/supported-shells/zsh.spec.js index 4b5a0f1..95c12ac 100644 --- a/src/shell-integration/supported-shells/zsh.spec.js +++ b/src/shell-integration/supported-shells/zsh.spec.js @@ -73,7 +73,7 @@ describe("Zsh shell integration", () => { const content = fs.readFileSync(mockStartupFile, "utf-8"); assert.ok( content.includes( - "source ~/.safe-chain/scripts/init-zsh.sh # Safe-chain Zsh initialization script" + "source ~/.safe-chain/scripts/init-posix.sh # Safe-chain Zsh initialization script" ) ); }); @@ -83,7 +83,7 @@ describe("Zsh shell integration", () => { assert.strictEqual(result, true); const content = fs.readFileSync(mockStartupFile, "utf-8"); - assert.ok(content.includes("source ~/.safe-chain/scripts/init-zsh.sh")); + assert.ok(content.includes("source ~/.safe-chain/scripts/init-posix.sh")); }); }); @@ -114,7 +114,7 @@ describe("Zsh shell integration", () => { it("should remove zsh initialization script source line", () => { const initialContent = [ "#!/bin/zsh", - "source ~/.safe-chain/scripts/init-zsh.sh", + "source ~/.safe-chain/scripts/init-posix.sh", "alias ls='ls --color=auto'", ].join("\n"); @@ -124,7 +124,9 @@ describe("Zsh shell integration", () => { assert.strictEqual(result, true); const content = fs.readFileSync(mockStartupFile, "utf-8"); - assert.ok(!content.includes("source ~/.safe-chain/scripts/init-zsh.sh")); + assert.ok( + !content.includes("source ~/.safe-chain/scripts/init-posix.sh") + ); assert.ok(content.includes("alias ls=")); }); @@ -178,12 +180,14 @@ describe("Zsh shell integration", () => { // Setup zsh.setup(); let content = fs.readFileSync(mockStartupFile, "utf-8"); - assert.ok(content.includes("source ~/.safe-chain/scripts/init-zsh.sh")); + assert.ok(content.includes("source ~/.safe-chain/scripts/init-posix.sh")); // Teardown zsh.teardown(tools); content = fs.readFileSync(mockStartupFile, "utf-8"); - assert.ok(!content.includes("source ~/.safe-chain/scripts/init-zsh.sh")); + assert.ok( + !content.includes("source ~/.safe-chain/scripts/init-posix.sh") + ); }); it("should handle multiple setup calls", () => { @@ -194,7 +198,7 @@ describe("Zsh shell integration", () => { zsh.setup(tools); const content = fs.readFileSync(mockStartupFile, "utf-8"); - const sourceMatches = (content.match(/source.*init-zsh\.sh/g) || []) + const sourceMatches = (content.match(/source.*init-posix\.sh/g) || []) .length; assert.strictEqual(sourceMatches, 1, "Should not duplicate source lines"); }); @@ -203,7 +207,7 @@ describe("Zsh shell integration", () => { const initialContent = [ "#!/bin/zsh", "alias npm='old-npm'", - "source ~/.safe-chain/scripts/init-zsh.sh", + "source ~/.safe-chain/scripts/init-posix.sh", "alias ls='ls --color=auto'", ].join("\n"); @@ -213,7 +217,9 @@ describe("Zsh shell integration", () => { zsh.teardown(knownAikidoTools); const content = fs.readFileSync(mockStartupFile, "utf-8"); assert.ok(!content.includes("alias npm=")); - assert.ok(!content.includes("source ~/.safe-chain/scripts/init-zsh.sh")); + assert.ok( + !content.includes("source ~/.safe-chain/scripts/init-posix.sh") + ); assert.ok(content.includes("alias ls=")); }); }); From eba1e9cc8eace78323f6ac489369c1859a5aa194 Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Fri, 18 Jul 2025 15:58:49 +0200 Subject: [PATCH 05/18] Add wrappers for pnpm and pnpx in the script --- src/shell-integration/startup-scripts/init-posix.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/shell-integration/startup-scripts/init-posix.sh b/src/shell-integration/startup-scripts/init-posix.sh index b936eeb..09a5926 100644 --- a/src/shell-integration/startup-scripts/init-posix.sh +++ b/src/shell-integration/startup-scripts/init-posix.sh @@ -69,6 +69,14 @@ function yarn() { wrapCommand "yarn" "aikido-yarn" "$@" } +function pnpm() { + wrapCommand "pnpm" "aikido-pnpm" "$@" +} + +function pnpx() { + wrapCommand "pnpx" "aikido-pnpx" "$@" +} + function npm() { if [[ "$1" == "-v" || "$1" == "--version" ]] && [[ $# -eq 1 ]]; then # If args is just -v or --version and nothing else, just run the npm version command From 577b09bd39c3882da1e5b1fba499dc4f4bcb65e8 Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Wed, 23 Jul 2025 11:16:38 +0200 Subject: [PATCH 06/18] Use powershell functions to wrap npm, npx, yarn, pnpm and pnpx --- src/shell-integration/setup.js | 2 +- .../startup-scripts/init-pwsh.ps1 | 122 ++++++++++++++++++ .../supported-shells/powershell.js | 18 ++- .../supported-shells/powershell.spec.js | 87 ++++++------- .../supported-shells/windowsPowershell.js | 18 ++- .../windowsPowershell.spec.js | 87 ++++++------- 6 files changed, 217 insertions(+), 117 deletions(-) create mode 100644 src/shell-integration/startup-scripts/init-pwsh.ps1 diff --git a/src/shell-integration/setup.js b/src/shell-integration/setup.js index 999f3d2..4cc2968 100644 --- a/src/shell-integration/setup.js +++ b/src/shell-integration/setup.js @@ -81,7 +81,7 @@ function setupShell(shell) { } function copyStartupFiles() { - const startupFiles = ["init-posix.sh"]; + const startupFiles = ["init-posix.sh", "init-pwsh.ps1"]; for (const file of startupFiles) { const targetPath = path.join(os.homedir(), ".safe-chain", "scripts", file); diff --git a/src/shell-integration/startup-scripts/init-pwsh.ps1 b/src/shell-integration/startup-scripts/init-pwsh.ps1 new file mode 100644 index 0000000..58a19c9 --- /dev/null +++ b/src/shell-integration/startup-scripts/init-pwsh.ps1 @@ -0,0 +1,122 @@ +function Test-CommandAvailable { + param([string]$Command) + + try { + Get-Command $Command -ErrorAction Stop | Out-Null + return $true + } catch { + return $false + } +} + +function Invoke-RealCommand { + param( + [string]$Command, + [string[]]$Arguments + ) + + # Find the real executable to avoid calling our wrapped functions + $realCommand = Get-Command -Name $Command -CommandType Application | Select-Object -First 1 + if ($realCommand) { + & $realCommand.Source @Arguments + } else { + # Fallback: try to call the .cmd version directly + & "$Command.cmd" @Arguments + } +} + +function Install-IfCommandNotFound { + param([string]$Command) + + # Check if the command already exists + if (Test-CommandAvailable $Command) { + return 0 + } + + # Check if Node.js version is below 18 + # Safe-chain requires Node.js 18 or higher + try { + $nodeVersion = (node -v) -replace 'v', '' | ForEach-Object { $_.Split('.')[0] } + if ([int]$nodeVersion -lt 18) { + return 2 + } + } catch { + return 2 + } + + # Command not found, ask user if they want to install safe-chain + $response = Read-Host "The command '$Command' is not available. Do you want to install safe-chain to provide it? (y/N)" + + if ($response -match '^[Yy]$') { + Write-Host "Installing safe-chain..." + $installResult = Install-SafeChain + + if ($installResult -ne 0) { + Write-Host "`nFailed to install safe-chain. Exiting." + return 1 + } + + return 0 + } else { + Write-Host "Skipping safe-chain installation. Using original command instead." + return 2 + } +} + +function Install-SafeChain { + try { + Invoke-RealCommand "npm" @("install", "-g", "@aikidosec/safe-chain") | Out-Null + + if ($LASTEXITCODE -ne 0) { + return 1 + } + + Write-Host "------" + return 0 + } catch { + return 1 + } +} + +function Invoke-WrappedCommand { + param( + [string]$OriginalCmd, + [string]$AikidoCmd, + [string[]]$Arguments + ) + + $installResult = Install-IfCommandNotFound $AikidoCmd + + if ($installResult -eq 2) { + Invoke-RealCommand $OriginalCmd $Arguments + } else { + & $AikidoCmd @Arguments + } +} + +function npx { + Invoke-WrappedCommand "npx" "aikido-npx" $args +} + +function yarn { + Invoke-WrappedCommand "yarn" "aikido-yarn" $args +} + +function pnpm { + Invoke-WrappedCommand "pnpm" "aikido-pnpm" $args +} + +function pnpx { + Invoke-WrappedCommand "pnpx" "aikido-pnpx" $args +} + +function npm { + # If args is just -v or --version and nothing else, just run the npm version command + # This is because nvm uses this to check the version of npm + if (($args.Length -eq 1) -and (($args[0] -eq "-v") -or ($args[0] -eq "--version"))) { + Invoke-RealCommand "npm" $args + return + } + + Invoke-WrappedCommand "npm" "aikido-npm" $args +} \ No newline at end of file diff --git a/src/shell-integration/supported-shells/powershell.js b/src/shell-integration/supported-shells/powershell.js index 4690bb6..47524c2 100644 --- a/src/shell-integration/supported-shells/powershell.js +++ b/src/shell-integration/supported-shells/powershell.js @@ -24,18 +24,22 @@ function teardown(tools) { ); } + // Remove the line that sources the safe-chain PowerShell initialization script + removeLinesMatchingPattern( + startupFile, + /^\.\s+["']?\$HOME[/\\].safe-chain[/\\]scripts[/\\]init-pwsh\.ps1["']?/ + ); + return true; } -function setup(tools) { +function setup() { const startupFile = getStartupFile(); - for (const { tool, aikidoCommand } of tools) { - addLineToFile( - startupFile, - `Set-Alias ${tool} ${aikidoCommand} # Safe-chain alias for ${tool}` - ); - } + addLineToFile( + startupFile, + `. "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1" # Safe-chain PowerShell initialization script` + ); return true; } diff --git a/src/shell-integration/supported-shells/powershell.spec.js b/src/shell-integration/supported-shells/powershell.spec.js index 9afade7..57d5098 100644 --- a/src/shell-integration/supported-shells/powershell.spec.js +++ b/src/shell-integration/supported-shells/powershell.spec.js @@ -69,49 +69,43 @@ describe("PowerShell Core shell integration", () => { }); describe("setup", () => { - it("should add aliases for all provided tools", () => { - const tools = [ - { tool: "npm", aikidoCommand: "aikido-npm" }, - { tool: "npx", aikidoCommand: "aikido-npx" }, - { tool: "yarn", aikidoCommand: "aikido-yarn" }, - ]; - - const result = powershell.setup(tools); + it("should add init-pwsh.ps1 source line", () => { + const result = powershell.setup(); assert.strictEqual(result, true); const content = fs.readFileSync(mockStartupFile, "utf-8"); assert.ok( - content.includes("Set-Alias npm aikido-npm # Safe-chain alias for npm") + content.includes(". \"$HOME\\\\.safe-chain\\\\scripts\\\\init-pwsh.ps1\" # Safe-chain PowerShell initialization script") ); - assert.ok( - content.includes("Set-Alias npx aikido-npx # Safe-chain alias for npx") - ); - assert.ok( - content.includes( - "Set-Alias yarn aikido-yarn # Safe-chain alias for yarn" - ) - ); - }); - - it("should handle empty tools array", () => { - const result = powershell.setup([]); - assert.strictEqual(result, true); - - // File should be created during teardown call even if no tools are provided - if (fs.existsSync(mockStartupFile)) { - const content = fs.readFileSync(mockStartupFile, "utf-8"); - assert.strictEqual(content.trim(), ""); - } }); }); describe("teardown", () => { - it("should remove npm, npx, and yarn aliases", () => { + it("should remove init-pwsh.ps1 source line", () => { const initialContent = [ "# PowerShell profile", - "Set-Alias npm aikido-npm", - "Set-Alias npx aikido-npx", - "Set-Alias yarn aikido-yarn", + ". \"$HOME\\\\.safe-chain\\\\scripts\\\\init-pwsh.ps1\" # Safe-chain PowerShell initialization script", + "Set-Alias ls Get-ChildItem", + "Set-Alias grep Select-String", + ].join("\n"); + + fs.writeFileSync(mockStartupFile, initialContent, "utf-8"); + + const result = powershell.teardown(knownAikidoTools); + assert.strictEqual(result, true); + + const content = fs.readFileSync(mockStartupFile, "utf-8"); + assert.ok(!content.includes(". \"$HOME\\\\.safe-chain\\\\scripts\\\\init-pwsh.ps1\"")); + assert.ok(content.includes("Set-Alias ls ")); + assert.ok(content.includes("Set-Alias grep ")); + }); + + it("should remove old-style aliases from earlier versions", () => { + const initialContent = [ + "# PowerShell profile", + "Set-Alias npm aikido-npm # Safe-chain alias for npm", + "Set-Alias npx aikido-npx # Safe-chain alias for npx", + "Set-Alias yarn aikido-yarn # Safe-chain alias for yarn", "Set-Alias ls Get-ChildItem", "Set-Alias grep Select-String", ].join("\n"); @@ -138,7 +132,7 @@ describe("PowerShell Core shell integration", () => { assert.strictEqual(result, true); }); - it("should handle file with no relevant aliases", () => { + it("should handle file with no relevant content", () => { const initialContent = [ "# PowerShell profile", "Set-Alias ls Get-ChildItem", @@ -171,34 +165,25 @@ describe("PowerShell Core shell integration", () => { describe("integration tests", () => { it("should handle complete setup and teardown cycle", () => { - const tools = [ - { tool: "npm", aikidoCommand: "aikido-npm" }, - { tool: "yarn", aikidoCommand: "aikido-yarn" }, - ]; - // Setup - powershell.setup(tools); + powershell.setup(); let content = fs.readFileSync(mockStartupFile, "utf-8"); - assert.ok(content.includes("Set-Alias npm aikido-npm")); - assert.ok(content.includes("Set-Alias yarn aikido-yarn")); + assert.ok(content.includes(". \"$HOME\\\\.safe-chain\\\\scripts\\\\init-pwsh.ps1\"")); // Teardown - powershell.teardown(tools); + powershell.teardown(knownAikidoTools); content = fs.readFileSync(mockStartupFile, "utf-8"); - assert.ok(!content.includes("Set-Alias npm ")); - assert.ok(!content.includes("Set-Alias yarn ")); + assert.ok(!content.includes(". \"$HOME\\\\.safe-chain\\\\scripts\\\\init-pwsh.ps1\"")); }); it("should handle multiple setup calls", () => { - const tools = [{ tool: "npm", aikidoCommand: "aikido-npm" }]; - - powershell.setup(tools); - powershell.teardown(tools); - powershell.setup(tools); + powershell.setup(); + powershell.teardown(knownAikidoTools); + powershell.setup(); const content = fs.readFileSync(mockStartupFile, "utf-8"); - const npmMatches = (content.match(/Set-Alias npm /g) || []).length; - assert.strictEqual(npmMatches, 1, "Should not duplicate aliases"); + const sourceMatches = (content.match(/\. "\$HOME\\\\.safe-chain\\\\scripts\\\\init-pwsh\.ps1"/g) || []).length; + assert.strictEqual(sourceMatches, 1, "Should not duplicate source lines"); }); }); }); diff --git a/src/shell-integration/supported-shells/windowsPowershell.js b/src/shell-integration/supported-shells/windowsPowershell.js index 118a0b9..03ff7f8 100644 --- a/src/shell-integration/supported-shells/windowsPowershell.js +++ b/src/shell-integration/supported-shells/windowsPowershell.js @@ -24,18 +24,22 @@ function teardown(tools) { ); } + // Remove the line that sources the safe-chain PowerShell initialization script + removeLinesMatchingPattern( + startupFile, + /^\.\s+["']?\$HOME[/\\].safe-chain[/\\]scripts[/\\]init-pwsh\.ps1["']?/ + ); + return true; } -function setup(tools) { +function setup() { const startupFile = getStartupFile(); - for (const { tool, aikidoCommand } of tools) { - addLineToFile( - startupFile, - `Set-Alias ${tool} ${aikidoCommand} # Safe-chain alias for ${tool}` - ); - } + addLineToFile( + startupFile, + `. "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1" # Safe-chain PowerShell initialization script` + ); return true; } diff --git a/src/shell-integration/supported-shells/windowsPowershell.spec.js b/src/shell-integration/supported-shells/windowsPowershell.spec.js index 85da9f1..96677a8 100644 --- a/src/shell-integration/supported-shells/windowsPowershell.spec.js +++ b/src/shell-integration/supported-shells/windowsPowershell.spec.js @@ -69,49 +69,43 @@ describe("Windows PowerShell shell integration", () => { }); describe("setup", () => { - it("should add aliases for all provided tools", () => { - const tools = [ - { tool: "npm", aikidoCommand: "aikido-npm" }, - { tool: "npx", aikidoCommand: "aikido-npx" }, - { tool: "yarn", aikidoCommand: "aikido-yarn" }, - ]; - - const result = windowsPowershell.setup(tools); + it("should add init-pwsh.ps1 source line", () => { + const result = windowsPowershell.setup(); assert.strictEqual(result, true); const content = fs.readFileSync(mockStartupFile, "utf-8"); assert.ok( - content.includes("Set-Alias npm aikido-npm # Safe-chain alias for npm") + content.includes(". \"$HOME\\\\.safe-chain\\\\scripts\\\\init-pwsh.ps1\" # Safe-chain PowerShell initialization script") ); - assert.ok( - content.includes("Set-Alias npx aikido-npx # Safe-chain alias for npx") - ); - assert.ok( - content.includes( - "Set-Alias yarn aikido-yarn # Safe-chain alias for yarn" - ) - ); - }); - - it("should handle empty tools array", () => { - const result = windowsPowershell.setup([]); - assert.strictEqual(result, true); - - // File should be created during teardown call even if no tools are provided - if (fs.existsSync(mockStartupFile)) { - const content = fs.readFileSync(mockStartupFile, "utf-8"); - assert.strictEqual(content.trim(), ""); - } }); }); describe("teardown", () => { - it("should remove npm, npx, and yarn aliases", () => { + it("should remove init-pwsh.ps1 source line", () => { const initialContent = [ "# Windows PowerShell profile", - "Set-Alias npm aikido-npm", - "Set-Alias npx aikido-npx", - "Set-Alias yarn aikido-yarn", + ". \"$HOME\\\\.safe-chain\\\\scripts\\\\init-pwsh.ps1\" # Safe-chain PowerShell initialization script", + "Set-Alias ls Get-ChildItem", + "Set-Alias grep Select-String", + ].join("\n"); + + fs.writeFileSync(mockStartupFile, initialContent, "utf-8"); + + const result = windowsPowershell.teardown(knownAikidoTools); + assert.strictEqual(result, true); + + const content = fs.readFileSync(mockStartupFile, "utf-8"); + assert.ok(!content.includes(". \"$HOME\\\\.safe-chain\\\\scripts\\\\init-pwsh.ps1\"")); + assert.ok(content.includes("Set-Alias ls ")); + assert.ok(content.includes("Set-Alias grep ")); + }); + + it("should remove old-style aliases from earlier versions", () => { + const initialContent = [ + "# Windows PowerShell profile", + "Set-Alias npm aikido-npm # Safe-chain alias for npm", + "Set-Alias npx aikido-npx # Safe-chain alias for npx", + "Set-Alias yarn aikido-yarn # Safe-chain alias for yarn", "Set-Alias ls Get-ChildItem", "Set-Alias grep Select-String", ].join("\n"); @@ -138,7 +132,7 @@ describe("Windows PowerShell shell integration", () => { assert.strictEqual(result, true); }); - it("should handle file with no relevant aliases", () => { + it("should handle file with no relevant content", () => { const initialContent = [ "# Windows PowerShell profile", "Set-Alias ls Get-ChildItem", @@ -171,34 +165,25 @@ describe("Windows PowerShell shell integration", () => { describe("integration tests", () => { it("should handle complete setup and teardown cycle", () => { - const tools = [ - { tool: "npm", aikidoCommand: "aikido-npm" }, - { tool: "yarn", aikidoCommand: "aikido-yarn" }, - ]; - // Setup - windowsPowershell.setup(tools); + windowsPowershell.setup(); let content = fs.readFileSync(mockStartupFile, "utf-8"); - assert.ok(content.includes("Set-Alias npm aikido-npm")); - assert.ok(content.includes("Set-Alias yarn aikido-yarn")); + assert.ok(content.includes(". \"$HOME\\\\.safe-chain\\\\scripts\\\\init-pwsh.ps1\"")); // Teardown - windowsPowershell.teardown(tools); + windowsPowershell.teardown(knownAikidoTools); content = fs.readFileSync(mockStartupFile, "utf-8"); - assert.ok(!content.includes("Set-Alias npm ")); - assert.ok(!content.includes("Set-Alias yarn ")); + assert.ok(!content.includes(". \"$HOME\\\\.safe-chain\\\\scripts\\\\init-pwsh.ps1\"")); }); it("should handle multiple setup calls", () => { - const tools = [{ tool: "npm", aikidoCommand: "aikido-npm" }]; - - windowsPowershell.setup(tools); - windowsPowershell.teardown(tools); - windowsPowershell.setup(tools); + windowsPowershell.setup(); + windowsPowershell.teardown(knownAikidoTools); + windowsPowershell.setup(); const content = fs.readFileSync(mockStartupFile, "utf-8"); - const npmMatches = (content.match(/Set-Alias npm /g) || []).length; - assert.strictEqual(npmMatches, 1, "Should not duplicate aliases"); + const sourceMatches = (content.match(/\. "\$HOME\\\\.safe-chain\\\\scripts\\\\init-pwsh\.ps1"/g) || []).length; + assert.strictEqual(sourceMatches, 1, "Should not duplicate source lines"); }); }); }); From 248bb9f7b6cc0d2e74de6326da1a4388295c71a8 Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Wed, 23 Jul 2025 11:19:53 +0200 Subject: [PATCH 07/18] Take environment path separators into account when getting the target dir --- src/shell-integration/setup.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/shell-integration/setup.js b/src/shell-integration/setup.js index 999f3d2..9339ef5 100644 --- a/src/shell-integration/setup.js +++ b/src/shell-integration/setup.js @@ -84,10 +84,9 @@ function copyStartupFiles() { const startupFiles = ["init-posix.sh"]; for (const file of startupFiles) { + const targetDir = path.join(os.homedir(), ".safe-chain", "scripts"); const targetPath = path.join(os.homedir(), ".safe-chain", "scripts", file); - // Create target directory if it doesn't exist - const targetDir = targetPath.substring(0, targetPath.lastIndexOf("/")); if (!fs.existsSync(targetDir)) { fs.mkdirSync(targetDir, { recursive: true }); } From ca5d3ecb2a2b62266e30463deaf03dd65c329b56 Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Wed, 23 Jul 2025 11:36:45 +0200 Subject: [PATCH 08/18] Use functions to wrap package managers and detect if the aikido commands are available --- src/shell-integration/setup.js | 2 +- .../startup-scripts/init-fish.fish | 85 +++++++++++++++++++ .../supported-shells/fish.js | 18 ++-- .../supported-shells/fish.spec.js | 58 +++++-------- 4 files changed, 118 insertions(+), 45 deletions(-) create mode 100644 src/shell-integration/startup-scripts/init-fish.fish diff --git a/src/shell-integration/setup.js b/src/shell-integration/setup.js index ada658b..792aa3c 100644 --- a/src/shell-integration/setup.js +++ b/src/shell-integration/setup.js @@ -81,7 +81,7 @@ function setupShell(shell) { } function copyStartupFiles() { - const startupFiles = ["init-posix.sh", "init-pwsh.ps1"]; + const startupFiles = ["init-posix.sh", "init-pwsh.ps1", "init-fish.fish"]; for (const file of startupFiles) { const targetDir = path.join(os.homedir(), ".safe-chain", "scripts"); diff --git a/src/shell-integration/startup-scripts/init-fish.fish b/src/shell-integration/startup-scripts/init-fish.fish new file mode 100644 index 0000000..3a6c18e --- /dev/null +++ b/src/shell-integration/startup-scripts/init-fish.fish @@ -0,0 +1,85 @@ +function installIfCommandNotFound + set cmd $argv[1] + + # Check if the command already exists + if type -q $cmd + return 0 + end + + # Check if Node.js version is below 18 + # Safe-chain requires Node.js 18 or higher + set node_version (node -v | sed 's/v//' | cut -d'.' -f1) + if test $node_version -lt 18 + return 2 + end + + # Command not found, ask user if they want to install safe-chain + read -l response -P "The command '$cmd' is not available. Do you want to install safe-chain to provide it? (y/N): " + + if string match -qi 'y*' $response + printf "Installing safe-chain...\n" + installSafeChain + + if test $status -ne 0 + printf "\nFailed to install safe-chain. Exiting.\n" + return 1 + end + + return 0 + else + printf "Skipping safe-chain installation. Using original command instead.\n" + return 2 + end +end + +function installSafeChain + command npm install -g @aikidosec/safe-chain + + if test $status -ne 0 + return 1 + end + + printf "------\n" +end + +function wrapCommand + set original_cmd $argv[1] + set aikido_cmd $argv[2] + set cmd_args $argv[3..-1] + + installIfCommandNotFound $aikido_cmd + set install_result $status + + if test $install_result -eq 2 + command $original_cmd $cmd_args + else + $aikido_cmd $cmd_args + end +end + +function npx + wrapCommand "npx" "aikido-npx" $argv +end + +function yarn + wrapCommand "yarn" "aikido-yarn" $argv +end + +function pnpm + wrapCommand "pnpm" "aikido-pnpm" $argv +end + +function pnpx + wrapCommand "pnpx" "aikido-pnpx" $argv +end + +function npm + if test (count $argv) -eq 1 -a \( "$argv[1]" = "-v" -o "$argv[1]" = "--version" \) + # If args is just -v or --version and nothing else, just run the npm version command + # This is because nvm uses this to check the version of npm + command npm $argv + return + end + + wrapCommand "npm" "aikido-npm" $argv +end \ No newline at end of file diff --git a/src/shell-integration/supported-shells/fish.js b/src/shell-integration/supported-shells/fish.js index fc6fc85..7b2c683 100644 --- a/src/shell-integration/supported-shells/fish.js +++ b/src/shell-integration/supported-shells/fish.js @@ -24,18 +24,22 @@ function teardown(tools) { ); } + // Removes the line that sources the safe-chain fish initialization script (~/.safe-chain/scripts/init-fish.fish) + removeLinesMatchingPattern( + startupFile, + /^source\s+~\/\.safe-chain\/scripts\/init-fish\.fish/ + ); + return true; } -function setup(tools) { +function setup() { const startupFile = getStartupFile(); - for (const { tool, aikidoCommand } of tools) { - addLineToFile( - startupFile, - `alias ${tool} "${aikidoCommand}" # Safe-chain alias for ${tool}` - ); - } + addLineToFile( + startupFile, + `source ~/.safe-chain/scripts/init-fish.fish # Safe-chain Fish initialization script` + ); return true; } diff --git a/src/shell-integration/supported-shells/fish.spec.js b/src/shell-integration/supported-shells/fish.spec.js index 5f1ab64..e138957 100644 --- a/src/shell-integration/supported-shells/fish.spec.js +++ b/src/shell-integration/supported-shells/fish.spec.js @@ -66,47 +66,34 @@ describe("Fish shell integration", () => { }); describe("setup", () => { - it("should add aliases for all provided tools", () => { - const tools = [ - { tool: "npm", aikidoCommand: "aikido-npm" }, - { tool: "npx", aikidoCommand: "aikido-npx" }, - { tool: "yarn", aikidoCommand: "aikido-yarn" }, - ]; - - const result = fish.setup(tools); + it("should add source line for safe-chain fish initialization script", () => { + const result = fish.setup(); assert.strictEqual(result, true); const content = fs.readFileSync(mockStartupFile, "utf-8"); assert.ok( - content.includes('alias npm "aikido-npm" # Safe-chain alias for npm') - ); - assert.ok( - content.includes('alias npx "aikido-npx" # Safe-chain alias for npx') - ); - assert.ok( - content.includes('alias yarn "aikido-yarn" # Safe-chain alias for yarn') + content.includes('source ~/.safe-chain/scripts/init-fish.fish # Safe-chain Fish initialization script') ); }); - it("should handle empty tools array", () => { - const result = fish.setup([]); - assert.strictEqual(result, true); + it("should not duplicate source lines on multiple calls", () => { + fish.setup(); + fish.setup(); - // File should be created during teardown call even if no tools are provided - if (fs.existsSync(mockStartupFile)) { - const content = fs.readFileSync(mockStartupFile, "utf-8"); - assert.strictEqual(content.trim(), ""); - } + const content = fs.readFileSync(mockStartupFile, "utf-8"); + const sourceMatches = (content.match(/source ~\/\.safe-chain\/scripts\/init-fish\.fish/g) || []).length; + assert.strictEqual(sourceMatches, 2, "Should allow multiple source lines (helper doesn't dedupe)"); }); }); describe("teardown", () => { - it("should remove npm, npx, and yarn aliases", () => { + it("should remove npm, npx, yarn aliases and source line", () => { const initialContent = [ "#!/usr/bin/env fish", "alias npm 'aikido-npm'", "alias npx 'aikido-npx'", "alias yarn 'aikido-yarn'", + "source ~/.safe-chain/scripts/init-fish.fish # Safe-chain Fish initialization script", "alias ls 'ls --color=auto'", "alias grep 'grep --color=auto'", ].join("\n"); @@ -120,6 +107,7 @@ describe("Fish shell integration", () => { assert.ok(!content.includes("alias npm ")); assert.ok(!content.includes("alias npx ")); assert.ok(!content.includes("alias yarn ")); + assert.ok(!content.includes("source ~/.safe-chain/scripts/init-fish.fish")); assert.ok(content.includes("alias ls ")); assert.ok(content.includes("alias grep ")); }); @@ -133,7 +121,7 @@ describe("Fish shell integration", () => { assert.strictEqual(result, true); }); - it("should handle file with no relevant aliases", () => { + it("should handle file with no relevant aliases or source lines", () => { const initialContent = [ "#!/usr/bin/env fish", "alias ls 'ls --color=auto'", @@ -172,28 +160,24 @@ describe("Fish shell integration", () => { ]; // Setup - fish.setup(tools); + fish.setup(); let content = fs.readFileSync(mockStartupFile, "utf-8"); - assert.ok(content.includes('alias npm "aikido-npm"')); - assert.ok(content.includes('alias yarn "aikido-yarn"')); + assert.ok(content.includes('source ~/.safe-chain/scripts/init-fish.fish')); // Teardown fish.teardown(tools); content = fs.readFileSync(mockStartupFile, "utf-8"); - assert.ok(!content.includes("alias npm ")); - assert.ok(!content.includes("alias yarn ")); + assert.ok(!content.includes("source ~/.safe-chain/scripts/init-fish.fish")); }); it("should handle multiple setup calls", () => { - const tools = [{ tool: "npm", aikidoCommand: "aikido-npm" }]; - - fish.setup(tools); - fish.teardown(tools); - fish.setup(tools); + fish.setup(); + fish.teardown(knownAikidoTools); + fish.setup(); const content = fs.readFileSync(mockStartupFile, "utf-8"); - const npmMatches = (content.match(/alias npm "/g) || []).length; - assert.strictEqual(npmMatches, 1, "Should not duplicate aliases"); + const sourceMatches = (content.match(/source ~\/\.safe-chain\/scripts\/init-fish\.fish/g) || []).length; + assert.strictEqual(sourceMatches, 1, "Should have exactly one source line after setup-teardown-setup cycle"); }); }); }); From 5df4671988f116f602e5eb089b2fde00dee97712 Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Wed, 23 Jul 2025 11:43:13 +0200 Subject: [PATCH 09/18] Fix paths in tests --- .../supported-shells/powershell.spec.js | 23 ++++++++++++++----- .../windowsPowershell.spec.js | 23 ++++++++++++++----- 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/src/shell-integration/supported-shells/powershell.spec.js b/src/shell-integration/supported-shells/powershell.spec.js index 57d5098..3a15376 100644 --- a/src/shell-integration/supported-shells/powershell.spec.js +++ b/src/shell-integration/supported-shells/powershell.spec.js @@ -75,7 +75,9 @@ describe("PowerShell Core shell integration", () => { const content = fs.readFileSync(mockStartupFile, "utf-8"); assert.ok( - content.includes(". \"$HOME\\\\.safe-chain\\\\scripts\\\\init-pwsh.ps1\" # Safe-chain PowerShell initialization script") + content.includes( + '. "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1" # Safe-chain PowerShell initialization script' + ) ); }); }); @@ -84,7 +86,7 @@ describe("PowerShell Core shell integration", () => { it("should remove init-pwsh.ps1 source line", () => { const initialContent = [ "# PowerShell profile", - ". \"$HOME\\\\.safe-chain\\\\scripts\\\\init-pwsh.ps1\" # Safe-chain PowerShell initialization script", + '. "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1" # Safe-chain PowerShell initialization script', "Set-Alias ls Get-ChildItem", "Set-Alias grep Select-String", ].join("\n"); @@ -95,7 +97,9 @@ describe("PowerShell Core shell integration", () => { assert.strictEqual(result, true); const content = fs.readFileSync(mockStartupFile, "utf-8"); - assert.ok(!content.includes(". \"$HOME\\\\.safe-chain\\\\scripts\\\\init-pwsh.ps1\"")); + assert.ok( + !content.includes('. "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1"') + ); assert.ok(content.includes("Set-Alias ls ")); assert.ok(content.includes("Set-Alias grep ")); }); @@ -168,12 +172,16 @@ describe("PowerShell Core shell integration", () => { // Setup powershell.setup(); let content = fs.readFileSync(mockStartupFile, "utf-8"); - assert.ok(content.includes(". \"$HOME\\\\.safe-chain\\\\scripts\\\\init-pwsh.ps1\"")); + assert.ok( + content.includes('. "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1"') + ); // Teardown powershell.teardown(knownAikidoTools); content = fs.readFileSync(mockStartupFile, "utf-8"); - assert.ok(!content.includes(". \"$HOME\\\\.safe-chain\\\\scripts\\\\init-pwsh.ps1\"")); + assert.ok( + !content.includes('. "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1"') + ); }); it("should handle multiple setup calls", () => { @@ -182,7 +190,10 @@ describe("PowerShell Core shell integration", () => { powershell.setup(); const content = fs.readFileSync(mockStartupFile, "utf-8"); - const sourceMatches = (content.match(/\. "\$HOME\\\\.safe-chain\\\\scripts\\\\init-pwsh\.ps1"/g) || []).length; + const sourceMatches = ( + content.match(/\. "\$HOME\\.safe-chain\\scripts\\init-pwsh\.ps1"/g) || + [] + ).length; assert.strictEqual(sourceMatches, 1, "Should not duplicate source lines"); }); }); diff --git a/src/shell-integration/supported-shells/windowsPowershell.spec.js b/src/shell-integration/supported-shells/windowsPowershell.spec.js index 96677a8..c201c60 100644 --- a/src/shell-integration/supported-shells/windowsPowershell.spec.js +++ b/src/shell-integration/supported-shells/windowsPowershell.spec.js @@ -75,7 +75,9 @@ describe("Windows PowerShell shell integration", () => { const content = fs.readFileSync(mockStartupFile, "utf-8"); assert.ok( - content.includes(". \"$HOME\\\\.safe-chain\\\\scripts\\\\init-pwsh.ps1\" # Safe-chain PowerShell initialization script") + content.includes( + '. "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1" # Safe-chain PowerShell initialization script' + ) ); }); }); @@ -84,7 +86,7 @@ describe("Windows PowerShell shell integration", () => { it("should remove init-pwsh.ps1 source line", () => { const initialContent = [ "# Windows PowerShell profile", - ". \"$HOME\\\\.safe-chain\\\\scripts\\\\init-pwsh.ps1\" # Safe-chain PowerShell initialization script", + '. "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1" # Safe-chain PowerShell initialization script', "Set-Alias ls Get-ChildItem", "Set-Alias grep Select-String", ].join("\n"); @@ -95,7 +97,9 @@ describe("Windows PowerShell shell integration", () => { assert.strictEqual(result, true); const content = fs.readFileSync(mockStartupFile, "utf-8"); - assert.ok(!content.includes(". \"$HOME\\\\.safe-chain\\\\scripts\\\\init-pwsh.ps1\"")); + assert.ok( + !content.includes('. "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1"') + ); assert.ok(content.includes("Set-Alias ls ")); assert.ok(content.includes("Set-Alias grep ")); }); @@ -168,12 +172,16 @@ describe("Windows PowerShell shell integration", () => { // Setup windowsPowershell.setup(); let content = fs.readFileSync(mockStartupFile, "utf-8"); - assert.ok(content.includes(". \"$HOME\\\\.safe-chain\\\\scripts\\\\init-pwsh.ps1\"")); + assert.ok( + content.includes('. "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1"') + ); // Teardown windowsPowershell.teardown(knownAikidoTools); content = fs.readFileSync(mockStartupFile, "utf-8"); - assert.ok(!content.includes(". \"$HOME\\\\.safe-chain\\\\scripts\\\\init-pwsh.ps1\"")); + assert.ok( + !content.includes('. "$HOME\\.safe-chain\\scripts\\init-pwsh.ps1"') + ); }); it("should handle multiple setup calls", () => { @@ -182,7 +190,10 @@ describe("Windows PowerShell shell integration", () => { windowsPowershell.setup(); const content = fs.readFileSync(mockStartupFile, "utf-8"); - const sourceMatches = (content.match(/\. "\$HOME\\\\.safe-chain\\\\scripts\\\\init-pwsh\.ps1"/g) || []).length; + const sourceMatches = ( + content.match(/\. "\$HOME\\.safe-chain\\scripts\\init-pwsh\.ps1"/g) || + [] + ).length; assert.strictEqual(sourceMatches, 1, "Should not duplicate source lines"); }); }); From d9ea0e2efca4055347a0fbacc721411733e9811a Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Thu, 24 Jul 2025 16:57:10 +0200 Subject: [PATCH 10/18] Modify posix script: don't automatically install --- package.json | 3 +- .../startup-scripts/init-posix.sh | 75 +++++-------------- 2 files changed, 22 insertions(+), 56 deletions(-) diff --git a/package.json b/package.json index 4ab4c95..7bd518b 100644 --- a/package.json +++ b/package.json @@ -42,5 +42,6 @@ "bugs": { "url": "https://github.com/AikidoSec/safe-chain/issues" }, - "homepage": "https://github.com/AikidoSec/safe-chain#readme" + "homepage": "https://github.com/AikidoSec/safe-chain#readme", + "packageManager": "npm@11.4.1+sha512.fcee43884166b6f9c5d04535fb95650e9708b6948a1f797eddf40e9778646778a518dfa32651b1c62ff36f4ac42becf177ca46ca27d53f24b539190c8d91802b" } diff --git a/src/shell-integration/startup-scripts/init-posix.sh b/src/shell-integration/startup-scripts/init-posix.sh index 09a5926..3b6d13b 100644 --- a/src/shell-integration/startup-scripts/init-posix.sh +++ b/src/shell-integration/startup-scripts/init-posix.sh @@ -1,80 +1,45 @@ -function installIfCommandNotFound() { - local cmd="$1" - - # Check if the command already exists - if command -v "$cmd" > /dev/null 2>&1; then - return 0 - fi - - # Check if Node.js version is below 18 - # Safe-chain requires Node.js 18 or higher - local node_version=$(node -v | sed 's/v//' | cut -d'.' -f1) - if [ "$node_version" -lt 18 ]; then - return 2 - fi - - # Command not found, ask user if they want to install safe-chain - printf "The command '%s' is not available. Do you want to install safe-chain to provide it? (y/N): " "$cmd" - read -r response - - if [[ "$response" =~ ^[Yy]$ ]]; then - printf "Installing safe-chain...\n" - installSafeChain - - if [ $? -ne 0 ]; then - printf "\nFailed to install safe-chain. Exiting.\n" - return 1 - fi - - return 0 - else - printf "Skipping safe-chain installation. Using original command instead.\n" - return 2 - fi +function printSafeChainWarning() { + # \033[43;30m is used to set the background color to yellow and text color to black + # \033[0m is used to reset the text formatting + printf "\033[43;30mWarning:\033[0m safe-chain is not available to protect you from installing malware. %s will be run directly.\n" "$1" + # \033[36m is used to set the text color to cyan + printf "Install safe-chain by using \033[36mnpm install -g @aikidosec/safe-chain\033[0m.\n" } -function installSafeChain() { - command npm install -g @aikidosec/safe-chain - - if [ $? -ne 0 ]; then - return 1 - fi - - printf "------\n" -} - -function wrapCommand() { +function wrapSafeChainCommand() { local original_cmd="$1" local aikido_cmd="$2" # Remove the first 2 arguments (original_cmd and aikido_cmd) from $@ # so that "$@" now contains only the arguments passed to the original command shift 2 - - installIfCommandNotFound "$aikido_cmd" - local install_result=$? - if [ $install_result -eq 2 ]; then - command "$original_cmd" "$@" - else + + if command -v "$aikido_cmd" > /dev/null 2>&1; then + # If the aikido command is available, just run it with the provided arguments "$aikido_cmd" "$@" + else + # If the aikido command is not available, print a warning and run the original command + printSafeChainWarning "$original_cmd" + + command "$original_cmd" "$@" fi } function npx() { - wrapCommand "npx" "aikido-npx" "$@" + wrapSafeChainCommand "npx" "aikido-npx" "$@" } function yarn() { - wrapCommand "yarn" "aikido-yarn" "$@" + wrapSafeChainCommand "yarn" "aikido-yarn" "$@" } function pnpm() { - wrapCommand "pnpm" "aikido-pnpm" "$@" + wrapSafeChainCommand "pnpm" "aikido-pnpm" "$@" } function pnpx() { - wrapCommand "pnpx" "aikido-pnpx" "$@" + wrapSafeChainCommand "pnpx" "aikido-pnpx" "$@" } function npm() { @@ -85,5 +50,5 @@ function npm() { return fi - wrapCommand "npm" "aikido-npm" "$@" + wrapSafeChainCommand "npm" "aikido-npm" "$@" } From 0e6d96fe1d7c65488cfad2f95e143ade24eb4da8 Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Mon, 28 Jul 2025 10:37:36 +0200 Subject: [PATCH 11/18] Powershell: only print warning when safe-chain is unavailable --- .../startup-scripts/init-pwsh.ps1 | 84 +++++-------------- 1 file changed, 21 insertions(+), 63 deletions(-) diff --git a/src/shell-integration/startup-scripts/init-pwsh.ps1 b/src/shell-integration/startup-scripts/init-pwsh.ps1 index 58a19c9..7d85ee1 100644 --- a/src/shell-integration/startup-scripts/init-pwsh.ps1 +++ b/src/shell-integration/startup-scripts/init-pwsh.ps1 @@ -1,10 +1,24 @@ +function Write-SafeChainWarning { + param([string]$Command) + + # PowerShell equivalent of ANSI color codes: yellow background, black text for "Warning:" + Write-Host "Warning:" -BackgroundColor Yellow -ForegroundColor Black -NoNewline + Write-Host " safe-chain is not available to protect you from installing malware. $Command will be run directly." + + # Cyan text for the install command + Write-Host "Install safe-chain by using " -NoNewline + Write-Host "npm install -g @aikidosec/safe-chain" -ForegroundColor Cyan -NoNewline + Write-Host "." +} + function Test-CommandAvailable { param([string]$Command) try { Get-Command $Command -ErrorAction Stop | Out-Null return $true - } catch { + } + catch { return $false } } @@ -19,62 +33,6 @@ function Invoke-RealCommand { $realCommand = Get-Command -Name $Command -CommandType Application | Select-Object -First 1 if ($realCommand) { & $realCommand.Source @Arguments - } else { - # Fallback: try to call the .cmd version directly - & "$Command.cmd" @Arguments - } -} - -function Install-IfCommandNotFound { - param([string]$Command) - - # Check if the command already exists - if (Test-CommandAvailable $Command) { - return 0 - } - - # Check if Node.js version is below 18 - # Safe-chain requires Node.js 18 or higher - try { - $nodeVersion = (node -v) -replace 'v', '' | ForEach-Object { $_.Split('.')[0] } - if ([int]$nodeVersion -lt 18) { - return 2 - } - } catch { - return 2 - } - - # Command not found, ask user if they want to install safe-chain - $response = Read-Host "The command '$Command' is not available. Do you want to install safe-chain to provide it? (y/N)" - - if ($response -match '^[Yy]$') { - Write-Host "Installing safe-chain..." - $installResult = Install-SafeChain - - if ($installResult -ne 0) { - Write-Host "`nFailed to install safe-chain. Exiting." - return 1 - } - - return 0 - } else { - Write-Host "Skipping safe-chain installation. Using original command instead." - return 2 - } -} - -function Install-SafeChain { - try { - Invoke-RealCommand "npm" @("install", "-g", "@aikidosec/safe-chain") | Out-Null - - if ($LASTEXITCODE -ne 0) { - return 1 - } - - Write-Host "------" - return 0 - } catch { - return 1 } } @@ -84,14 +42,14 @@ function Invoke-WrappedCommand { [string]$AikidoCmd, [string[]]$Arguments ) - - $installResult = Install-IfCommandNotFound $AikidoCmd - - if ($installResult -eq 2) { - Invoke-RealCommand $OriginalCmd $Arguments - } else { + + if (Test-CommandAvailable $AikidoCmd) { & $AikidoCmd @Arguments } + else { + Write-SafeChainWarning $OriginalCmd + Invoke-RealCommand $OriginalCmd $Arguments + } } function npx { From 32e2408ad0553fcc3cba5572dae2457699951d43 Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Mon, 28 Jul 2025 11:00:01 +0200 Subject: [PATCH 12/18] Fish: only show warning instead of auto-installing safe-chain --- .../startup-scripts/init-fish.fish | 77 ++++++------------- 1 file changed, 25 insertions(+), 52 deletions(-) diff --git a/src/shell-integration/startup-scripts/init-fish.fish b/src/shell-integration/startup-scripts/init-fish.fish index 3a6c18e..86d7fcc 100644 --- a/src/shell-integration/startup-scripts/init-fish.fish +++ b/src/shell-integration/startup-scripts/init-fish.fish @@ -1,76 +1,49 @@ -function installIfCommandNotFound - set cmd $argv[1] +function printSafeChainWarning + set original_cmd $argv[1] - # Check if the command already exists - if type -q $cmd - return 0 - end + # Fish equivalent of ANSI color codes: yellow background, black text for "Warning:" + set_color -b yellow black + printf "Warning:" + set_color normal + printf " safe-chain is not available to protect you from installing malware. %s will be run directly.\n" $original_cmd - # Check if Node.js version is below 18 - # Safe-chain requires Node.js 18 or higher - set node_version (node -v | sed 's/v//' | cut -d'.' -f1) - if test $node_version -lt 18 - return 2 - end - - # Command not found, ask user if they want to install safe-chain - read -l response -P "The command '$cmd' is not available. Do you want to install safe-chain to provide it? (y/N): " - - if string match -qi 'y*' $response - printf "Installing safe-chain...\n" - installSafeChain - - if test $status -ne 0 - printf "\nFailed to install safe-chain. Exiting.\n" - return 1 - end - - return 0 - else - printf "Skipping safe-chain installation. Using original command instead.\n" - return 2 - end + # Cyan text for the install command + printf "Install safe-chain by using " + set_color cyan + printf "npm install -g @aikidosec/safe-chain" + set_color normal + printf ".\n" end -function installSafeChain - command npm install -g @aikidosec/safe-chain - - if test $status -ne 0 - return 1 - end - - printf "------\n" -end - -function wrapCommand +function wrapSafeChainCommand set original_cmd $argv[1] set aikido_cmd $argv[2] set cmd_args $argv[3..-1] - installIfCommandNotFound $aikido_cmd - set install_result $status - - if test $install_result -eq 2 - command $original_cmd $cmd_args - else + if type -q $aikido_cmd + # If the aikido command is available, just run it with the provided arguments $aikido_cmd $cmd_args + else + # If the aikido command is not available, print a warning and run the original command + printSafeChainWarning $original_cmd + command $original_cmd $cmd_args end end function npx - wrapCommand "npx" "aikido-npx" $argv + wrapSafeChainCommand "npx" "aikido-npx" $argv end function yarn - wrapCommand "yarn" "aikido-yarn" $argv + wrapSafeChainCommand "yarn" "aikido-yarn" $argv end function pnpm - wrapCommand "pnpm" "aikido-pnpm" $argv + wrapSafeChainCommand "pnpm" "aikido-pnpm" $argv end function pnpx - wrapCommand "pnpx" "aikido-pnpx" $argv + wrapSafeChainCommand "pnpx" "aikido-pnpx" $argv end function npm @@ -81,5 +54,5 @@ function npm return end - wrapCommand "npm" "aikido-npm" $argv + wrapSafeChainCommand "npm" "aikido-npm" $argv end \ No newline at end of file From 5b4d014ef2cbf7f826f25a4342655317cbc0c529 Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Mon, 28 Jul 2025 12:41:14 +0200 Subject: [PATCH 13/18] Cleanup --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index 7bd518b..4ab4c95 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,5 @@ "bugs": { "url": "https://github.com/AikidoSec/safe-chain/issues" }, - "homepage": "https://github.com/AikidoSec/safe-chain#readme", - "packageManager": "npm@11.4.1+sha512.fcee43884166b6f9c5d04535fb95650e9708b6948a1f797eddf40e9778646778a518dfa32651b1c62ff36f4ac42becf177ca46ca27d53f24b539190c8d91802b" + "homepage": "https://github.com/AikidoSec/safe-chain#readme" } From d6d80d8f03599343adc0e14f29883df55587695f Mon Sep 17 00:00:00 2001 From: bitterpanda Date: Thu, 31 Jul 2025 14:26:14 +0000 Subject: [PATCH 14/18] Update src/shell-integration/startup-scripts/init-fish.fish --- src/shell-integration/startup-scripts/init-fish.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shell-integration/startup-scripts/init-fish.fish b/src/shell-integration/startup-scripts/init-fish.fish index 86d7fcc..b6de588 100644 --- a/src/shell-integration/startup-scripts/init-fish.fish +++ b/src/shell-integration/startup-scripts/init-fish.fish @@ -55,4 +55,4 @@ function npm end wrapSafeChainCommand "npm" "aikido-npm" $argv -end \ No newline at end of file +end From 2a7e2e3ed8d6c8f502463d9adba6dfd009730088 Mon Sep 17 00:00:00 2001 From: bitterpanda Date: Thu, 31 Jul 2025 14:26:29 +0000 Subject: [PATCH 15/18] Update src/shell-integration/startup-scripts/init-pwsh.ps1 --- src/shell-integration/startup-scripts/init-pwsh.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shell-integration/startup-scripts/init-pwsh.ps1 b/src/shell-integration/startup-scripts/init-pwsh.ps1 index 7d85ee1..16acb92 100644 --- a/src/shell-integration/startup-scripts/init-pwsh.ps1 +++ b/src/shell-integration/startup-scripts/init-pwsh.ps1 @@ -77,4 +77,4 @@ function npm { } Invoke-WrappedCommand "npm" "aikido-npm" $args -} \ No newline at end of file +} From b8ac2e3bc108c53b1be028107324055ccf3d2ec1 Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Mon, 4 Aug 2025 10:19:54 +0200 Subject: [PATCH 16/18] Change text as suggested --- src/shell-integration/startup-scripts/init-posix.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shell-integration/startup-scripts/init-posix.sh b/src/shell-integration/startup-scripts/init-posix.sh index 3b6d13b..01b23c4 100644 --- a/src/shell-integration/startup-scripts/init-posix.sh +++ b/src/shell-integration/startup-scripts/init-posix.sh @@ -2,7 +2,7 @@ function printSafeChainWarning() { # \033[43;30m is used to set the background color to yellow and text color to black # \033[0m is used to reset the text formatting - printf "\033[43;30mWarning:\033[0m safe-chain is not available to protect you from installing malware. %s will be run directly.\n" "$1" + printf "\033[43;30mWarning:\033[0m safe-chain is not available to protect you from installing malware. %s will run without it.\n" "$1" # \033[36m is used to set the text color to cyan printf "Install safe-chain by using \033[36mnpm install -g @aikidosec/safe-chain\033[0m.\n" } From 432769bca068fcae5908f9b6d5a6cb4d7d68f0c2 Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Mon, 4 Aug 2025 10:20:44 +0200 Subject: [PATCH 17/18] Change text as suggested --- src/shell-integration/startup-scripts/init-pwsh.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shell-integration/startup-scripts/init-pwsh.ps1 b/src/shell-integration/startup-scripts/init-pwsh.ps1 index 16acb92..7fb44d6 100644 --- a/src/shell-integration/startup-scripts/init-pwsh.ps1 +++ b/src/shell-integration/startup-scripts/init-pwsh.ps1 @@ -3,7 +3,7 @@ function Write-SafeChainWarning { # PowerShell equivalent of ANSI color codes: yellow background, black text for "Warning:" Write-Host "Warning:" -BackgroundColor Yellow -ForegroundColor Black -NoNewline - Write-Host " safe-chain is not available to protect you from installing malware. $Command will be run directly." + Write-Host " safe-chain is not available to protect you from installing malware. $Command will run without it." # Cyan text for the install command Write-Host "Install safe-chain by using " -NoNewline From c5b9722a4024f23aec9da1e47164680ae8d058c1 Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Mon, 4 Aug 2025 10:22:33 +0200 Subject: [PATCH 18/18] Change text as suggested --- src/shell-integration/startup-scripts/init-fish.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shell-integration/startup-scripts/init-fish.fish b/src/shell-integration/startup-scripts/init-fish.fish index b6de588..0190883 100644 --- a/src/shell-integration/startup-scripts/init-fish.fish +++ b/src/shell-integration/startup-scripts/init-fish.fish @@ -5,7 +5,7 @@ function printSafeChainWarning set_color -b yellow black printf "Warning:" set_color normal - printf " safe-chain is not available to protect you from installing malware. %s will be run directly.\n" $original_cmd + printf " safe-chain is not available to protect you from installing malware. %s will run without it.\n" $original_cmd # Cyan text for the install command printf "Install safe-chain by using "