@aikidosec/safe-chain 1.0.21 → 1.0.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/aikido-npm.js CHANGED
@@ -1,8 +1,19 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ import { execSync } from "child_process";
3
4
  import { main } from "../src/main.js";
4
5
  import { initializePackageManager } from "../src/packagemanager/currentPackageManager.js";
5
6
 
6
7
  const packageManagerName = "npm";
7
- initializePackageManager(packageManagerName, process.versions.node);
8
+ initializePackageManager(packageManagerName, getNpmVersion());
8
9
  await main(process.argv.slice(2));
10
+
11
+ function getNpmVersion() {
12
+ try {
13
+ return execSync("npm --version").toString().trim();
14
+ } catch {
15
+ // Default to 0.0.0 if npm is not found
16
+ // That way we don't use any unsupported features
17
+ return "0.0.0";
18
+ }
19
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aikidosec/safe-chain",
3
- "version": "1.0.21",
3
+ "version": "1.0.22",
4
4
  "scripts": {
5
5
  "test": "node --test --experimental-test-module-mocks 'src/**/*.spec.js'",
6
6
  "test:watch": "node --test --watch --experimental-test-module-mocks 'src/**/*.spec.js'",
@@ -28,9 +28,9 @@
28
28
  "license": "AGPL-3.0-or-later",
29
29
  "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.",
30
30
  "dependencies": {
31
- "@inquirer/prompts": "^7.4.1",
32
31
  "abbrev": "^3.0.1",
33
32
  "chalk": "^5.4.1",
33
+ "make-fetch-happen": "^14.0.3",
34
34
  "npm-registry-fetch": "^18.0.2",
35
35
  "ora": "^8.2.0",
36
36
  "semver": "^7.7.2"
package/src/api/aikido.js CHANGED
@@ -1,3 +1,5 @@
1
+ import fetch from "make-fetch-happen";
2
+
1
3
  const malwareDatabaseUrl =
2
4
  "https://malware-list.aikido.dev/malware_predictions.json";
3
5
 
@@ -1,6 +1,6 @@
1
1
  import chalk from "chalk";
2
2
  import ora from "ora";
3
- import { confirm as inquirerConfirm } from "@inquirer/prompts";
3
+ import { createInterface } from "readline";
4
4
  import { isCi } from "./environment.js";
5
5
 
6
6
  function emptyLine() {
@@ -61,12 +61,29 @@ function startProcess(message) {
61
61
  async function confirm(config) {
62
62
  if (isCi()) {
63
63
  return Promise.resolve(config.default);
64
- } else {
65
- return inquirerConfirm({
66
- message: config.message,
67
- default: config.default,
68
- });
69
64
  }
65
+
66
+ const rl = createInterface({
67
+ input: process.stdin,
68
+ output: process.stdout,
69
+ });
70
+
71
+ return new Promise((resolve) => {
72
+ const defaultText = config.default ? " (Y/n)" : " (y/N)";
73
+ rl.question(`${config.message}${defaultText} `, (answer) => {
74
+ rl.close();
75
+
76
+ const normalizedAnswer = answer.trim().toLowerCase();
77
+
78
+ if (normalizedAnswer === "y" || normalizedAnswer === "yes") {
79
+ resolve(true);
80
+ } else if (normalizedAnswer === "n" || normalizedAnswer === "no") {
81
+ resolve(false);
82
+ } else {
83
+ resolve(config.default);
84
+ }
85
+ });
86
+ });
70
87
  }
71
88
 
72
89
  export const ui = {
@@ -14,10 +14,13 @@ import {
14
14
  } from "./utils/npmCommands.js";
15
15
 
16
16
  export function createNpmPackageManager(version) {
17
- const supportedScanners =
18
- getMajorVersion(version) >= 22
19
- ? npm22AndAboveSupportedScanners
20
- : npm21AndBelowSupportedScanners;
17
+ // From npm v10.4.0 onwards, the npm commands output detailed information
18
+ // when using the --dry-run flag.
19
+ // We use that information to scan for dependency changes.
20
+ // For older versions of npm we have to rely on parsing the command arguments.
21
+ const supportedScanners = isPriorToNpm10_4(version)
22
+ ? npm10_3AndBelowSupportedScanners
23
+ : npm10_4AndAboveSupportedScanners;
21
24
 
22
25
  function isSupportedCommand(args) {
23
26
  const scanner = findDependencyScannerForCommand(supportedScanners, args);
@@ -30,14 +33,13 @@ export function createNpmPackageManager(version) {
30
33
  }
31
34
 
32
35
  return {
33
- getWarningMessage: () => warnForLimitedSupport(version),
34
36
  runCommand: runNpm,
35
37
  isSupportedCommand,
36
38
  getDependencyUpdatesForCommand,
37
39
  };
38
40
  }
39
41
 
40
- const npm22AndAboveSupportedScanners = {
42
+ const npm10_4AndAboveSupportedScanners = {
41
43
  [npmInstallCommand]: dryRunScanner(),
42
44
  [npmUpdateCommand]: dryRunScanner(),
43
45
  [npmCiCommand]: dryRunScanner(),
@@ -53,23 +55,22 @@ const npm22AndAboveSupportedScanners = {
53
55
  [npmInstallCiTestCommand]: dryRunScanner({ dryRunCommand: npmCiCommand }),
54
56
  };
55
57
 
56
- const npm21AndBelowSupportedScanners = {
58
+ const npm10_3AndBelowSupportedScanners = {
57
59
  [npmInstallCommand]: commandArgumentScanner(),
58
60
  [npmUpdateCommand]: commandArgumentScanner(),
59
61
  [npmExecCommand]: commandArgumentScanner({ ignoreDryRun: true }), // exec command doesn't support dry-run
60
62
  };
61
63
 
62
- function warnForLimitedSupport(version) {
63
- if (getMajorVersion(version) >= 22) {
64
- return null;
64
+ function isPriorToNpm10_4(version) {
65
+ try {
66
+ const [major, minor] = version.split(".").map(Number);
67
+ if (major < 10) return true;
68
+ if (major === 10 && minor < 4) return true;
69
+ return false;
70
+ } catch {
71
+ // Default to true: if version parsing fails, assume it's an older version
72
+ return true;
65
73
  }
66
-
67
- return `Aikido-npm will only scan the arguments of the install command for Node.js version prior to version 22.
68
- Please update your Node.js version to 22 or higher for full coverage. Current version: v${version}`;
69
- }
70
-
71
- function getMajorVersion(version) {
72
- return parseInt(version.split(".")[0]);
73
74
  }
74
75
 
75
76
  function findDependencyScannerForCommand(scanners, args) {
@@ -5,7 +5,6 @@ export function createNpxPackageManager() {
5
5
  const scanner = commandArgumentScanner();
6
6
 
7
7
  return {
8
- getWarningMessage: () => null,
9
8
  runCommand: runNpx,
10
9
  isSupportedCommand: (args) => scanner.shouldScan(args),
11
10
  getDependencyUpdatesForCommand: (args) => scanner.scan(args),
@@ -6,7 +6,6 @@ const scanner = commandArgumentScanner();
6
6
 
7
7
  export function createPnpmPackageManager() {
8
8
  return {
9
- getWarningMessage: () => null,
10
9
  runCommand: (args) => runPnpmCommand(args, "pnpm"),
11
10
  isSupportedCommand: (args) =>
12
11
  matchesCommand(args, "add") ||
@@ -26,7 +25,6 @@ export function createPnpmPackageManager() {
26
25
 
27
26
  export function createPnpxPackageManager() {
28
27
  return {
29
- getWarningMessage: () => null,
30
28
  runCommand: (args) => runPnpmCommand(args, "pnpx"),
31
29
  isSupportedCommand: () => true,
32
30
  getDependencyUpdatesForCommand: (args) =>
@@ -5,7 +5,6 @@ const scanner = commandArgumentScanner();
5
5
 
6
6
  export function createYarnPackageManager() {
7
7
  return {
8
- getWarningMessage: () => null,
9
8
  runCommand: runYarnCommand,
10
9
  isSupportedCommand: (args) =>
11
10
  matchesCommand(args, "add") ||