@aikidosec/safe-chain 0.0.4-connect-timeout-beta

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.
Files changed (94) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +257 -0
  3. package/bin/aikido-bun.js +14 -0
  4. package/bin/aikido-bunx.js +14 -0
  5. package/bin/aikido-npm.js +14 -0
  6. package/bin/aikido-npx.js +14 -0
  7. package/bin/aikido-pip.js +20 -0
  8. package/bin/aikido-pip3.js +21 -0
  9. package/bin/aikido-pnpm.js +14 -0
  10. package/bin/aikido-pnpx.js +14 -0
  11. package/bin/aikido-python.js +30 -0
  12. package/bin/aikido-python3.js +30 -0
  13. package/bin/aikido-uv.js +16 -0
  14. package/bin/aikido-yarn.js +14 -0
  15. package/bin/safe-chain.js +190 -0
  16. package/docs/banner.svg +151 -0
  17. package/docs/npm-to-binary-migration.md +89 -0
  18. package/docs/safe-package-manager-demo.gif +0 -0
  19. package/docs/safe-package-manager-demo.png +0 -0
  20. package/docs/shell-integration.md +149 -0
  21. package/package.json +68 -0
  22. package/src/api/aikido.js +54 -0
  23. package/src/api/npmApi.js +71 -0
  24. package/src/config/cliArguments.js +138 -0
  25. package/src/config/configFile.js +192 -0
  26. package/src/config/environmentVariables.js +7 -0
  27. package/src/config/settings.js +100 -0
  28. package/src/environment/environment.js +14 -0
  29. package/src/environment/userInteraction.js +122 -0
  30. package/src/main.js +104 -0
  31. package/src/packagemanager/_shared/matchesCommand.js +18 -0
  32. package/src/packagemanager/bun/createBunPackageManager.js +53 -0
  33. package/src/packagemanager/currentPackageManager.js +72 -0
  34. package/src/packagemanager/npm/createPackageManager.js +72 -0
  35. package/src/packagemanager/npm/dependencyScanner/commandArgumentScanner.js +74 -0
  36. package/src/packagemanager/npm/dependencyScanner/nullScanner.js +9 -0
  37. package/src/packagemanager/npm/parsing/parsePackagesFromInstallArgs.js +144 -0
  38. package/src/packagemanager/npm/runNpmCommand.js +25 -0
  39. package/src/packagemanager/npm/utils/abbrevs-generated.js +359 -0
  40. package/src/packagemanager/npm/utils/cmd-list.js +174 -0
  41. package/src/packagemanager/npm/utils/npmCommands.js +34 -0
  42. package/src/packagemanager/npx/createPackageManager.js +15 -0
  43. package/src/packagemanager/npx/dependencyScanner/commandArgumentScanner.js +43 -0
  44. package/src/packagemanager/npx/parsing/parsePackagesFromArguments.js +130 -0
  45. package/src/packagemanager/npx/runNpxCommand.js +25 -0
  46. package/src/packagemanager/pip/createPackageManager.js +21 -0
  47. package/src/packagemanager/pip/pipSettings.js +30 -0
  48. package/src/packagemanager/pip/runPipCommand.js +175 -0
  49. package/src/packagemanager/pnpm/createPackageManager.js +57 -0
  50. package/src/packagemanager/pnpm/dependencyScanner/commandArgumentScanner.js +35 -0
  51. package/src/packagemanager/pnpm/parsing/parsePackagesFromArguments.js +109 -0
  52. package/src/packagemanager/pnpm/runPnpmCommand.js +36 -0
  53. package/src/packagemanager/uv/createUvPackageManager.js +18 -0
  54. package/src/packagemanager/uv/runUvCommand.js +71 -0
  55. package/src/packagemanager/yarn/createPackageManager.js +41 -0
  56. package/src/packagemanager/yarn/dependencyScanner/commandArgumentScanner.js +35 -0
  57. package/src/packagemanager/yarn/parsing/parsePackagesFromArguments.js +128 -0
  58. package/src/packagemanager/yarn/runYarnCommand.js +41 -0
  59. package/src/registryProxy/certBundle.js +95 -0
  60. package/src/registryProxy/certUtils.js +128 -0
  61. package/src/registryProxy/http-utils.js +17 -0
  62. package/src/registryProxy/interceptors/createInterceptorForEcoSystem.js +25 -0
  63. package/src/registryProxy/interceptors/interceptorBuilder.js +140 -0
  64. package/src/registryProxy/interceptors/npm/modifyNpmInfo.js +177 -0
  65. package/src/registryProxy/interceptors/npm/npmInterceptor.js +47 -0
  66. package/src/registryProxy/interceptors/npm/parseNpmPackageUrl.js +43 -0
  67. package/src/registryProxy/interceptors/pipInterceptor.js +115 -0
  68. package/src/registryProxy/mitmRequestHandler.js +231 -0
  69. package/src/registryProxy/plainHttpProxy.js +95 -0
  70. package/src/registryProxy/registryProxy.js +184 -0
  71. package/src/registryProxy/tunnelRequestHandler.js +180 -0
  72. package/src/scanning/audit/index.js +129 -0
  73. package/src/scanning/index.js +82 -0
  74. package/src/scanning/malwareDatabase.js +131 -0
  75. package/src/shell-integration/helpers.js +213 -0
  76. package/src/shell-integration/path-wrappers/templates/unix-wrapper.template.sh +22 -0
  77. package/src/shell-integration/path-wrappers/templates/windows-wrapper.template.cmd +24 -0
  78. package/src/shell-integration/setup-ci.js +170 -0
  79. package/src/shell-integration/setup.js +127 -0
  80. package/src/shell-integration/shellDetection.js +37 -0
  81. package/src/shell-integration/startup-scripts/include-python/init-fish.fish +94 -0
  82. package/src/shell-integration/startup-scripts/include-python/init-posix.sh +81 -0
  83. package/src/shell-integration/startup-scripts/include-python/init-pwsh.ps1 +115 -0
  84. package/src/shell-integration/startup-scripts/init-fish.fish +71 -0
  85. package/src/shell-integration/startup-scripts/init-posix.sh +58 -0
  86. package/src/shell-integration/startup-scripts/init-pwsh.ps1 +92 -0
  87. package/src/shell-integration/supported-shells/bash.js +134 -0
  88. package/src/shell-integration/supported-shells/fish.js +77 -0
  89. package/src/shell-integration/supported-shells/powershell.js +73 -0
  90. package/src/shell-integration/supported-shells/windowsPowershell.js +73 -0
  91. package/src/shell-integration/supported-shells/zsh.js +74 -0
  92. package/src/shell-integration/teardown.js +64 -0
  93. package/src/utils/safeSpawn.js +137 -0
  94. package/tsconfig.json +21 -0
@@ -0,0 +1,170 @@
1
+ import chalk from "chalk";
2
+ import { ui } from "../environment/userInteraction.js";
3
+ import { getPackageManagerList, knownAikidoTools } from "./helpers.js";
4
+ import fs from "fs";
5
+ import os from "os";
6
+ import path from "path";
7
+ import { fileURLToPath } from "url";
8
+ import { includePython } from "../config/cliArguments.js";
9
+ import { ECOSYSTEM_PY } from "../config/settings.js";
10
+
11
+ /** @type {string} */
12
+ // This checks the current file's dirname in a way that's compatible with:
13
+ // - Modulejs (import.meta.url)
14
+ // - ES modules (__dirname)
15
+ // This is needed because safe-chain's npm package is built using ES modules,
16
+ // but building the binaries requires commonjs.
17
+ let dirname;
18
+ if (import.meta.url) {
19
+ const filename = fileURLToPath(import.meta.url);
20
+ dirname = path.dirname(filename);
21
+ } else {
22
+ dirname = __dirname;
23
+ }
24
+
25
+ /**
26
+ * Loops over the detected shells and calls the setup function for each.
27
+ */
28
+ export async function setupCi() {
29
+ ui.writeInformation(
30
+ chalk.bold("Setting up shell aliases.") +
31
+ ` This will wrap safe-chain around ${getPackageManagerList()}.`
32
+ );
33
+ ui.emptyLine();
34
+
35
+ const shimsDir = path.join(os.homedir(), ".safe-chain", "shims");
36
+ const binDir = path.join(os.homedir(), ".safe-chain", "bin");
37
+ // Create the shims directory if it doesn't exist
38
+ if (!fs.existsSync(shimsDir)) {
39
+ fs.mkdirSync(shimsDir, { recursive: true });
40
+ }
41
+
42
+ createShims(shimsDir);
43
+ ui.writeInformation(`Created shims in ${shimsDir}`);
44
+ modifyPathForCi(shimsDir, binDir);
45
+ ui.writeInformation(`Added shims directory to PATH for CI environments.`);
46
+ }
47
+
48
+ /**
49
+ * @param {string} shimsDir
50
+ *
51
+ * @returns {void}
52
+ */
53
+ function createUnixShims(shimsDir) {
54
+ // Read the template file
55
+ const templatePath = path.resolve(
56
+ dirname,
57
+ "path-wrappers",
58
+ "templates",
59
+ "unix-wrapper.template.sh"
60
+ );
61
+
62
+ if (!fs.existsSync(templatePath)) {
63
+ ui.writeError(`Template file not found: ${templatePath}`);
64
+ return;
65
+ }
66
+
67
+ const template = fs.readFileSync(templatePath, "utf-8");
68
+
69
+ // Create a shim for each tool
70
+ let created = 0;
71
+ for (const toolInfo of getToolsToSetup()) {
72
+ const shimContent = template
73
+ .replaceAll("{{PACKAGE_MANAGER}}", toolInfo.tool)
74
+ .replaceAll("{{AIKIDO_COMMAND}}", toolInfo.aikidoCommand);
75
+
76
+ const shimPath = path.join(shimsDir, toolInfo.tool);
77
+ fs.writeFileSync(shimPath, shimContent, "utf-8");
78
+
79
+ // Make the shim executable on Unix systems
80
+ fs.chmodSync(shimPath, 0o755);
81
+ created++;
82
+ }
83
+
84
+ ui.writeInformation(`Created ${created} Unix shim(s) in ${shimsDir}`);
85
+ }
86
+
87
+ /**
88
+ * @param {string} shimsDir
89
+ *
90
+ * @returns {void}
91
+ */
92
+ function createWindowsShims(shimsDir) {
93
+ // Read the template file
94
+ const templatePath = path.resolve(
95
+ dirname,
96
+ "path-wrappers",
97
+ "templates",
98
+ "windows-wrapper.template.cmd"
99
+ );
100
+
101
+ if (!fs.existsSync(templatePath)) {
102
+ ui.writeError(`Windows template file not found: ${templatePath}`);
103
+ return;
104
+ }
105
+
106
+ const template = fs.readFileSync(templatePath, "utf-8");
107
+
108
+ // Create a shim for each tool
109
+ let created = 0;
110
+ for (const toolInfo of getToolsToSetup()) {
111
+ const shimContent = template
112
+ .replaceAll("{{PACKAGE_MANAGER}}", toolInfo.tool)
113
+ .replaceAll("{{AIKIDO_COMMAND}}", toolInfo.aikidoCommand);
114
+
115
+ const shimPath = `${shimsDir}/${toolInfo.tool}.cmd`;
116
+ fs.writeFileSync(shimPath, shimContent, "utf-8");
117
+ created++;
118
+ }
119
+
120
+ ui.writeInformation(`Created ${created} Windows shim(s) in ${shimsDir}`);
121
+ }
122
+
123
+ /**
124
+ * @param {string} shimsDir
125
+ *
126
+ * @returns {void}
127
+ */
128
+ function createShims(shimsDir) {
129
+ if (os.platform() === "win32") {
130
+ createWindowsShims(shimsDir);
131
+ } else {
132
+ createUnixShims(shimsDir);
133
+ }
134
+ }
135
+
136
+ /**
137
+ * @param {string} shimsDir
138
+ * @param {string} binDir
139
+ *
140
+ * @returns {void}
141
+ */
142
+ function modifyPathForCi(shimsDir, binDir) {
143
+ if (process.env.GITHUB_PATH) {
144
+ // In GitHub Actions, append the shims directory to GITHUB_PATH
145
+ fs.appendFileSync(
146
+ process.env.GITHUB_PATH,
147
+ shimsDir + os.EOL + binDir + os.EOL,
148
+ "utf-8"
149
+ );
150
+ ui.writeInformation(
151
+ `Added shims directory to GITHUB_PATH for GitHub Actions.`
152
+ );
153
+ }
154
+
155
+ if (process.env.TF_BUILD) {
156
+ // In Azure Pipelines, prepending the path is done via a logging command:
157
+ // ##vso[task.prependpath]/path/to/add
158
+ // Logging this to stdout will cause the Azure Pipelines agent to pick it up
159
+ ui.writeInformation("##vso[task.prependpath]" + shimsDir);
160
+ ui.writeInformation("##vso[task.prependpath]" + binDir);
161
+ }
162
+ }
163
+
164
+ function getToolsToSetup() {
165
+ if (includePython()) {
166
+ return knownAikidoTools;
167
+ } else {
168
+ return knownAikidoTools.filter((tool) => tool.ecoSystem !== ECOSYSTEM_PY);
169
+ }
170
+ }
@@ -0,0 +1,127 @@
1
+ import chalk from "chalk";
2
+ import { ui } from "../environment/userInteraction.js";
3
+ import { detectShells } from "./shellDetection.js";
4
+ import { knownAikidoTools, getPackageManagerList } from "./helpers.js";
5
+ import fs from "fs";
6
+ import os from "os";
7
+ import path from "path";
8
+ import { includePython } from "../config/cliArguments.js";
9
+ import { fileURLToPath } from "url";
10
+
11
+ /** @type {string} */
12
+ // This checks the current file's dirname in a way that's compatible with:
13
+ // - Modulejs (import.meta.url)
14
+ // - ES modules (__dirname)
15
+ // This is needed because safe-chain's npm package is built using ES modules,
16
+ // but building the binaries requires commonjs.
17
+ let dirname;
18
+ if (import.meta.url) {
19
+ const filename = fileURLToPath(import.meta.url);
20
+ dirname = path.dirname(filename);
21
+ } else {
22
+ dirname = __dirname;
23
+ }
24
+
25
+ /**
26
+ * Loops over the detected shells and calls the setup function for each.
27
+ */
28
+ export async function setup() {
29
+ ui.writeInformation(
30
+ chalk.bold("Setting up shell aliases.") +
31
+ ` This will wrap safe-chain around ${getPackageManagerList()}.`
32
+ );
33
+ ui.emptyLine();
34
+
35
+ copyStartupFiles();
36
+
37
+ try {
38
+ const shells = detectShells();
39
+ if (shells.length === 0) {
40
+ ui.writeError("No supported shells detected. Cannot set up aliases.");
41
+ return;
42
+ }
43
+
44
+ ui.writeInformation(
45
+ `Detected ${shells.length} supported shell(s): ${shells
46
+ .map((shell) => chalk.bold(shell.name))
47
+ .join(", ")}.`
48
+ );
49
+
50
+ let updatedCount = 0;
51
+ for (const shell of shells) {
52
+ if (setupShell(shell)) {
53
+ updatedCount++;
54
+ }
55
+ }
56
+
57
+ if (updatedCount > 0) {
58
+ ui.emptyLine();
59
+ ui.writeInformation(`Please restart your terminal to apply the changes.`);
60
+ }
61
+ } catch (/** @type {any} */ error) {
62
+ ui.writeError(
63
+ `Failed to set up shell aliases: ${error.message}. Please check your shell configuration.`
64
+ );
65
+ return;
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Calls the setup function for the given shell and reports the result.
71
+ * @param {import("./shellDetection.js").Shell} shell
72
+ */
73
+ function setupShell(shell) {
74
+ let success = false;
75
+ let error;
76
+ try {
77
+ shell.teardown(knownAikidoTools); // First, tear down to prevent duplicate aliases
78
+ success = shell.setup(knownAikidoTools);
79
+ } catch (/** @type {any} */ err) {
80
+ success = false;
81
+ error = err;
82
+ }
83
+
84
+ if (success) {
85
+ ui.writeInformation(
86
+ `${chalk.bold("- " + shell.name + ":")} ${chalk.green(
87
+ "Setup successful"
88
+ )}`
89
+ );
90
+ } else {
91
+ ui.writeError(
92
+ `${chalk.bold("- " + shell.name + ":")} ${chalk.red(
93
+ "Setup failed"
94
+ )}. Please check your ${shell.name} configuration.`
95
+ );
96
+ if (error) {
97
+ let message = ` Error: ${error.message}`;
98
+ if (error.code) {
99
+ message += ` (code: ${error.code})`;
100
+ }
101
+ ui.writeError(message);
102
+ }
103
+ }
104
+
105
+ return success;
106
+ }
107
+
108
+ function copyStartupFiles() {
109
+ const startupFiles = ["init-posix.sh", "init-pwsh.ps1", "init-fish.fish"];
110
+
111
+ for (const file of startupFiles) {
112
+ const targetDir = path.join(os.homedir(), ".safe-chain", "scripts");
113
+ const targetPath = path.join(os.homedir(), ".safe-chain", "scripts", file);
114
+
115
+ if (!fs.existsSync(targetDir)) {
116
+ fs.mkdirSync(targetDir, { recursive: true });
117
+ }
118
+
119
+ // Use absolute path for source
120
+ const sourcePath = path.join(
121
+ dirname,
122
+ includePython() ? "startup-scripts/include-python" : "startup-scripts",
123
+ file
124
+ );
125
+ fs.copyFileSync(sourcePath, targetPath);
126
+ }
127
+ }
@@ -0,0 +1,37 @@
1
+ import zsh from "./supported-shells/zsh.js";
2
+ import bash from "./supported-shells/bash.js";
3
+ import powershell from "./supported-shells/powershell.js";
4
+ import windowsPowershell from "./supported-shells/windowsPowershell.js";
5
+ import fish from "./supported-shells/fish.js";
6
+ import { ui } from "../environment/userInteraction.js";
7
+
8
+ /**
9
+ * @typedef {Object} Shell
10
+ * @property {string} name
11
+ * @property {() => boolean} isInstalled
12
+ * @property {(tools: import("./helpers.js").AikidoTool[]) => boolean} setup
13
+ * @property {(tools: import("./helpers.js").AikidoTool[]) => boolean} teardown
14
+ */
15
+
16
+ /**
17
+ * @returns {Shell[]}
18
+ */
19
+ export function detectShells() {
20
+ let possibleShells = [zsh, bash, powershell, windowsPowershell, fish];
21
+ let availableShells = [];
22
+
23
+ try {
24
+ for (const shell of possibleShells) {
25
+ if (shell.isInstalled()) {
26
+ availableShells.push(shell);
27
+ }
28
+ }
29
+ } catch (/** @type {any} */ error) {
30
+ ui.writeError(
31
+ `We were not able to detect which shells are installed on your system. Please check your shell configuration. Error: ${error.message}`
32
+ );
33
+ return [];
34
+ }
35
+
36
+ return availableShells;
37
+ }
@@ -0,0 +1,94 @@
1
+ set -gx PATH $PATH $HOME/.safe-chain/bin
2
+
3
+ function npx
4
+ wrapSafeChainCommand "npx" $argv
5
+ end
6
+
7
+ function yarn
8
+ wrapSafeChainCommand "yarn" $argv
9
+ end
10
+
11
+ function pnpm
12
+ wrapSafeChainCommand "pnpm" $argv
13
+ end
14
+
15
+ function pnpx
16
+ wrapSafeChainCommand "pnpx" $argv
17
+ end
18
+
19
+ function bun
20
+ wrapSafeChainCommand "bun" $argv
21
+ end
22
+
23
+ function bunx
24
+ wrapSafeChainCommand "bunx" $argv
25
+ end
26
+
27
+ function npm
28
+ # If args is just -v or --version and nothing else, just run the `npm -v` command
29
+ # This is because nvm uses this to check the version of npm
30
+ set argc (count $argv)
31
+ if test $argc -eq 1
32
+ switch $argv[1]
33
+ case "-v" "--version"
34
+ command npm $argv
35
+ return
36
+ end
37
+ end
38
+
39
+ wrapSafeChainCommand "npm" $argv
40
+ end
41
+
42
+
43
+ function pip
44
+ wrapSafeChainCommand "pip" $argv
45
+ end
46
+
47
+ function pip3
48
+ wrapSafeChainCommand "pip3" $argv
49
+ end
50
+
51
+ function uv
52
+ wrapSafeChainCommand "uv" $argv
53
+ end
54
+
55
+ # `python -m pip`, `python -m pip3`.
56
+ function python
57
+ wrapSafeChainCommand "python" $argv
58
+ end
59
+
60
+ # `python3 -m pip`, `python3 -m pip3'.
61
+ function python3
62
+ wrapSafeChainCommand "python3" $argv
63
+ end
64
+
65
+ function printSafeChainWarning
66
+ set original_cmd $argv[1]
67
+
68
+ # Fish equivalent of ANSI color codes: yellow background, black text for "Warning:"
69
+ set_color -b yellow black
70
+ printf "Warning:"
71
+ set_color normal
72
+ printf " safe-chain is not available to protect you from installing malware. %s will run without it.\n" $original_cmd
73
+
74
+ # Cyan text for the install command
75
+ printf "Install safe-chain by using "
76
+ set_color cyan
77
+ printf "npm install -g @aikidosec/safe-chain"
78
+ set_color normal
79
+ printf ".\n"
80
+ end
81
+
82
+ function wrapSafeChainCommand
83
+ set original_cmd $argv[1]
84
+ set cmd_args $argv[2..-1]
85
+
86
+ if type -q safe-chain
87
+ # If the safe-chain command is available, just run it with the provided arguments
88
+ safe-chain $original_cmd $cmd_args
89
+ else
90
+ # If the safe-chain command is not available, print a warning and run the original command
91
+ printSafeChainWarning $original_cmd
92
+ command $original_cmd $cmd_args
93
+ end
94
+ end
@@ -0,0 +1,81 @@
1
+ export PATH="$PATH:$HOME/.safe-chain/bin"
2
+
3
+ function npx() {
4
+ wrapSafeChainCommand "npx" "$@"
5
+ }
6
+
7
+ function yarn() {
8
+ wrapSafeChainCommand "yarn" "$@"
9
+ }
10
+
11
+ function pnpm() {
12
+ wrapSafeChainCommand "pnpm" "$@"
13
+ }
14
+
15
+ function pnpx() {
16
+ wrapSafeChainCommand "pnpx" "$@"
17
+ }
18
+
19
+ function bun() {
20
+ wrapSafeChainCommand "bun" "$@"
21
+ }
22
+
23
+ function bunx() {
24
+ wrapSafeChainCommand "bunx" "$@"
25
+ }
26
+
27
+ function npm() {
28
+ if [[ "$1" == "-v" || "$1" == "--version" ]] && [[ $# -eq 1 ]]; then
29
+ # If args is just -v or --version and nothing else, just run the npm version command
30
+ # This is because nvm uses this to check the version of npm
31
+ command npm "$@"
32
+ return
33
+ fi
34
+
35
+ wrapSafeChainCommand "npm" "$@"
36
+ }
37
+
38
+
39
+ function pip() {
40
+ wrapSafeChainCommand "pip" "$@"
41
+ }
42
+
43
+ function pip3() {
44
+ wrapSafeChainCommand "pip3" "$@"
45
+ }
46
+
47
+ function uv() {
48
+ wrapSafeChainCommand "uv" "$@"
49
+ }
50
+
51
+ # `python -m pip`, `python -m pip3`.
52
+ function python() {
53
+ wrapSafeChainCommand "python" "$@"
54
+ }
55
+
56
+ # `python3 -m pip`, `python3 -m pip3'.
57
+ function python3() {
58
+ wrapSafeChainCommand "python3" "$@"
59
+ }
60
+
61
+ function printSafeChainWarning() {
62
+ # \033[43;30m is used to set the background color to yellow and text color to black
63
+ # \033[0m is used to reset the text formatting
64
+ printf "\033[43;30mWarning:\033[0m safe-chain is not available to protect you from installing malware. %s will run without it.\n" "$1"
65
+ # \033[36m is used to set the text color to cyan
66
+ printf "Install safe-chain by using \033[36mnpm install -g @aikidosec/safe-chain\033[0m.\n"
67
+ }
68
+
69
+ function wrapSafeChainCommand() {
70
+ local original_cmd="$1"
71
+
72
+ if command -v safe-chain > /dev/null 2>&1; then
73
+ # If the aikido command is available, just run it with the provided arguments
74
+ safe-chain "$@"
75
+ else
76
+ # If the aikido command is not available, print a warning and run the original command
77
+ printSafeChainWarning "$original_cmd"
78
+
79
+ command "$original_cmd" "$@"
80
+ fi
81
+ }
@@ -0,0 +1,115 @@
1
+ # Use cross-platform path separator (: on Unix, ; on Windows)
2
+ $pathSeparator = if ($IsWindows) { ';' } else { ':' }
3
+ $safeChainBin = Join-Path $HOME '.safe-chain' 'bin'
4
+ $env:PATH = "$env:PATH$pathSeparator$safeChainBin"
5
+
6
+ function npx {
7
+ Invoke-WrappedCommand "npx" $args
8
+ }
9
+
10
+ function yarn {
11
+ Invoke-WrappedCommand "yarn" $args
12
+ }
13
+
14
+ function pnpm {
15
+ Invoke-WrappedCommand "pnpm" $args
16
+ }
17
+
18
+ function pnpx {
19
+ Invoke-WrappedCommand "pnpx" $args
20
+ }
21
+
22
+ function bun {
23
+ Invoke-WrappedCommand "bun" $args
24
+ }
25
+
26
+ function bunx {
27
+ Invoke-WrappedCommand "bunx" $args
28
+ }
29
+
30
+ function npm {
31
+ # If args is just -v or --version and nothing else, just run the npm version command
32
+ # This is because nvm uses this to check the version of npm
33
+ if (($args.Length -eq 1) -and (($args[0] -eq "-v") -or ($args[0] -eq "--version"))) {
34
+ Invoke-RealCommand "npm" $args
35
+ return
36
+ }
37
+
38
+ Invoke-WrappedCommand "npm" $args
39
+ }
40
+
41
+ function pip {
42
+ Invoke-WrappedCommand "pip" $args
43
+ }
44
+
45
+ function pip3 {
46
+ Invoke-WrappedCommand "pip3" $args
47
+ }
48
+
49
+ function uv {
50
+ Invoke-WrappedCommand "uv" $args
51
+ }
52
+
53
+ # `python -m pip`, `python -m pip3`.
54
+ function python {
55
+ Invoke-WrappedCommand 'python' $args
56
+ }
57
+
58
+ # `python3 -m pip`, `python3 -m pip3'.
59
+ function python3 {
60
+ Invoke-WrappedCommand 'python3' $args
61
+ }
62
+
63
+
64
+ function Write-SafeChainWarning {
65
+ param([string]$Command)
66
+
67
+ # PowerShell equivalent of ANSI color codes: yellow background, black text for "Warning:"
68
+ Write-Host "Warning:" -BackgroundColor Yellow -ForegroundColor Black -NoNewline
69
+ Write-Host " safe-chain is not available to protect you from installing malware. $Command will run without it."
70
+
71
+ # Cyan text for the install command
72
+ Write-Host "Install safe-chain by using " -NoNewline
73
+ Write-Host "npm install -g @aikidosec/safe-chain" -ForegroundColor Cyan -NoNewline
74
+ Write-Host "."
75
+ }
76
+
77
+ function Test-CommandAvailable {
78
+ param([string]$Command)
79
+
80
+ try {
81
+ Get-Command $Command -ErrorAction Stop | Out-Null
82
+ return $true
83
+ }
84
+ catch {
85
+ return $false
86
+ }
87
+ }
88
+
89
+ function Invoke-RealCommand {
90
+ param(
91
+ [string]$Command,
92
+ [string[]]$Arguments
93
+ )
94
+
95
+ # Find the real executable to avoid calling our wrapped functions
96
+ $realCommand = Get-Command -Name $Command -CommandType Application | Select-Object -First 1
97
+ if ($realCommand) {
98
+ & $realCommand.Source @Arguments
99
+ }
100
+ }
101
+
102
+ function Invoke-WrappedCommand {
103
+ param(
104
+ [string]$OriginalCmd,
105
+ [string[]]$Arguments
106
+ )
107
+
108
+ if (Test-CommandAvailable "safe-chain") {
109
+ & safe-chain $OriginalCmd @Arguments
110
+ }
111
+ else {
112
+ Write-SafeChainWarning $OriginalCmd
113
+ Invoke-RealCommand $OriginalCmd $Arguments
114
+ }
115
+ }
@@ -0,0 +1,71 @@
1
+ set -gx PATH $PATH $HOME/.safe-chain/bin
2
+
3
+ function npx
4
+ wrapSafeChainCommand "npx" $argv
5
+ end
6
+
7
+ function yarn
8
+ wrapSafeChainCommand "yarn" $argv
9
+ end
10
+
11
+ function pnpm
12
+ wrapSafeChainCommand "pnpm" $argv
13
+ end
14
+
15
+ function pnpx
16
+ wrapSafeChainCommand "pnpx" $argv
17
+ end
18
+
19
+ function bun
20
+ wrapSafeChainCommand "bun" $argv
21
+ end
22
+
23
+ function bunx
24
+ wrapSafeChainCommand "bunx" $argv
25
+ end
26
+
27
+ function npm
28
+ # If args is just -v or --version and nothing else, just run the `npm -v` command
29
+ # This is because nvm uses this to check the version of npm
30
+ set argc (count $argv)
31
+ if test $argc -eq 1
32
+ switch $argv[1]
33
+ case "-v" "--version"
34
+ command npm $argv
35
+ return
36
+ end
37
+ end
38
+
39
+ wrapSafeChainCommand "npm" $argv
40
+ end
41
+
42
+ function printSafeChainWarning
43
+ set original_cmd $argv[1]
44
+
45
+ # Fish equivalent of ANSI color codes: yellow background, black text for "Warning:"
46
+ set_color -b yellow black
47
+ printf "Warning:"
48
+ set_color normal
49
+ printf " safe-chain is not available to protect you from installing malware. %s will run without it.\n" $original_cmd
50
+
51
+ # Cyan text for the install command
52
+ printf "Install safe-chain by using "
53
+ set_color cyan
54
+ printf "npm install -g @aikidosec/safe-chain"
55
+ set_color normal
56
+ printf ".\n"
57
+ end
58
+
59
+ function wrapSafeChainCommand
60
+ set original_cmd $argv[1]
61
+ set cmd_args $argv[2..-1]
62
+
63
+ if type -q safe-chain
64
+ # If the safe-chain command is available, just run it with the provided arguments
65
+ safe-chain $original_cmd $cmd_args
66
+ else
67
+ # If the safe-chain command is not available, print a warning and run the original command
68
+ printSafeChainWarning $original_cmd
69
+ command $original_cmd $cmd_args
70
+ end
71
+ end