diff --git a/packages/safe-chain/src/main.js b/packages/safe-chain/src/main.js index 916a81f..3d4d5b1 100644 --- a/packages/safe-chain/src/main.js +++ b/packages/safe-chain/src/main.js @@ -5,6 +5,7 @@ import { ui } from "./environment/userInteraction.js"; import { getPackageManager } from "./packagemanager/currentPackageManager.js"; import { initializeCliArguments } from "./config/cliArguments.js"; import { createSafeChainProxy } from "./registryProxy/registryProxy.js"; +import chalk from "chalk"; export async function main(args) { const proxy = createSafeChainProxy(); @@ -27,5 +28,12 @@ export async function main(args) { await proxy.stopServer(); proxy.verifyNoMaliciousPackages(); + ui.emptyLine(); + ui.writeInformation( + `${chalk.green( + "✔" + )} Safe-chain: Command completed, no malicious packages found.` + ); + return result.status; } diff --git a/packages/safe-chain/src/scanning/index.js b/packages/safe-chain/src/scanning/index.js index 48a3e3a..d4f8613 100644 --- a/packages/safe-chain/src/scanning/index.js +++ b/packages/safe-chain/src/scanning/index.js @@ -61,7 +61,7 @@ export async function scanCommand(args) { } if (!audit || audit.isAllowed) { - spinner.succeed("Safe-chain: No malicious packages detected."); + spinner.stop(); } else { printMaliciousChanges(audit.disallowedChanges, spinner); await onMalwareFound(); diff --git a/packages/safe-chain/src/scanning/index.scanCommand.spec.js b/packages/safe-chain/src/scanning/index.scanCommand.spec.js index 715ecfb..f81a155 100644 --- a/packages/safe-chain/src/scanning/index.scanCommand.spec.js +++ b/packages/safe-chain/src/scanning/index.scanCommand.spec.js @@ -13,6 +13,7 @@ describe("scanCommand", async () => { setText: () => {}, succeed: () => {}, fail: () => {}, + stop: () => {}, })); const mockConfirm = mock.fn(() => true); let malwareAction = MALWARE_ACTION_PROMPT; @@ -88,29 +89,31 @@ describe("scanCommand", async () => { const { scanCommand } = await import("./index.js"); it("should succeed when there are no changes", async () => { - let successMessageWasSet = false; + let progressWasStopped = false; mockStartProcess.mock.mockImplementationOnce(() => ({ setText: () => {}, - succeed: () => { - successMessageWasSet = true; - }, + succeed: () => {}, fail: () => {}, + stop: () => { + progressWasStopped = true; + }, })); mockGetDependencyUpdatesForCommand.mock.mockImplementation(() => []); await scanCommand(["install", "lodash"]); - assert.equal(successMessageWasSet, true); + assert.equal(progressWasStopped, true); }); it("should succeed when changes are not malicious", async () => { - let successMessageWasSet = false; + let progressWasStopped = false; mockStartProcess.mock.mockImplementationOnce(() => ({ setText: () => {}, - succeed: () => { - successMessageWasSet = true; - }, + succeed: () => {}, fail: () => {}, + stop: () => { + progressWasStopped = true; + }, })); mockGetDependencyUpdatesForCommand.mock.mockImplementation(() => [ { name: "lodash", version: "4.17.21" }, @@ -118,7 +121,7 @@ describe("scanCommand", async () => { await scanCommand(["install", "lodash"]); - assert.equal(successMessageWasSet, true); + assert.equal(progressWasStopped, true); }); it("should throw an error when timing out", async () => { @@ -129,6 +132,7 @@ describe("scanCommand", async () => { fail: () => { failureMessageWasSet = true; }, + stop: () => {}, })); getScanTimeoutMock.mock.mockImplementationOnce(() => 100); mockGetDependencyUpdatesForCommand.mock.mockImplementation(async () => { @@ -149,6 +153,7 @@ describe("scanCommand", async () => { fail: () => { failureMessageWasSet = true; }, + stop: () => {}, })); mockGetDependencyUpdatesForCommand.mock.mockImplementation(() => [ { name: "malicious", version: "1.0.0" }, @@ -173,6 +178,7 @@ describe("scanCommand", async () => { fail: (message) => { failureMessages.push(message); }, + stop: () => {}, })); getScanTimeoutMock.mock.mockImplementationOnce(() => 100); mockGetDependencyUpdatesForCommand.mock.mockImplementation(async () => { @@ -194,21 +200,22 @@ 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; let exitCode = null; - + mockStartProcess.mock.mockImplementationOnce(() => ({ setText: () => {}, succeed: () => {}, fail: () => { failureMessageWasSet = true; }, + stop: () => {}, })); - + mockGetDependencyUpdatesForCommand.mock.mockImplementation(() => [ { name: "malicious", version: "1.0.0" }, ]); @@ -241,19 +248,20 @@ describe("scanCommand", async () => { 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 processExited = false; let userWasPrompted = false; - + mockStartProcess.mock.mockImplementationOnce(() => ({ setText: () => {}, succeed: () => {}, fail: () => {}, + stop: () => {}, })); - + mockGetDependencyUpdatesForCommand.mock.mockImplementation(() => [ { name: "malicious", version: "1.0.0" }, ]); diff --git a/test/e2e/npm-ci.e2e.spec.js b/test/e2e/npm-ci.e2e.spec.js index 3e08c3d..dc1c23f 100644 --- a/test/e2e/npm-ci.e2e.spec.js +++ b/test/e2e/npm-ci.e2e.spec.js @@ -36,7 +36,7 @@ describe("E2E: npm coverage using PATH", () => { const result = await shell.runCommand("npm i axios"); assert.ok( - result.output.includes("No malicious packages detected."), + result.output.includes("no malicious packages found."), `Output did not include expected text. Output was:\n${result.output}` ); }); diff --git a/test/e2e/npm.e2e.spec.js b/test/e2e/npm.e2e.spec.js index fc23ebb..c744835 100644 --- a/test/e2e/npm.e2e.spec.js +++ b/test/e2e/npm.e2e.spec.js @@ -31,7 +31,7 @@ describe("E2E: npm coverage", () => { const result = await shell.runCommand("npm i axios"); assert.ok( - result.output.includes("No malicious packages detected."), + result.output.includes("no malicious packages found."), `Output did not include expected text. Output was:\n${result.output}` ); }); diff --git a/test/e2e/pnpm-ci.e2e.spec.js b/test/e2e/pnpm-ci.e2e.spec.js index 9a8c6a2..339a5e0 100644 --- a/test/e2e/pnpm-ci.e2e.spec.js +++ b/test/e2e/pnpm-ci.e2e.spec.js @@ -36,7 +36,7 @@ describe("E2E: pnpm coverage", () => { const result = await shell.runCommand("pnpm add axios"); assert.ok( - result.output.includes("No malicious packages detected."), + result.output.includes("no malicious packages found."), `Output did not include expected text. Output was:\n${result.output}` ); }); diff --git a/test/e2e/pnpm.e2e.spec.js b/test/e2e/pnpm.e2e.spec.js index c9460e6..c0187d7 100644 --- a/test/e2e/pnpm.e2e.spec.js +++ b/test/e2e/pnpm.e2e.spec.js @@ -31,7 +31,7 @@ describe("E2E: pnpm coverage", () => { const result = await shell.runCommand("pnpm add axios"); assert.ok( - result.output.includes("No malicious packages detected."), + result.output.includes("no malicious packages found."), `Output did not include expected text. Output was:\n${result.output}` ); }); diff --git a/test/e2e/yarn-ci.e2e.spec.js b/test/e2e/yarn-ci.e2e.spec.js index 5466851..33ef4f2 100644 --- a/test/e2e/yarn-ci.e2e.spec.js +++ b/test/e2e/yarn-ci.e2e.spec.js @@ -36,7 +36,7 @@ describe("E2E: yarn coverage", () => { const result = await shell.runCommand("yarn add axios"); assert.ok( - result.output.includes("No malicious packages detected."), + result.output.includes("no malicious packages found."), `Output did not include expected text. Output was:\n${result.output}` ); }); diff --git a/test/e2e/yarn.e2e.spec.js b/test/e2e/yarn.e2e.spec.js index df8a88b..3909318 100644 --- a/test/e2e/yarn.e2e.spec.js +++ b/test/e2e/yarn.e2e.spec.js @@ -31,7 +31,7 @@ describe("E2E: yarn coverage", () => { const result = await shell.runCommand("yarn add axios"); assert.ok( - result.output.includes("No malicious packages detected."), + result.output.includes("no malicious packages found."), `Output did not include expected text. Output was:\n${result.output}` ); });