diff --git a/.github/workflows/test-on-pr.yml b/.github/workflows/test-on-pr.yml index 740d741..59d0549 100644 --- a/.github/workflows/test-on-pr.yml +++ b/.github/workflows/test-on-pr.yml @@ -82,7 +82,7 @@ jobs: # EOL compatibility testing - Node 16 (EOL Sept 2023) - node_version: "16" npm_version: "8.0.0" - yarn_version: "3.6.0" + yarn_version: "1.22.0" pnpm_version: "8.0.0" steps: diff --git a/test/e2e/npm.e2e.spec.js b/test/e2e/npm.e2e.spec.js index 0e64971..fc23ebb 100644 --- a/test/e2e/npm.e2e.spec.js +++ b/test/e2e/npm.e2e.spec.js @@ -60,6 +60,52 @@ describe("E2E: npm coverage", () => { ); }); + it(`safe-chain blocks download of malicious packages already in package.json`, async () => { + const shell = await container.openShell("zsh"); + const npmVersion = (await shell.runCommand("npm --version")).output.trim(); + const majorVersion = parseInt(npmVersion.split(".")[0]); + const minorVersion = parseInt(npmVersion.split(".")[1]); + const isBelow10_4 = + majorVersion < 10 || (majorVersion === 10 && minorVersion < 4); + await shell.runCommand( + 'echo \'{"name":"test-project","version":"1.0.0","dependencies":{"safe-chain-test":"0.0.1-security"}}\' > package.json' + ); + + var result = await shell.runCommand("npm install"); + + if (isBelow10_4) { + assert.ok( + result.output.includes("blocked 1 malicious package downloads"), + `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}` + ); + } else { + 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 npx from executing malicious packages", async () => { const shell = await container.openShell("zsh"); const result = await shell.runCommand("npx safe-chain-test"); diff --git a/test/e2e/pnpm.e2e.spec.js b/test/e2e/pnpm.e2e.spec.js index db7eb58..c9460e6 100644 --- a/test/e2e/pnpm.e2e.spec.js +++ b/test/e2e/pnpm.e2e.spec.js @@ -60,6 +60,28 @@ describe("E2E: pnpm coverage", () => { ); }); + it(`safe-chain blocks download of malicious packages already in package.json`, async () => { + const shell = await container.openShell("zsh"); + await shell.runCommand( + 'echo \'{"name":"test-project","version":"1.0.0","dependencies":{"safe-chain-test":"0.0.1-security"}}\' > package.json' + ); + + var result = await shell.runCommand("pnpm install"); + + assert.ok( + result.output.includes("blocked 1 malicious package downloads"), + `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 pnpx from executing malicious packages", async () => { const shell = await container.openShell("zsh"); const result = await shell.runCommand("pnpx safe-chain-test"); diff --git a/test/e2e/yarn.e2e.spec.js b/test/e2e/yarn.e2e.spec.js index fb22b76..df8a88b 100644 --- a/test/e2e/yarn.e2e.spec.js +++ b/test/e2e/yarn.e2e.spec.js @@ -60,6 +60,52 @@ describe("E2E: yarn coverage", () => { ); }); + it(`safe-chain blocks download of malicious packages already in package.json`, async () => { + const shell = await container.openShell("zsh"); + await shell.runCommand( + 'echo \'{"name":"test-project","version":"1.0.0","dependencies":{"safe-chain-test":"0.0.1-security"}}\' > package.json' + ); + + var result = await shell.runCommand("yarn"); + + assert.ok( + result.output.includes("blocked 1 malicious package downloads"), + `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 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");