From d5cd59fd25f210e953d93bfbbc98b97e5c8e09fe Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Wed, 17 Sep 2025 14:14:04 +0200 Subject: [PATCH 1/3] Use strict dependency versions --- package-lock.json | 17 +++++++---------- packages/safe-chain/package.json | 12 ++++++------ test/e2e/package-lock.json | 32 -------------------------------- 3 files changed, 13 insertions(+), 48 deletions(-) delete mode 100644 test/e2e/package-lock.json diff --git a/package-lock.json b/package-lock.json index 4993cc4..4840448 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4875,12 +4875,12 @@ "version": "1.0.0", "license": "AGPL-3.0-or-later", "dependencies": { - "abbrev": "^3.0.1", - "chalk": "^5.4.1", - "make-fetch-happen": "^14.0.3", - "npm-registry-fetch": "^18.0.2", - "ora": "^8.2.0", - "semver": "^7.7.2" + "abbrev": "3.0.1", + "chalk": "5.4.1", + "make-fetch-happen": "14.0.3", + "npm-registry-fetch": "18.0.2", + "ora": "8.2.0", + "semver": "7.7.2" }, "bin": { "aikido-npm": "bin/aikido-npm.js", @@ -4896,8 +4896,7 @@ "version": "1.0.0", "license": "AGPL-3.0-or-later", "dependencies": { - "@aikidosec/safe-chain": "file:../safe-chain", - "make-fetch-happen": "^14.0.3" + "@aikidosec/safe-chain": "file:../safe-chain" }, "peerDependencies": { "bun": ">=1.2.21" @@ -4908,8 +4907,6 @@ "version": "1.0.0", "license": "AGPL-3.0-or-later", "dependencies": { - "@aikidosec/safe-chain": "file:../../packages/safe-chain", - "make-fetch-happen": "^14.0.3", "node-pty": "^1.0.0" } } diff --git a/packages/safe-chain/package.json b/packages/safe-chain/package.json index 9f0a37a..32228e7 100644 --- a/packages/safe-chain/package.json +++ b/packages/safe-chain/package.json @@ -28,12 +28,12 @@ "license": "AGPL-3.0-or-later", "description": "The Aikido Safe Chain wraps around the [npm cli](https://github.com/npm/cli), [npx](https://github.com/npm/cli/blob/latest/docs/content/commands/npx.md), [yarn](https://yarnpkg.com/), [pnpm](https://pnpm.io/), and [pnpx](https://pnpm.io/cli/dlx) to provide extra checks before installing new packages. This tool will detect when a package contains malware and prompt you to exit, preventing npm, npx, yarn, pnpm, or pnpx from downloading or running the malware.", "dependencies": { - "abbrev": "^3.0.1", - "chalk": "^5.4.1", - "make-fetch-happen": "^14.0.3", - "npm-registry-fetch": "^18.0.2", - "ora": "^8.2.0", - "semver": "^7.7.2" + "abbrev": "3.0.1", + "chalk": "5.4.1", + "make-fetch-happen": "14.0.3", + "npm-registry-fetch": "18.0.2", + "ora": "8.2.0", + "semver": "7.7.2" }, "main": "src/main.js", "bugs": { diff --git a/test/e2e/package-lock.json b/test/e2e/package-lock.json deleted file mode 100644 index 55aabb7..0000000 --- a/test/e2e/package-lock.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "@aikidosec/safe-chain-e2e-tests", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "@aikidosec/safe-chain-e2e-tests", - "version": "1.0.0", - "license": "AGPL-3.0-or-later", - "dependencies": { - "node-pty": "^1.0.0" - } - }, - "node_modules/nan": { - "version": "2.23.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.23.0.tgz", - "integrity": "sha512-1UxuyYGdoQHcGg87Lkqm3FzefucTa0NAiOcuRsDmysep3c1LVCRK2krrUDafMWtjSG04htvAmvg96+SDknOmgQ==", - "license": "MIT" - }, - "node_modules/node-pty": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.0.0.tgz", - "integrity": "sha512-wtBMWWS7dFZm/VgqElrTvtfMq4GzJ6+edFI0Y0zyzygUSZMgZdraDUMUhCIvkjhJjme15qWmbyJbtAx4ot4uZA==", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "nan": "^2.17.0" - } - } - } -} From 57ce17e7f568800c1a7adb496062438f53018092 Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Wed, 17 Sep 2025 16:42:10 +0200 Subject: [PATCH 2/3] Don't remove empty lines in shell startup scripts. Fixes #58 --- .../src/shell-integration/helpers.js | 11 ++++++++--- .../supported-shells/zsh.spec.js | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/packages/safe-chain/src/shell-integration/helpers.js b/packages/safe-chain/src/shell-integration/helpers.js index 7efface..be943e7 100644 --- a/packages/safe-chain/src/shell-integration/helpers.js +++ b/packages/safe-chain/src/shell-integration/helpers.js @@ -28,7 +28,7 @@ export function removeLinesMatchingPattern(filePath, pattern) { } const fileContent = fs.readFileSync(filePath, "utf-8"); - const lines = fileContent.split(/[\r\n\u2028\u2029]+/); + const lines = fileContent.split(/[\r\n\u2028\u2029]/); const updatedLines = lines.filter((line) => !shouldRemoveLine(line, pattern)); fs.writeFileSync(filePath, updatedLines.join(os.EOL), "utf-8"); } @@ -43,12 +43,17 @@ function shouldRemoveLine(line, pattern) { if (line.length > maxLineLength) { // safe-chain only adds lines shorter than maxLineLength - // so if the line is longer, it must be from a different + // so if the line is longer, it must be from a different // source and could be dangerous to remove return false; } - if (line.includes("\n") || line.includes("\r") || line.includes("\u2028") || line.includes("\u2029")) { + if ( + line.includes("\n") || + line.includes("\r") || + line.includes("\u2028") || + line.includes("\u2029") + ) { // If the line contains newlines, something has gone wrong in splitting // \u2028 and \u2029 are Unicode line separator characters (line and paragraph separators) return false; diff --git a/packages/safe-chain/src/shell-integration/supported-shells/zsh.spec.js b/packages/safe-chain/src/shell-integration/supported-shells/zsh.spec.js index 95c12ac..99106ec 100644 --- a/packages/safe-chain/src/shell-integration/supported-shells/zsh.spec.js +++ b/packages/safe-chain/src/shell-integration/supported-shells/zsh.spec.js @@ -222,5 +222,24 @@ describe("Zsh shell integration", () => { ); assert.ok(content.includes("alias ls=")); }); + + it("should respect empty lines and comments", () => { + const initialContent = [ + "#!/bin/zsh", + "", + "# Some comment", + "", + "", + "", + "# Another comment", + ].join("\n"); + + fs.writeFileSync(mockStartupFile, initialContent, "utf-8"); + + zsh.teardown(knownAikidoTools); + + const content = fs.readFileSync(mockStartupFile, "utf-8"); + assert.strictEqual(content, initialContent); + }); }); }); From 93c23ee39f9f0a325f4ccf1f87edd5abf2636d68 Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Thu, 18 Sep 2025 08:05:11 +0200 Subject: [PATCH 3/3] Always use \n line endings for bash, zsh and fish --- .../src/shell-integration/helpers.js | 21 +++++++++++++------ .../supported-shells/bash.js | 13 +++++++++--- .../supported-shells/fish.js | 10 ++++++--- .../shell-integration/supported-shells/zsh.js | 13 +++++++++--- 4 files changed, 42 insertions(+), 15 deletions(-) diff --git a/packages/safe-chain/src/shell-integration/helpers.js b/packages/safe-chain/src/shell-integration/helpers.js index 7efface..e3320a1 100644 --- a/packages/safe-chain/src/shell-integration/helpers.js +++ b/packages/safe-chain/src/shell-integration/helpers.js @@ -22,15 +22,17 @@ export function doesExecutableExistOnSystem(executableName) { } } -export function removeLinesMatchingPattern(filePath, pattern) { +export function removeLinesMatchingPattern(filePath, pattern, eol) { if (!fs.existsSync(filePath)) { return; } + eol = eol || os.EOL; + const fileContent = fs.readFileSync(filePath, "utf-8"); const lines = fileContent.split(/[\r\n\u2028\u2029]+/); const updatedLines = lines.filter((line) => !shouldRemoveLine(line, pattern)); - fs.writeFileSync(filePath, updatedLines.join(os.EOL), "utf-8"); + fs.writeFileSync(filePath, updatedLines.join(eol), "utf-8"); } const maxLineLength = 100; @@ -43,12 +45,17 @@ function shouldRemoveLine(line, pattern) { if (line.length > maxLineLength) { // safe-chain only adds lines shorter than maxLineLength - // so if the line is longer, it must be from a different + // so if the line is longer, it must be from a different // source and could be dangerous to remove return false; } - if (line.includes("\n") || line.includes("\r") || line.includes("\u2028") || line.includes("\u2029")) { + if ( + line.includes("\n") || + line.includes("\r") || + line.includes("\u2028") || + line.includes("\u2029") + ) { // If the line contains newlines, something has gone wrong in splitting // \u2028 and \u2029 are Unicode line separator characters (line and paragraph separators) return false; @@ -57,12 +64,14 @@ function shouldRemoveLine(line, pattern) { return true; } -export function addLineToFile(filePath, line) { +export function addLineToFile(filePath, line, eol) { if (!fs.existsSync(filePath)) { fs.writeFileSync(filePath, "", "utf-8"); } + eol = eol || os.EOL; + const fileContent = fs.readFileSync(filePath, "utf-8"); - const updatedContent = fileContent + os.EOL + line; + const updatedContent = fileContent + eol + line; fs.writeFileSync(filePath, updatedContent, "utf-8"); } diff --git a/packages/safe-chain/src/shell-integration/supported-shells/bash.js b/packages/safe-chain/src/shell-integration/supported-shells/bash.js index 5f9c54e..6038f95 100644 --- a/packages/safe-chain/src/shell-integration/supported-shells/bash.js +++ b/packages/safe-chain/src/shell-integration/supported-shells/bash.js @@ -9,6 +9,7 @@ import * as os from "os"; const shellName = "Bash"; const executableName = "bash"; const startupFileCommand = "echo ~/.bashrc"; +const eol = "\n"; // When bash runs on Windows (e.g., Git Bash or WSL), it expects LF line endings. function isInstalled() { return doesExecutableExistOnSystem(executableName); @@ -19,13 +20,18 @@ function teardown(tools) { for (const { tool } of tools) { // Remove any existing alias for the tool - removeLinesMatchingPattern(startupFile, new RegExp(`^alias\\s+${tool}=`)); + removeLinesMatchingPattern( + startupFile, + new RegExp(`^alias\\s+${tool}=`), + eol + ); } // 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/ + /^source\s+~\/\.safe-chain\/scripts\/init-posix\.sh/, + eol ); return true; @@ -36,7 +42,8 @@ function setup() { addLineToFile( startupFile, - `source ~/.safe-chain/scripts/init-posix.sh # Safe-chain bash initialization script` + `source ~/.safe-chain/scripts/init-posix.sh # Safe-chain bash initialization script`, + eol ); return true; diff --git a/packages/safe-chain/src/shell-integration/supported-shells/fish.js b/packages/safe-chain/src/shell-integration/supported-shells/fish.js index 7b2c683..4c39ba6 100644 --- a/packages/safe-chain/src/shell-integration/supported-shells/fish.js +++ b/packages/safe-chain/src/shell-integration/supported-shells/fish.js @@ -8,6 +8,7 @@ import { execSync } from "child_process"; const shellName = "Fish"; const executableName = "fish"; const startupFileCommand = "echo ~/.config/fish/config.fish"; +const eol = "\n"; // When fish runs on Windows (e.g., Git Bash or WSL), it expects LF line endings. function isInstalled() { return doesExecutableExistOnSystem(executableName); @@ -20,14 +21,16 @@ function teardown(tools) { // Remove any existing alias for the tool removeLinesMatchingPattern( startupFile, - new RegExp(`^alias\\s+${tool}\\s+`) + new RegExp(`^alias\\s+${tool}\\s+`), + eol ); } // 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/ + /^source\s+~\/\.safe-chain\/scripts\/init-fish\.fish/, + eol ); return true; @@ -38,7 +41,8 @@ function setup() { addLineToFile( startupFile, - `source ~/.safe-chain/scripts/init-fish.fish # Safe-chain Fish initialization script` + `source ~/.safe-chain/scripts/init-fish.fish # Safe-chain Fish initialization script`, + eol ); return true; diff --git a/packages/safe-chain/src/shell-integration/supported-shells/zsh.js b/packages/safe-chain/src/shell-integration/supported-shells/zsh.js index b5167fe..b90f769 100644 --- a/packages/safe-chain/src/shell-integration/supported-shells/zsh.js +++ b/packages/safe-chain/src/shell-integration/supported-shells/zsh.js @@ -8,6 +8,7 @@ import { execSync } from "child_process"; const shellName = "Zsh"; const executableName = "zsh"; const startupFileCommand = "echo ${ZDOTDIR:-$HOME}/.zshrc"; +const eol = "\n"; // When zsh runs on Windows (e.g., Git Bash or WSL), it expects LF line endings. function isInstalled() { return doesExecutableExistOnSystem(executableName); @@ -18,13 +19,18 @@ function teardown(tools) { for (const { tool } of tools) { // Remove any existing alias for the tool - removeLinesMatchingPattern(startupFile, new RegExp(`^alias\\s+${tool}=`)); + removeLinesMatchingPattern( + startupFile, + new RegExp(`^alias\\s+${tool}=`), + eol + ); } // Removes the line that sources the safe-chain zsh initialization script (~/.aikido/scripts/init-posix.sh) removeLinesMatchingPattern( startupFile, - /^source\s+~\/\.safe-chain\/scripts\/init-posix\.sh/ + /^source\s+~\/\.safe-chain\/scripts\/init-posix\.sh/, + eol ); return true; @@ -35,7 +41,8 @@ function setup() { addLineToFile( startupFile, - `source ~/.safe-chain/scripts/init-posix.sh # Safe-chain Zsh initialization script` + `source ~/.safe-chain/scripts/init-posix.sh # Safe-chain Zsh initialization script`, + eol ); return true;