From ab3319a3104dfe5c1ce015362986501f63fa95ac Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Mon, 27 Oct 2025 11:51:19 +0100 Subject: [PATCH] Remove --safe-chain-malware-action flag --- .../safe-chain/src/config/cliArguments.js | 17 ------ .../src/config/cliArguments.spec.js | 56 +----------------- packages/safe-chain/src/config/settings.js | 13 ---- .../src/environment/userInteraction.js | 30 ---------- packages/safe-chain/src/scanning/index.js | 21 +------ .../src/scanning/index.scanCommand.spec.js | 59 ++----------------- 6 files changed, 8 insertions(+), 188 deletions(-) diff --git a/packages/safe-chain/src/config/cliArguments.js b/packages/safe-chain/src/config/cliArguments.js index 70c56b1..f234bbb 100644 --- a/packages/safe-chain/src/config/cliArguments.js +++ b/packages/safe-chain/src/config/cliArguments.js @@ -1,5 +1,4 @@ const state = { - malwareAction: undefined, loggingLevel: undefined, }; @@ -7,7 +6,6 @@ const SAFE_CHAIN_ARG_PREFIX = "--safe-chain-"; export function initializeCliArguments(args) { // Reset state on each call - state.malwareAction = undefined; state.loggingLevel = undefined; const safeChainArgs = []; @@ -21,22 +19,11 @@ export function initializeCliArguments(args) { } } - setMalwareAction(safeChainArgs); setLoggingLevel(safeChainArgs); return remainingArgs; } -function setMalwareAction(args) { - const safeChainMalwareActionArg = SAFE_CHAIN_ARG_PREFIX + "malware-action="; - - const action = getLastArgEqualsValue(args, safeChainMalwareActionArg); - if (!action) { - return; - } - state.malwareAction = action.toLowerCase(); -} - function getLastArgEqualsValue(args, prefix) { for (var i = args.length - 1; i >= 0; i--) { const arg = args[i]; @@ -48,10 +35,6 @@ function getLastArgEqualsValue(args, prefix) { return undefined; } -export function getMalwareAction() { - return state.malwareAction; -} - function setLoggingLevel(args) { const safeChainLoggingArg = SAFE_CHAIN_ARG_PREFIX + "logging="; diff --git a/packages/safe-chain/src/config/cliArguments.spec.js b/packages/safe-chain/src/config/cliArguments.spec.js index c7d9a84..415d34a 100644 --- a/packages/safe-chain/src/config/cliArguments.spec.js +++ b/packages/safe-chain/src/config/cliArguments.spec.js @@ -1,10 +1,6 @@ import { describe, it } from "node:test"; import assert from "node:assert"; -import { - initializeCliArguments, - getMalwareAction, - getLoggingLevel, -} from "./cliArguments.js"; +import { initializeCliArguments, getLoggingLevel } from "./cliArguments.js"; describe("initializeCliArguments", () => { it("should return all args when no safe-chain args are present", () => { @@ -61,55 +57,6 @@ describe("initializeCliArguments", () => { assert.deepEqual(result, ["install", "my--safe-chain-package", "--save"]); }); - it("should not set malwareAction when no safe-chain arguments are passed", () => { - const args = ["install", "express", "--save"]; - const result = initializeCliArguments(args); - - assert.deepEqual(result, ["install", "express", "--save"]); - assert.strictEqual(getMalwareAction(), undefined); - }); - - it("should parse malware-action=block and set state", () => { - const args = ["--safe-chain-malware-action=block", "install", "package"]; - const result = initializeCliArguments(args); - - assert.deepEqual(result, ["install", "package"]); - assert.strictEqual(getMalwareAction(), "block"); - }); - - it("should parse malware-action=prompt and set state", () => { - const args = ["--safe-chain-malware-action=prompt", "install", "package"]; - const result = initializeCliArguments(args); - - assert.deepEqual(result, ["install", "package"]); - assert.strictEqual(getMalwareAction(), "prompt"); - }); - - it("should handle multiple malware-action args, using the last valid one", () => { - const args = [ - "--safe-chain-malware-action=block", - "--safe-chain-malware-action=prompt", - "install", - ]; - const result = initializeCliArguments(args); - - assert.deepEqual(result, ["install"]); - assert.strictEqual(getMalwareAction(), "prompt"); - }); - - it("should handle malware-action with other safe-chain args", () => { - const args = [ - "--safe-chain-debug", - "--safe-chain-malware-action=block", - "--safe-chain-verbose", - "install", - ]; - const result = initializeCliArguments(args); - - assert.deepEqual(result, ["install"]); - assert.strictEqual(getMalwareAction(), "block"); - }); - it("should not set loggingLevel when no logging argument is passed", () => { const args = ["install", "express", "--save"]; initializeCliArguments(args); @@ -170,6 +117,5 @@ describe("initializeCliArguments", () => { assert.deepEqual(result, ["install"]); assert.strictEqual(getLoggingLevel(), "silent"); - assert.strictEqual(getMalwareAction(), "block"); }); }); diff --git a/packages/safe-chain/src/config/settings.js b/packages/safe-chain/src/config/settings.js index 17c1cdb..ad150b2 100644 --- a/packages/safe-chain/src/config/settings.js +++ b/packages/safe-chain/src/config/settings.js @@ -1,15 +1,5 @@ import * as cliArguments from "./cliArguments.js"; -export function getMalwareAction() { - const action = cliArguments.getMalwareAction(); - - if (action === MALWARE_ACTION_PROMPT) { - return MALWARE_ACTION_PROMPT; - } - - return MALWARE_ACTION_BLOCK; -} - export function getLoggingLevel() { const level = cliArguments.getLoggingLevel(); @@ -20,8 +10,5 @@ export function getLoggingLevel() { return LOGGING_NORMAL; } -export const MALWARE_ACTION_BLOCK = "block"; -export const MALWARE_ACTION_PROMPT = "prompt"; - export const LOGGING_SILENT = "silent"; export const LOGGING_NORMAL = "normal"; diff --git a/packages/safe-chain/src/environment/userInteraction.js b/packages/safe-chain/src/environment/userInteraction.js index 99fe90f..e1a4f93 100644 --- a/packages/safe-chain/src/environment/userInteraction.js +++ b/packages/safe-chain/src/environment/userInteraction.js @@ -1,7 +1,6 @@ // oxlint-disable no-console import chalk from "chalk"; import ora from "ora"; -import { createInterface } from "readline"; import { isCi } from "./environment.js"; import { getLoggingLevel, LOGGING_SILENT } from "../config/settings.js"; @@ -87,34 +86,6 @@ function startProcess(message) { } } -async function confirm(config) { - if (isCi() || isSilentMode()) { - return Promise.resolve(config.default); - } - - const rl = createInterface({ - input: process.stdin, - output: process.stdout, - }); - - return new Promise((resolve) => { - const defaultText = config.default ? " (Y/n)" : " (y/N)"; - rl.question(`${config.message}${defaultText} `, (answer) => { - rl.close(); - - const normalizedAnswer = answer.trim().toLowerCase(); - - if (normalizedAnswer === "y" || normalizedAnswer === "yes") { - resolve(true); - } else if (normalizedAnswer === "n" || normalizedAnswer === "no") { - resolve(false); - } else { - resolve(config.default); - } - }); - }); -} - export const ui = { writeInformation, writeWarning, @@ -122,5 +93,4 @@ export const ui = { writeExitWithoutInstallingMaliciousPackages, emptyLine, startProcess, - confirm, }; diff --git a/packages/safe-chain/src/scanning/index.js b/packages/safe-chain/src/scanning/index.js index 4b0f654..969c994 100644 --- a/packages/safe-chain/src/scanning/index.js +++ b/packages/safe-chain/src/scanning/index.js @@ -4,7 +4,6 @@ import { setTimeout } from "timers/promises"; import chalk from "chalk"; import { getPackageManager } from "../packagemanager/currentPackageManager.js"; import { ui } from "../environment/userInteraction.js"; -import { getMalwareAction, MALWARE_ACTION_PROMPT } from "../config/settings.js"; export function shouldScanCommand(args) { if (!args || args.length === 0) { @@ -65,7 +64,8 @@ export async function scanCommand(args) { return 0; } else { printMaliciousChanges(audit.disallowedChanges, spinner); - return await onMalwareFound(); + onMalwareFound(); + return 1; } } @@ -77,23 +77,8 @@ function printMaliciousChanges(changes, spinner) { } } -async function onMalwareFound() { +function onMalwareFound() { ui.emptyLine(); - - if (getMalwareAction() === MALWARE_ACTION_PROMPT) { - const continueInstall = await ui.confirm({ - message: - "Malicious packages were found. Do you want to continue with the installation?", - default: false, - }); - - if (continueInstall) { - ui.writeWarning("Continuing with the installation despite the risks..."); - return 0; - } - } - ui.writeExitWithoutInstallingMaliciousPackages(); ui.emptyLine(); - return 1; } diff --git a/packages/safe-chain/src/scanning/index.scanCommand.spec.js b/packages/safe-chain/src/scanning/index.scanCommand.spec.js index abcdc97..c47555f 100644 --- a/packages/safe-chain/src/scanning/index.scanCommand.spec.js +++ b/packages/safe-chain/src/scanning/index.scanCommand.spec.js @@ -1,10 +1,6 @@ import assert from "node:assert/strict"; -import { beforeEach, describe, it, mock } from "node:test"; +import { describe, it, mock } from "node:test"; import { setTimeout } from "node:timers/promises"; -import { - MALWARE_ACTION_PROMPT, - MALWARE_ACTION_BLOCK, -} from "../config/settings.js"; describe("scanCommand", async () => { const getScanTimeoutMock = mock.fn(() => 1000); @@ -15,8 +11,6 @@ describe("scanCommand", async () => { fail: () => {}, stop: () => {}, })); - const mockConfirm = mock.fn(() => true); - let malwareAction = MALWARE_ACTION_PROMPT; // import { getPackageManager } from "../packagemanager/currentPackageManager.js"; mock.module("../packagemanager/currentPackageManager.js", { @@ -48,19 +42,10 @@ describe("scanCommand", async () => { writeWarning: () => {}, writeExitWithoutInstallingMaliciousPackages: () => {}, emptyLine: () => {}, - confirm: mockConfirm, }, }, }); - mock.module("../config/settings.js", { - namedExports: { - getMalwareAction: () => malwareAction, - MALWARE_ACTION_PROMPT, - MALWARE_ACTION_BLOCK, - }, - }); - // import { auditChanges, MAX_LENGTH_EXCEEDED } from "./audit/index.js"; mock.module("./audit/index.js", { namedExports: { @@ -89,11 +74,6 @@ describe("scanCommand", async () => { const { scanCommand } = await import("./index.js"); - beforeEach(() => { - // Reset malware action back to prompt mode for other tests - malwareAction = MALWARE_ACTION_PROMPT; - }); - it("should succeed when there are no changes", async () => { let progressWasStopped = false; mockStartProcess.mock.mockImplementationOnce(() => ({ @@ -151,7 +131,7 @@ describe("scanCommand", async () => { assert.equal(failureMessageWasSet, true); }); - it("should fail and prompt the user when malicious changes are detected", async () => { + it("should fail and return 1 malicious changes are detected", async () => { let failureMessageWasSet = false; mockStartProcess.mock.mockImplementationOnce(() => ({ setText: () => {}, @@ -164,16 +144,11 @@ describe("scanCommand", async () => { mockGetDependencyUpdatesForCommand.mock.mockImplementation(() => [ { name: "malicious", version: "1.0.0" }, ]); - let userWasPrompted = false; - mockConfirm.mock.mockImplementationOnce(() => { - userWasPrompted = true; - return true; // Simulate user accepting the risk, otherwise the process would exit - }); - await scanCommand(["install", "malicious"]); + const result = await scanCommand(["install", "malicious"]); assert.equal(failureMessageWasSet, true); - assert.equal(userWasPrompted, true); + assert.equal(result, 1); }); it("should not report a timeout when the user takes a long time to respond (it should not affect the timeout)", async () => { @@ -190,10 +165,6 @@ describe("scanCommand", async () => { mockGetDependencyUpdatesForCommand.mock.mockImplementation(async () => { return [{ name: "malicious", version: "4.17.21" }]; }); - mockConfirm.mock.mockImplementationOnce(async () => { - await setTimeout(200); - return true; // Simulate user accepting the risk, otherwise the process would exit - }); await scanCommand(["install", "malicious"]); @@ -204,12 +175,6 @@ describe("scanCommand", async () => { }); it("should exit immediately when malicious changes are detected in block mode", async () => { - // Set malware action to block mode for this test - malwareAction = MALWARE_ACTION_BLOCK; - - // Reset mock call count - mockConfirm.mock.resetCalls(); - let failureMessageWasSet = false; mockStartProcess.mock.mockImplementationOnce(() => ({ @@ -229,19 +194,9 @@ describe("scanCommand", async () => { assert.equal(failureMessageWasSet, true); assert.equal(result, 1); - // Confirm should not have been called in block mode - assert.equal(mockConfirm.mock.callCount(), 0); }); it("should exit immediately when malicious changes are detected in block mode without prompting", async () => { - // Set malware action to block mode for this test - malwareAction = MALWARE_ACTION_BLOCK; - - // Reset mock call count - mockConfirm.mock.resetCalls(); - - let userWasPrompted = false; - mockStartProcess.mock.mockImplementationOnce(() => ({ setText: () => {}, succeed: () => {}, @@ -253,14 +208,8 @@ describe("scanCommand", async () => { { name: "malicious", version: "1.0.0" }, ]); - mockConfirm.mock.mockImplementationOnce(() => { - userWasPrompted = true; - return false; - }); - const result = await scanCommand(["install", "malicious"]); assert.equal(result, 1); - assert.equal(userWasPrompted, false); }); });