From 58b15caba30b01fee6da02503f1f53b096e1f4ff Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Tue, 16 Sep 2025 12:40:45 +0200 Subject: [PATCH] Add e2e tests for blocking malware on npm, pnpm and yarn --- test/e2e/npm.e2e.spec.js | 18 ++++++ test/e2e/pnpm.e2e.spec.js | 118 ++++++++++++++++++++++++++++++++++++++ test/e2e/yarn.e2e.spec.js | 80 ++++++++++++++++++++++++++ 3 files changed, 216 insertions(+) create mode 100644 test/e2e/pnpm.e2e.spec.js create mode 100644 test/e2e/yarn.e2e.spec.js diff --git a/test/e2e/npm.e2e.spec.js b/test/e2e/npm.e2e.spec.js index 51c9f3e..0e64971 100644 --- a/test/e2e/npm.e2e.spec.js +++ b/test/e2e/npm.e2e.spec.js @@ -77,4 +77,22 @@ describe("E2E: npm coverage", () => { `Output did not include expected text. Output was:\n${result.output}` ); }); + + it("safe-chain blocks npm exec from executing malicious packages", async () => { + const shell = await container.openShell("zsh"); + const result = await shell.runCommand("npm exec safe-chain-test"); + + assert.ok( + result.output.includes("Malicious changes detected:"), + `Output did not include expected text. Output was:\n${result.output}` + ); + assert.ok( + result.output.includes("- safe-chain-test"), + `Output did not include expected text. Output was:\n${result.output}` + ); + assert.ok( + result.output.includes("Exiting without installing malicious packages."), + `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 new file mode 100644 index 0000000..db7eb58 --- /dev/null +++ b/test/e2e/pnpm.e2e.spec.js @@ -0,0 +1,118 @@ +import { describe, it, before, beforeEach, afterEach } from "node:test"; +import { DockerTestContainer } from "./DockerTestContainer.js"; +import assert from "node:assert"; + +describe("E2E: pnpm coverage", () => { + let container; + + before(async () => { + DockerTestContainer.buildImage(); + }); + + beforeEach(async () => { + // Run a new Docker container for each test + container = new DockerTestContainer(); + await container.start(); + + const installationShell = await container.openShell("zsh"); + await installationShell.runCommand("safe-chain setup"); + }); + + afterEach(async () => { + // Stop and clean up the container after each test + if (container) { + await container.stop(); + container = null; + } + }); + + it(`safe-chain succesfully installs safe packages`, async () => { + const shell = await container.openShell("zsh"); + const result = await shell.runCommand("pnpm add axios"); + + assert.ok( + result.output.includes("No malicious packages detected."), + `Output did not include expected text. Output was:\n${result.output}` + ); + }); + + it(`safe-chain blocks installation of malicious packages`, async () => { + const shell = await container.openShell("zsh"); + const result = await shell.runCommand("pnpm add safe-chain-test"); + + assert.ok( + result.output.includes("Malicious changes detected:"), + `Output did not include expected text. Output was:\n${result.output}` + ); + assert.ok( + result.output.includes("- safe-chain-test"), + `Output did not include expected text. Output was:\n${result.output}` + ); + assert.ok( + result.output.includes("Exiting without installing malicious packages."), + `Output did not include expected text. Output was:\n${result.output}` + ); + + const listResult = await shell.runCommand("pnpm list"); + assert.ok( + !listResult.output.includes("safe-chain-test"), + `Malicious package was installed despite safe-chain protection. Output of 'pnpm list' was:\n${listResult.output}` + ); + }); + + it("safe-chain blocks pnpx from executing malicious packages", async () => { + const shell = await container.openShell("zsh"); + const result = await shell.runCommand("pnpx safe-chain-test"); + + assert.ok( + result.output.includes("Malicious changes detected:"), + `Output did not include expected text. Output was:\n${result.output}` + ); + assert.ok( + result.output.includes("- safe-chain-test"), + `Output did not include expected text. Output was:\n${result.output}` + ); + assert.ok( + result.output.includes("Exiting without installing malicious packages."), + `Output did not include expected text. Output was:\n${result.output}` + ); + }); + + it("safe-chain blocks pnpm dlx from executing malicious packages", async () => { + const shell = await container.openShell("zsh"); + const result = await shell.runCommand("pnpm dlx safe-chain-test"); + + assert.ok( + result.output.includes("Malicious changes detected:"), + `Output did not include expected text. Output was:\n${result.output}` + ); + assert.ok( + result.output.includes("- safe-chain-test"), + `Output did not include expected text. Output was:\n${result.output}` + ); + assert.ok( + result.output.includes("Exiting without installing malicious packages."), + `Output did not include expected text. Output was:\n${result.output}` + ); + }); + + it("safe-chain blocks pnpm --package=name dlx from executing malicious packages", async () => { + const shell = await container.openShell("zsh"); + const result = await shell.runCommand( + "pnpm --package=safe-chain-test dlx safe-chain-test" + ); + + assert.ok( + result.output.includes("Malicious changes detected:"), + `Output did not include expected text. Output was:\n${result.output}` + ); + assert.ok( + result.output.includes("- safe-chain-test"), + `Output did not include expected text. Output was:\n${result.output}` + ); + assert.ok( + result.output.includes("Exiting without installing malicious packages."), + `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 new file mode 100644 index 0000000..fb22b76 --- /dev/null +++ b/test/e2e/yarn.e2e.spec.js @@ -0,0 +1,80 @@ +import { describe, it, before, beforeEach, afterEach } from "node:test"; +import { DockerTestContainer } from "./DockerTestContainer.js"; +import assert from "node:assert"; + +describe("E2E: yarn coverage", () => { + let container; + + before(async () => { + DockerTestContainer.buildImage(); + }); + + beforeEach(async () => { + // Run a new Docker container for each test + container = new DockerTestContainer(); + await container.start(); + + const installationShell = await container.openShell("zsh"); + await installationShell.runCommand("safe-chain setup"); + }); + + afterEach(async () => { + // Stop and clean up the container after each test + if (container) { + await container.stop(); + container = null; + } + }); + + it(`safe-chain succesfully installs safe packages`, async () => { + const shell = await container.openShell("zsh"); + const result = await shell.runCommand("yarn add axios"); + + assert.ok( + result.output.includes("No malicious packages detected."), + `Output did not include expected text. Output was:\n${result.output}` + ); + }); + + it(`safe-chain blocks installation of malicious packages`, async () => { + const shell = await container.openShell("zsh"); + const result = await shell.runCommand("yarn add safe-chain-test"); + + assert.ok( + result.output.includes("Malicious changes detected:"), + `Output did not include expected text. Output was:\n${result.output}` + ); + assert.ok( + result.output.includes("- safe-chain-test"), + `Output did not include expected text. Output was:\n${result.output}` + ); + assert.ok( + result.output.includes("Exiting without installing malicious packages."), + `Output did not include expected text. Output was:\n${result.output}` + ); + + const listResult = await shell.runCommand("yarn list"); + assert.ok( + !listResult.output.includes("safe-chain-test"), + `Malicious package was installed despite safe-chain protection. Output of 'yarn list' was:\n${listResult.output}` + ); + }); + + it("safe-chain blocks yarn dlx from executing malicious packages", async () => { + const shell = await container.openShell("zsh"); + const result = await shell.runCommand("yarn dlx safe-chain-test"); + + assert.ok( + result.output.includes("Malicious changes detected:"), + `Output did not include expected text. Output was:\n${result.output}` + ); + assert.ok( + result.output.includes("- safe-chain-test"), + `Output did not include expected text. Output was:\n${result.output}` + ); + assert.ok( + result.output.includes("Exiting without installing malicious packages."), + `Output did not include expected text. Output was:\n${result.output}` + ); + }); +});