@aikidosec/safe-chain 1.5.2 β†’ 1.5.3

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/README.md CHANGED
@@ -25,6 +25,8 @@ Aikido Safe Chain supports the following package managers:
25
25
  - πŸ“¦ **yarn**
26
26
  - πŸ“¦ **pnpm**
27
27
  - πŸ“¦ **pnpx**
28
+ - πŸ“¦ **rush**
29
+ - πŸ“¦ **rushx**
28
30
  - πŸ“¦ **bun**
29
31
  - πŸ“¦ **bunx**
30
32
  - πŸ“¦ **pip**
@@ -75,7 +77,7 @@ You can find all available versions on the [releases page](https://github.com/Ai
75
77
  ### Verify the installation
76
78
 
77
79
  1. **❗Restart your terminal** to start using the Aikido Safe Chain.
78
- - This step is crucial as it ensures that the shell aliases for npm, npx, yarn, pnpm, pnpx, bun, bunx, pip, pip3, poetry, uv, uvx and pipx are loaded correctly. If you do not restart your terminal, the aliases will not be available.
80
+ - This step is crucial as it ensures that the shell aliases for npm, npx, yarn, pnpm, pnpx, rush, rushx, bun, bunx, pip, pip3, poetry, uv, uvx and pipx are loaded correctly. If you do not restart your terminal, the aliases will not be available.
79
81
 
80
82
  2. **Verify the installation** by running the verification command:
81
83
 
@@ -106,7 +108,7 @@ You can find all available versions on the [releases page](https://github.com/Ai
106
108
 
107
109
  - The output should show that Aikido Safe Chain is blocking the installation of these test packages as they are flagged as malware.
108
110
 
109
- When running `npm`, `npx`, `yarn`, `pnpm`, `pnpx`, `bun`, `bunx`, `pip`, `pip3`, `uv`, `uvx`, `poetry` and `pipx` commands, the Aikido Safe Chain will automatically check for malware in the packages you are trying to install. It also intercepts Python module invocations for pip when available (e.g., `python -m pip install ...`, `python3 -m pip download ...`). If any malware is detected, it will prompt you to exit the command.
111
+ When running `npm`, `npx`, `yarn`, `pnpm`, `pnpx`, `rush`, `rushx`, `bun`, `bunx`, `pip`, `pip3`, `uv`, `uvx`, `poetry` and `pipx` commands, the Aikido Safe Chain will automatically check for malware in the packages you are trying to install. It also intercepts Python module invocations for pip when available (e.g., `python -m pip install ...`, `python3 -m pip download ...`). If any malware is detected, it will prompt you to exit the command.
110
112
 
111
113
  You can check the installed version by running:
112
114
 
@@ -118,7 +120,7 @@ safe-chain --version
118
120
 
119
121
  ### Malware Blocking
120
122
 
121
- The Aikido Safe Chain works by running a lightweight proxy server that intercepts package downloads from the npm registry and PyPI. When you run npm, npx, yarn, pnpm, pnpx, bun, bunx, pip, pip3, uv, uvx, poetry or pipx commands, all package downloads are routed through this local proxy, which verifies packages in real-time against **[Aikido Intel - Open Sources Threat Intelligence](https://intel.aikido.dev/?tab=malware)**. If malware is detected in any package (including deep dependencies), the proxy blocks the download before the malicious code reaches your machine.
123
+ The Aikido Safe Chain works by running a lightweight proxy server that intercepts package downloads from the npm registry and PyPI. When you run npm, npx, yarn, pnpm, pnpx, rush, rushx, bun, bunx, pip, pip3, uv, uvx, poetry or pipx commands, all package downloads are routed through this local proxy, which verifies packages in real-time against **[Aikido Intel - Open Sources Threat Intelligence](https://intel.aikido.dev/?tab=malware)**. If malware is detected in any package (including deep dependencies), the proxy blocks the download before the malicious code reaches your machine.
122
124
 
123
125
  ### Minimum package age
124
126
 
@@ -137,7 +139,7 @@ By default, the minimum package age is 48 hours. This provides an additional sec
137
139
 
138
140
  ### Shell Integration
139
141
 
140
- The Aikido Safe Chain integrates with your shell to provide a seamless experience when using npm, npx, yarn, pnpm, pnpx, bun, bunx, and Python package managers (pip, uv, uvx, poetry, pipx). It sets up aliases for these commands so that they are wrapped by the Aikido Safe Chain commands, which manage the proxy server before executing the original commands. We currently support:
142
+ The Aikido Safe Chain integrates with your shell to provide a seamless experience when using npm, npx, yarn, pnpm, pnpx, rush, rushx, bun, bunx, and Python package managers (pip, uv, uvx, poetry, pipx). It sets up aliases for these commands so that they are wrapped by the Aikido Safe Chain commands, which manage the proxy server before executing the original commands. We currently support:
141
143
 
142
144
  - βœ… **Bash**
143
145
  - βœ… **Zsh**
@@ -548,4 +550,16 @@ npm-ci:
548
550
 
549
551
  # Troubleshooting
550
552
 
551
- Having issues? See the [Troubleshooting Guide](https://help.aikido.dev/code-scanning/aikido-malware-scanning/safe-chain-troubleshooting) for help with common problems.
553
+ Having issues? See the [Troubleshooting Guide](./docs/troubleshooting) for help with common problems.
554
+
555
+ # Report Issues
556
+
557
+ If you encounter problems:
558
+
559
+ 1. Visit [GitHub Issues](https://github.com/AikidoSec/safe-chain/issues)
560
+ 2. Include:
561
+ * Operating system and version
562
+ * Shell type and version
563
+ * `safe-chain --version` output
564
+ * Output from verification commands
565
+ * Verbose logs of the failing command (add the `--safe-chain-logging=verbose` argument)
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { main } from "../src/main.js";
4
+ import { initializePackageManager } from "../src/packagemanager/currentPackageManager.js";
5
+ import { setEcoSystem, ECOSYSTEM_JS } from "../src/config/settings.js";
6
+
7
+ setEcoSystem(ECOSYSTEM_JS);
8
+ const packageManagerName = "rush";
9
+ initializePackageManager(packageManagerName);
10
+
11
+ (async () => {
12
+ var exitCode = await main(process.argv.slice(2));
13
+ process.exit(exitCode);
14
+ })();
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { main } from "../src/main.js";
4
+ import { initializePackageManager } from "../src/packagemanager/currentPackageManager.js";
5
+ import { setEcoSystem, ECOSYSTEM_JS } from "../src/config/settings.js";
6
+
7
+ setEcoSystem(ECOSYSTEM_JS);
8
+ const packageManagerName = "rushx";
9
+ initializePackageManager(packageManagerName);
10
+
11
+ (async () => {
12
+ var exitCode = await main(process.argv.slice(2));
13
+ process.exit(exitCode);
14
+ })();
package/bin/safe-chain.js CHANGED
@@ -108,7 +108,7 @@ function writeHelp() {
108
108
  ui.writeInformation(
109
109
  `- ${chalk.cyan(
110
110
  "safe-chain setup",
111
- )}: This will setup your shell to wrap safe-chain around npm, npx, yarn, pnpm, pnpx, bun, bunx, pip and pip3.`,
111
+ )}: This will setup your shell to wrap safe-chain around npm, npx, yarn, pnpm, pnpx, rush, rushx, bun, bunx, pip and pip3.`,
112
112
  );
113
113
  ui.writeInformation(
114
114
  `- ${chalk.cyan(
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- The shell integration automatically wraps common package manager commands (`npm`, `npx`, `yarn`, `pnpm`, `pnpx`, `bun`, `bunx`, `pip`, `pip3`, `uv`, `uvx`, `poetry`, `pipx`) with Aikido's security scanning functionality. It also intercepts Python module invocations for pip when available: `python -m pip`, `python -m pip3`, `python3 -m pip`, `python3 -m pip3`. This is achieved by sourcing startup scripts that define shell functions to wrap these commands with their Aikido-protected equivalents.
5
+ The shell integration automatically wraps common package manager commands (`npm`, `npx`, `yarn`, `pnpm`, `pnpx`, `rush`, `rushx`, `bun`, `bunx`, `pip`, `pip3`, `uv`, `uvx`, `poetry`, `pipx`) with Aikido's security scanning functionality. It also intercepts Python module invocations for pip when available: `python -m pip`, `python -m pip3`, `python3 -m pip`, `python3 -m pip3`. This is achieved by sourcing startup scripts that define shell functions to wrap these commands with their Aikido-protected equivalents.
6
6
 
7
7
  ## Supported Shells
8
8
 
@@ -28,7 +28,7 @@ This command:
28
28
 
29
29
  - Copies necessary startup scripts to Safe Chain's installation directory (`~/.safe-chain/scripts`)
30
30
  - Detects all supported shells on your system
31
- - Sources each shell's startup file to add Safe Chain functions for `npm`, `npx`, `yarn`, `pnpm`, `pnpx`, `bun`, `bunx`, `pip`, `pip3`, `uv`, `uvx`, `poetry` and `pipx`
31
+ - Sources each shell's startup file to add Safe Chain functions for `npm`, `npx`, `yarn`, `pnpm`, `pnpx`, `rush`, `rushx`, `bun`, `bunx`, `pip`, `pip3`, `uv`, `uvx`, `poetry` and `pipx`
32
32
  - Adds lightweight interceptors so `python -m pip[...]` and `python3 -m pip[...]` route through Safe Chain when invoked by name
33
33
 
34
34
  ❗ After running this command, **you must restart your terminal** for the changes to take effect. This ensures that the startup scripts are sourced correctly.
@@ -78,7 +78,7 @@ The system modifies the following files to source Safe Chain startup scripts:
78
78
  This means the shell functions are working but the Aikido commands aren't installed or available in your PATH:
79
79
 
80
80
  - Make sure Aikido Safe Chain is properly installed on your system
81
- - Verify the `aikido-npm`, `aikido-npx`, `aikido-yarn`, `aikido-pnpm`, `aikido-pnpx`, `aikido-bun`, `aikido-bunx`, `aikido-pip`, `aikido-pip3`, `aikido-uv`, `aikido-uvx`, `aikido-poetry` and `aikido-pipx` commands exist
81
+ - Verify the `aikido-npm`, `aikido-npx`, `aikido-yarn`, `aikido-pnpm`, `aikido-pnpx`, `aikido-rush`, `aikido-rushx`, `aikido-bun`, `aikido-bunx`, `aikido-pip`, `aikido-pip3`, `aikido-uv`, `aikido-uvx`, `aikido-poetry` and `aikido-pipx` commands exist
82
82
  - Check that these commands are in your system's PATH
83
83
 
84
84
  ### Manual Verification
@@ -121,7 +121,7 @@ npm() {
121
121
  }
122
122
  ```
123
123
 
124
- Repeat this pattern for `npx`, `yarn`, `pnpm`, `pnpx`, `bun`, `bunx`, `pip`, `pip3`, `uv`, `uvx`, `poetry` and `pipx` using their respective `aikido-*` commands. After adding these functions, restart your terminal to apply the changes.
124
+ Repeat this pattern for `npx`, `yarn`, `pnpm`, `pnpx`, `rush`, `rushx`, `bun`, `bunx`, `pip`, `pip3`, `uv`, `uvx`, `poetry` and `pipx` using their respective `aikido-*` commands. After adding these functions, restart your terminal to apply the changes.
125
125
 
126
126
  To intercept Python module invocations for pip without altering Python itself, you can add small forwarding functions:
127
127
 
@@ -4,49 +4,38 @@ This guide helps you diagnose and resolve common issues with Aikido Safe Chain.
4
4
 
5
5
  ## Verification & Diagnostics
6
6
 
7
- ### Check Installation
7
+ **Check Installation**
8
8
 
9
9
  ```bash
10
10
  # Check version
11
11
  safe-chain --version
12
12
  ```
13
13
 
14
- ### Verify Shell Integration
14
+ **Verify Shell Integration**
15
15
 
16
16
  Run the verification command for your package manager:
17
17
 
18
18
  ```bash
19
19
  npm safe-chain-verify
20
20
  pnpm safe-chain-verify
21
- pip safe-chain-verify
22
- uv safe-chain-verify
23
-
24
- # Any other supported package manager: {packagemanager} safe-chain-verify
25
21
  ```
26
22
 
23
+ ```
27
24
  Expected output: `OK: Safe-chain works!`
25
+ ```
28
26
 
29
- ### Test Malware Blocking
27
+ **Test Malware Blocking**
30
28
 
31
29
  Verify that malware detection is working:
32
-
33
- **For JavaScript/Node.js:**
34
-
35
- ```bash
36
- npm install safe-chain-test
37
30
  ```
38
-
39
- **For Python:**
40
-
41
- ```bash
42
- pip3 install safe-chain-pi-test
31
+ npm install safe-chain-test
43
32
  ```
44
33
 
45
34
  These test packages are flagged as malware and should be blocked by Safe Chain.
46
35
 
47
- **If the test package installs successfully instead of being blocked**, see [Malware Not Being Blocked](#malware-not-being-blocked) below.
36
+ **If the test package installs successfully instead of being blocked**, see Malware Not Being Blocked below.
48
37
 
49
- ### Logging Options
38
+ ## Logging Options
50
39
 
51
40
  Use logging flags or environment variables to get more information:
52
41
 
@@ -74,41 +63,39 @@ Safe-chain blocks malicious packages by intercepting network requests to package
74
63
 
75
64
  When a package is already cached locally, the package manager skips downloading it from the registry, which bypasses the proxy.
76
65
 
77
- **Resolution Steps:**
78
-
79
- 1. **Clear your package manager's cache:**
66
+ **Resolution Steps**
80
67
 
81
- ```bash
82
- # For npm
83
- npm cache clean --force
68
+ 1) Clear your package manager's cache
84
69
 
85
- # For pnpm
86
- pnpm store prune
70
+ ```bash
71
+ # For npm
72
+ npm cache clean --force
87
73
 
88
- # For yarn (classic)
89
- yarn cache clean
74
+ # For pnpm
75
+ pnpm store prune
90
76
 
91
- # For yarn (berry/v2+)
92
- yarn cache clean --all
77
+ # For yarn (classic)
78
+ yarn cache clean
93
79
 
94
- # For bun
95
- bun pm cache rm
96
- ```
80
+ # For yarn (berry/v2+)
81
+ yarn cache clean --all
97
82
 
98
- > **⚠️ Warning:** Cache clearing is safe but will remove all cached packages. Subsequent installations will need to re-download packages. In CI/CD environments or monorepos, this may affect build times.
83
+ # For bun
84
+ bun pm cache rm
85
+ ```
99
86
 
100
- 2. **Clean local installation artifacts:**
87
+ 2) Clean local installation artifacts:
101
88
 
102
- ```bash
103
- # Remove node_modules if you want a completely fresh install
104
- rm -rf node_modules
105
- ```
89
+ ```bash
90
+ # Remove node_modules if you want a completely fresh install
91
+ rm -rf node_modules
92
+ ```
106
93
 
107
- 3. **Re-test malware blocking:**
94
+ 3) Re-test malware blocking:
108
95
 
109
- ```bash
110
- npm install safe-chain-test # Should be blocked
111
- ```
96
+ ```bash
97
+ npm install safe-chain-test # Should be blocked
98
+ ```
112
99
 
113
100
  ### Shell Aliases Not Working After Installation
114
101
 
@@ -128,10 +115,10 @@ Should show: `npm is a function`
128
115
 
129
116
  Check that your startup file sources safe-chain scripts from `~/.safe-chain/scripts/`:
130
117
 
131
- - Bash: `~/.bashrc`
132
- - Zsh: `~/.zshrc`
133
- - Fish: `~/.config/fish/config.fish`
134
- - PowerShell: `$PROFILE`
118
+ * Bash: `~/.bashrc`
119
+ * Zsh: `~/.zshrc`
120
+ * Fish: `~/.config/fish/config.fish`
121
+ * PowerShell: `$PROFILE`
135
122
 
136
123
  ### "Command Not Found: safe-chain"
137
124
 
@@ -162,37 +149,39 @@ FullyQualifiedErrorId : UnauthorizedAccess
162
149
 
163
150
  **Cause:** Windows PowerShell's default execution policy (`Restricted`) blocks all script execution, including safe-chain's initialization script that's sourced from your PowerShell profile.
164
151
 
165
- **Resolution:**
152
+ **Resolution**
166
153
 
167
- 1. **Set the execution policy to allow local scripts:**
154
+ 1) Set the execution policy to allow local scripts
168
155
 
169
- Open PowerShell as Administrator and run:
156
+ Open PowerShell as Administrator and run:
170
157
 
171
- ```powershell
172
- Set-ExecutionPolicy -ExecutionPolicy RemoteSigned
173
- ```
158
+ ```powershell
159
+ Set-ExecutionPolicy -ExecutionPolicy RemoteSigned
160
+ ```
161
+
162
+ This allows:
174
163
 
175
- This allows:
176
- - Local scripts (like safe-chain's) to run without signing
177
- - Downloaded scripts to run only if signed by a trusted publisher
164
+ * Local scripts (like safe-chain's) to run without signing
165
+ * Downloaded scripts to run only if signed by a trusted publisher
178
166
 
179
- 2. **Restart PowerShell** and verify the error is resolved.
167
+ 2) Restart PowerShell and verify the error is resolved.
180
168
 
181
- > **Note:** `RemoteSigned` is Microsoft's recommended execution policy for client computers. It provides a good balance between security and usability.
169
+ > [!IMPORTANT]
170
+ > `RemoteSigned` is Microsoft's recommended execution policy for client computers. It provides a good balance between security and usability.
182
171
 
183
172
  ### Shell Aliases Persist After Uninstallation
184
173
 
185
174
  **Symptom:** safe-chain commands still active after running uninstall script
186
175
 
187
- **Steps:**
176
+ **Steps**
188
177
 
189
178
  1. Run `safe-chain teardown` (if binary still exists)
190
179
  2. Restart your terminal
191
180
  3. If still present, manually edit shell config files:
192
- - Bash: `~/.bashrc`
193
- - Zsh: `~/.zshrc`
194
- - Fish: `~/.config/fish/config.fish`
195
- - PowerShell: `$PROFILE`
181
+ * Bash: `~/.bashrc`
182
+ * Zsh: `~/.zshrc`
183
+ * Fish: `~/.config/fish/config.fish`
184
+ * PowerShell: `$PROFILE`
196
185
  4. Remove lines that source scripts from `~/.safe-chain/scripts/`
197
186
  5. Restart terminal again
198
187
 
@@ -217,10 +206,10 @@ type pip
217
206
 
218
207
  **Expected `which` output:**
219
208
 
220
- - Standalone binary (correct): `~/.safe-chain/bin/safe-chain` or `/Users/<username>/.safe-chain/bin/safe-chain`
221
- - npm global (outdated): path containing `node_modules` or nvm version paths
209
+ * Standalone binary (correct): `~/.safe-chain/bin/safe-chain` or `/Users/<username>/.safe-chain/bin/safe-chain`
210
+ * npm global (outdated): path containing `node_modules` or nvm version paths
222
211
 
223
- If `which` shows an npm installation, see [Check for Conflicting Installations](#check-for-conflicting-installations).
212
+ If `which` shows an npm installation, see Check for Conflicting Installations.
224
213
 
225
214
  ### Check Shell Integration
226
215
 
@@ -259,23 +248,23 @@ for version in $(nvm list | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+'); do
259
248
  done
260
249
  ```
261
250
 
262
- ## Manual Cleanup
251
+ ### Manual Cleanup
263
252
 
264
253
  > **Note:** The install and uninstall scripts automatically handle these cleanup steps. Use these manual commands only if automatic cleanup fails.
265
254
 
266
- ### Remove npm Global Installation
255
+ #### Remove npm Global Installation
267
256
 
268
257
  ```bash
269
258
  npm uninstall -g @aikidosec/safe-chain
270
259
  ```
271
260
 
272
- ### Remove Volta Installation
261
+ #### Remove Volta Installation
273
262
 
274
263
  ```bash
275
264
  volta uninstall @aikidosec/safe-chain
276
265
  ```
277
266
 
278
- ### Remove nvm Installations (All Versions)
267
+ #### Remove nvm Installations (All Versions)
279
268
 
280
269
  ```bash
281
270
  # Automated approach
@@ -288,34 +277,22 @@ nvm use <version>
288
277
  npm uninstall -g @aikidosec/safe-chain
289
278
  ```
290
279
 
291
- ### Clean Shell Configuration Files
280
+ #### Clean Shell Configuration Files
292
281
 
293
282
  Manually remove safe-chain entries from:
294
283
 
295
- - Bash: `~/.bashrc`
296
- - Zsh: `~/.zshrc`
297
- - Fish: `~/.config/fish/config.fish`
298
- - PowerShell: `$PROFILE`
284
+ * Bash: `~/.bashrc`
285
+ * Zsh: `~/.zshrc`
286
+ * Fish: `~/.config/fish/config.fish`
287
+ * PowerShell: `$PROFILE`
299
288
 
300
289
  Look for and remove:
301
290
 
302
- - Lines sourcing from `~/.safe-chain/scripts/`
303
- - Any safe-chain related function definitions
291
+ * Lines sourcing from `~/.safe-chain/scripts/`
292
+ * Any safe-chain related function definitions
304
293
 
305
- ### Remove Installation Directory
294
+ #### Remove Installation Directory
306
295
 
307
296
  ```bash
308
297
  rm -rf ~/.safe-chain
309
298
  ```
310
-
311
- ### Report Issues
312
-
313
- If you encounter problems:
314
-
315
- 1. Visit [GitHub Issues](https://github.com/AikidoSec/safe-chain/issues)
316
- 2. Include:
317
- - Operating system and version
318
- - Shell type and version
319
- - `safe-chain --version` output
320
- - Output from verification commands
321
- - Verbose logs of the failing command (add the `--safe-chain-logging=verbose` argument)
@@ -3112,7 +3112,7 @@
3112
3112
  },
3113
3113
  "packages/safe-chain": {
3114
3114
  "name": "@aikidosec/safe-chain",
3115
- "version": "1.5.2",
3115
+ "version": "1.5.3",
3116
3116
  "license": "AGPL-3.0-or-later",
3117
3117
  "dependencies": {
3118
3118
  "certifi": "14.5.15",
@@ -3137,6 +3137,8 @@
3137
3137
  "aikido-poetry": "bin/aikido-poetry.js",
3138
3138
  "aikido-python": "bin/aikido-python.js",
3139
3139
  "aikido-python3": "bin/aikido-python3.js",
3140
+ "aikido-rush": "bin/aikido-rush.js",
3141
+ "aikido-rushx": "bin/aikido-rushx.js",
3140
3142
  "aikido-uv": "bin/aikido-uv.js",
3141
3143
  "aikido-uvx": "bin/aikido-uvx.js",
3142
3144
  "aikido-yarn": "bin/aikido-yarn.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aikidosec/safe-chain",
3
- "version": "1.5.2",
3
+ "version": "1.5.3",
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'",
@@ -13,6 +13,8 @@
13
13
  "aikido-yarn": "bin/aikido-yarn.js",
14
14
  "aikido-pnpm": "bin/aikido-pnpm.js",
15
15
  "aikido-pnpx": "bin/aikido-pnpx.js",
16
+ "aikido-rush": "bin/aikido-rush.js",
17
+ "aikido-rushx": "bin/aikido-rushx.js",
16
18
  "aikido-bun": "bin/aikido-bun.js",
17
19
  "aikido-bunx": "bin/aikido-bunx.js",
18
20
  "aikido-uv": "bin/aikido-uv.js",
@@ -37,7 +39,7 @@
37
39
  "keywords": [],
38
40
  "author": "Aikido Security",
39
41
  "license": "AGPL-3.0-or-later",
40
- "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/), [pnpx](https://pnpm.io/cli/dlx), [bun](https://bun.sh/), [bunx](https://bun.sh/docs/cli/bunx), [uv](https://docs.astral.sh/uv/) (Python), and [pip](https://pip.pypa.io/) 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, pnpx, bun, bunx, uv, uvx, or pip/pip3 from downloading or running the malware.",
42
+ "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/), [pnpx](https://pnpm.io/cli/dlx), [rush](https://rushjs.io/), [rushx](https://rushjs.io/pages/commands/rushx/), [bun](https://bun.sh/), [bunx](https://bun.sh/docs/cli/bunx), [uv](https://docs.astral.sh/uv/) (Python), and [pip](https://pip.pypa.io/) 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, pnpx, rush, rushx, bun, bunx, uv, uvx, or pip/pip3 from downloading or running the malware.",
41
43
  "dependencies": {
42
44
  "certifi": "14.5.15",
43
45
  "chalk": "5.4.1",
@@ -13,6 +13,8 @@ import { createPipPackageManager } from "./pip/createPackageManager.js";
13
13
  import { createUvPackageManager } from "./uv/createUvPackageManager.js";
14
14
  import { createPoetryPackageManager } from "./poetry/createPoetryPackageManager.js";
15
15
  import { createPipXPackageManager } from "./pipx/createPipXPackageManager.js";
16
+ import { createRushPackageManager } from "./rush/createRushPackageManager.js";
17
+ import { createRushxPackageManager } from "./rushx/createRushxPackageManager.js";
16
18
  import { createUvxPackageManager } from "./uvx/createUvxPackageManager.js";
17
19
 
18
20
  /**
@@ -67,6 +69,10 @@ export function initializePackageManager(packageManagerName, context) {
67
69
  state.packageManagerName = createPoetryPackageManager();
68
70
  } else if (packageManagerName === "pipx") {
69
71
  state.packageManagerName = createPipXPackageManager();
72
+ } else if (packageManagerName === "rush") {
73
+ state.packageManagerName = createRushPackageManager();
74
+ } else if (packageManagerName === "rushx") {
75
+ state.packageManagerName = createRushxPackageManager();
70
76
  } else {
71
77
  throw new Error("Unsupported package manager: " + packageManagerName);
72
78
  }
@@ -0,0 +1,64 @@
1
+ import { runRushCommand } from "./runRushCommand.js";
2
+ import { resolvePackageVersion } from "../../api/npmApi.js";
3
+ import { parsePackagesFromRushAddArgs } from "./parsing/parsePackagesFromRushAddArgs.js";
4
+
5
+ /**
6
+ * @returns {import("../currentPackageManager.js").PackageManager}
7
+ */
8
+ export function createRushPackageManager() {
9
+ return {
10
+ runCommand: (args) => runRushCommand("rush", args),
11
+ // We pre-scan rush add commands and rely on MITM for install/update flows.
12
+ isSupportedCommand: (args) => getRushCommand(args) === "add",
13
+ getDependencyUpdatesForCommand: scanRushAddCommand,
14
+ };
15
+ }
16
+
17
+ /**
18
+ * @param {string[]} args
19
+ * @returns {Promise<import("../currentPackageManager.js").GetDependencyUpdatesResult[]>}
20
+ */
21
+ async function scanRushAddCommand(args) {
22
+ if (getRushCommand(args) !== "add") {
23
+ return [];
24
+ }
25
+
26
+ const parsedSpecs = parsePackagesFromRushAddArgs(args.slice(1));
27
+
28
+ const resolvedVersions = await Promise.all(
29
+ parsedSpecs.map(async (parsed) => {
30
+ const exactVersion = await resolvePackageVersion(parsed.name, parsed.version);
31
+ return {
32
+ parsed,
33
+ exactVersion,
34
+ };
35
+ }),
36
+ );
37
+
38
+ const changes = [];
39
+ for (const resolved of resolvedVersions) {
40
+ if (!resolved.exactVersion) {
41
+ continue;
42
+ }
43
+
44
+ changes.push({
45
+ name: resolved.parsed.name,
46
+ version: resolved.exactVersion,
47
+ type: "add",
48
+ });
49
+ }
50
+
51
+ return changes;
52
+ }
53
+
54
+ /**
55
+ * @param {string[]} args
56
+ * @returns {string | undefined}
57
+ */
58
+ function getRushCommand(args) {
59
+ if (!args || args.length === 0) {
60
+ return undefined;
61
+ }
62
+
63
+ return args[0]?.toLowerCase();
64
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * @param {string[]} args
3
+ * @returns {{name: string, version: string | null}[]}
4
+ */
5
+ export function parsePackagesFromRushAddArgs(args) {
6
+ const packageSpecs = [];
7
+
8
+ for (let i = 0; i < args.length; i++) {
9
+ const arg = args[i];
10
+ if (!arg) {
11
+ continue;
12
+ }
13
+
14
+ if (arg === "--package" || arg === "-p") {
15
+ const next = args[i + 1];
16
+ if (next && !next.startsWith("-")) {
17
+ packageSpecs.push(next);
18
+ i += 1;
19
+ }
20
+ continue;
21
+ }
22
+
23
+ if (arg.startsWith("--package=")) {
24
+ const value = arg.slice("--package=".length);
25
+ if (value) {
26
+ packageSpecs.push(value);
27
+ }
28
+ }
29
+ }
30
+
31
+ return packageSpecs
32
+ .map((spec) => parsePackageSpec(spec))
33
+ .filter((spec) => spec !== null);
34
+ }
35
+
36
+ /**
37
+ * @param {string} spec
38
+ * @returns {{name: string, version: string | null} | null}
39
+ */
40
+ function parsePackageSpec(spec) {
41
+ const value = removeAlias(spec.trim());
42
+ if (!value) {
43
+ return null;
44
+ }
45
+
46
+ const lastAtIndex = value.lastIndexOf("@");
47
+ if (lastAtIndex > 0) {
48
+ return {
49
+ name: value.slice(0, lastAtIndex),
50
+ version: value.slice(lastAtIndex + 1),
51
+ };
52
+ }
53
+
54
+ return {
55
+ name: value,
56
+ version: null,
57
+ };
58
+ }
59
+
60
+ /**
61
+ * @param {string} spec
62
+ * @returns {string}
63
+ */
64
+ function removeAlias(spec) {
65
+ const aliasIndex = spec.indexOf("@npm:");
66
+ if (aliasIndex !== -1) {
67
+ return spec.slice(aliasIndex + 5);
68
+ }
69
+
70
+ return spec;
71
+ }
@@ -0,0 +1,21 @@
1
+ import { mergeSafeChainProxyEnvironmentVariables } from "../../registryProxy/registryProxy.js";
2
+ import { safeSpawn } from "../../utils/safeSpawn.js";
3
+ import { reportCommandExecutionFailure } from "../_shared/commandErrors.js";
4
+
5
+ /**
6
+ * @param {"rush" | "rushx"} executableName
7
+ * @param {string[]} args
8
+ * @returns {Promise<{status: number}>}
9
+ */
10
+ export async function runRushCommand(executableName, args) {
11
+ try {
12
+ const result = await safeSpawn(executableName, args, {
13
+ stdio: "inherit",
14
+ env: mergeSafeChainProxyEnvironmentVariables(process.env),
15
+ });
16
+
17
+ return { status: result.status };
18
+ } catch (/** @type any */ error) {
19
+ return reportCommandExecutionFailure(error, executableName);
20
+ }
21
+ }
@@ -0,0 +1,18 @@
1
+ import { runRushCommand } from "../rush/runRushCommand.js";
2
+
3
+ /**
4
+ * @returns {import("../currentPackageManager.js").PackageManager}
5
+ */
6
+ export function createRushxPackageManager() {
7
+ return {
8
+ /**
9
+ * @param {string[]} args
10
+ */
11
+ runCommand: (args) => {
12
+ return runRushCommand("rushx", args);
13
+ },
14
+ // For rushx, rely solely on MITM.
15
+ isSupportedCommand: () => false,
16
+ getDependencyUpdatesForCommand: () => [],
17
+ };
18
+ }
@@ -48,6 +48,18 @@ export const knownAikidoTools = [
48
48
  ecoSystem: ECOSYSTEM_JS,
49
49
  internalPackageManagerName: "pnpx",
50
50
  },
51
+ {
52
+ tool: "rush",
53
+ aikidoCommand: "aikido-rush",
54
+ ecoSystem: ECOSYSTEM_JS,
55
+ internalPackageManagerName: "rush",
56
+ },
57
+ {
58
+ tool: "rushx",
59
+ aikidoCommand: "aikido-rushx",
60
+ ecoSystem: ECOSYSTEM_JS,
61
+ internalPackageManagerName: "rushx",
62
+ },
51
63
  {
52
64
  tool: "bun",
53
65
  aikidoCommand: "aikido-bun",
@@ -19,6 +19,14 @@ function pnpx
19
19
  wrapSafeChainCommand "pnpx" $argv
20
20
  end
21
21
 
22
+ function rush
23
+ wrapSafeChainCommand "rush" $argv
24
+ end
25
+
26
+ function rushx
27
+ wrapSafeChainCommand "rushx" $argv
28
+ end
29
+
22
30
  function bun
23
31
  wrapSafeChainCommand "bun" $argv
24
32
  end
@@ -28,6 +28,14 @@ function pnpx() {
28
28
  wrapSafeChainCommand "pnpx" "$@"
29
29
  }
30
30
 
31
+ function rush() {
32
+ wrapSafeChainCommand "rush" "$@"
33
+ }
34
+
35
+ function rushx() {
36
+ wrapSafeChainCommand "rushx" "$@"
37
+ }
38
+
31
39
  function bun() {
32
40
  wrapSafeChainCommand "bun" "$@"
33
41
  }
@@ -22,6 +22,14 @@ function pnpx {
22
22
  Invoke-WrappedCommand "pnpx" $args $MyInvocation.Line $MyInvocation.OffsetInLine
23
23
  }
24
24
 
25
+ function rush {
26
+ Invoke-WrappedCommand "rush" $args $MyInvocation.Line $MyInvocation.OffsetInLine
27
+ }
28
+
29
+ function rushx {
30
+ Invoke-WrappedCommand "rushx" $args $MyInvocation.Line $MyInvocation.OffsetInLine
31
+ }
32
+
25
33
  function bun {
26
34
  Invoke-WrappedCommand "bun" $args $MyInvocation.Line $MyInvocation.OffsetInLine
27
35
  }