mirror of
https://github.com/AikidoSec/safe-chain.git
synced 2026-05-26 12:10:49 +00:00
Add rushx support too
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
parent
5cf2ffe201
commit
98a1ba7d10
16 changed files with 101 additions and 27 deletions
14
packages/safe-chain/bin/aikido-rushx.js
Executable file
14
packages/safe-chain/bin/aikido-rushx.js
Executable file
|
|
@ -0,0 +1,14 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
import { main } from "../src/main.js";
|
||||
import { initializePackageManager } from "../src/packagemanager/currentPackageManager.js";
|
||||
import { setEcoSystem, ECOSYSTEM_JS } from "../src/config/settings.js";
|
||||
|
||||
setEcoSystem(ECOSYSTEM_JS);
|
||||
const packageManagerName = "rushx";
|
||||
initializePackageManager(packageManagerName);
|
||||
|
||||
(async () => {
|
||||
var exitCode = await main(process.argv.slice(2));
|
||||
process.exit(exitCode);
|
||||
})();
|
||||
|
|
@ -108,7 +108,7 @@ function writeHelp() {
|
|||
ui.writeInformation(
|
||||
`- ${chalk.cyan(
|
||||
"safe-chain setup",
|
||||
)}: This will setup your shell to wrap safe-chain around npm, npx, yarn, pnpm, pnpx, rush, bun, bunx, pip and pip3.`,
|
||||
)}: This will setup your shell to wrap safe-chain around npm, npx, yarn, pnpm, pnpx, rush, rushx, bun, bunx, pip and pip3.`,
|
||||
);
|
||||
ui.writeInformation(
|
||||
`- ${chalk.cyan(
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
"aikido-pnpm": "bin/aikido-pnpm.js",
|
||||
"aikido-pnpx": "bin/aikido-pnpx.js",
|
||||
"aikido-rush": "bin/aikido-rush.js",
|
||||
"aikido-rushx": "bin/aikido-rushx.js",
|
||||
"aikido-bun": "bin/aikido-bun.js",
|
||||
"aikido-bunx": "bin/aikido-bunx.js",
|
||||
"aikido-uv": "bin/aikido-uv.js",
|
||||
|
|
@ -38,7 +39,7 @@
|
|||
"keywords": [],
|
||||
"author": "Aikido Security",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"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/), [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, bun, bunx, uv, uvx, or pip/pip3 from downloading or running the malware.",
|
||||
"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.",
|
||||
"dependencies": {
|
||||
"certifi": "14.5.15",
|
||||
"chalk": "5.4.1",
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import { createUvPackageManager } from "./uv/createUvPackageManager.js";
|
|||
import { createPoetryPackageManager } from "./poetry/createPoetryPackageManager.js";
|
||||
import { createPipXPackageManager } from "./pipx/createPipXPackageManager.js";
|
||||
import { createRushPackageManager } from "./rush/createRushPackageManager.js";
|
||||
import { createRushxPackageManager } from "./rushx/createRushxPackageManager.js";
|
||||
import { createUvxPackageManager } from "./uvx/createUvxPackageManager.js";
|
||||
|
||||
/**
|
||||
|
|
@ -70,6 +71,8 @@ export function initializePackageManager(packageManagerName, context) {
|
|||
state.packageManagerName = createPipXPackageManager();
|
||||
} else if (packageManagerName === "rush") {
|
||||
state.packageManagerName = createRushPackageManager();
|
||||
} else if (packageManagerName === "rushx") {
|
||||
state.packageManagerName = createRushxPackageManager();
|
||||
} else {
|
||||
throw new Error("Unsupported package manager: " + packageManagerName);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { resolvePackageVersion } from "../../api/npmApi.js";
|
|||
*/
|
||||
export function createRushPackageManager() {
|
||||
return {
|
||||
runCommand: runRushCommand,
|
||||
runCommand: (args) => runRushCommand("rush", args),
|
||||
// We pre-scan rush add commands and rely on MITM for install/update flows.
|
||||
isSupportedCommand: (args) => getRushCommand(args) === "add",
|
||||
getDependencyUpdatesForCommand: scanRushAddCommand,
|
||||
|
|
|
|||
|
|
@ -3,23 +3,24 @@ import { safeSpawn } from "../../utils/safeSpawn.js";
|
|||
import { reportCommandExecutionFailure } from "../_shared/commandErrors.js";
|
||||
|
||||
/**
|
||||
* @param {"rush" | "rushx"} executableName
|
||||
* @param {string[]} args
|
||||
* @returns {Promise<{status: number}>}
|
||||
*/
|
||||
export async function runRushCommand(args) {
|
||||
export async function runRushCommand(executableName, args) {
|
||||
try {
|
||||
const env = normalizeProxyEnvironmentVariables(
|
||||
mergeSafeChainProxyEnvironmentVariables(process.env),
|
||||
);
|
||||
|
||||
const result = await safeSpawn("rush", args, {
|
||||
const result = await safeSpawn(executableName, args, {
|
||||
stdio: "inherit",
|
||||
env,
|
||||
});
|
||||
|
||||
return { status: result.status };
|
||||
} catch (/** @type any */ error) {
|
||||
return reportCommandExecutionFailure(error, "rush");
|
||||
return reportCommandExecutionFailure(error, executableName);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ describe("runRushCommand", () => {
|
|||
});
|
||||
|
||||
it("spawns rush with merged proxy env", async () => {
|
||||
const res = await runRushCommand(["install"]);
|
||||
const res = await runRushCommand("rush", ["install"]);
|
||||
|
||||
assert.strictEqual(res.status, 0);
|
||||
assert.strictEqual(safeSpawnMock.mock.calls.length, 1);
|
||||
|
|
@ -88,7 +88,7 @@ describe("runRushCommand", () => {
|
|||
it("returns spawn result status", async () => {
|
||||
nextSpawnStatus = 7;
|
||||
|
||||
const res = await runRushCommand(["update"]);
|
||||
const res = await runRushCommand("rush", ["update"]);
|
||||
|
||||
assert.strictEqual(res.status, 7);
|
||||
});
|
||||
|
|
@ -98,7 +98,7 @@ describe("runRushCommand", () => {
|
|||
code: "ENOENT",
|
||||
});
|
||||
|
||||
const res = await runRushCommand(["install"]);
|
||||
const res = await runRushCommand("rush", ["install"]);
|
||||
|
||||
assert.strictEqual(res.status, 1);
|
||||
});
|
||||
|
|
@ -108,7 +108,7 @@ describe("runRushCommand", () => {
|
|||
HTTPS_PROXY: "http://localhost:8080",
|
||||
};
|
||||
|
||||
await runRushCommand(["install"]);
|
||||
await runRushCommand("rush", ["install"]);
|
||||
|
||||
assert.deepStrictEqual(mergeResultEnv, {
|
||||
HTTPS_PROXY: "http://localhost:8080",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
import { runRushCommand } from "../rush/runRushCommand.js";
|
||||
|
||||
/**
|
||||
* @returns {import("../currentPackageManager.js").PackageManager}
|
||||
*/
|
||||
export function createRushxPackageManager() {
|
||||
return {
|
||||
/**
|
||||
* @param {string[]} args
|
||||
*/
|
||||
runCommand: (args) => {
|
||||
return runRushCommand("rushx", args);
|
||||
},
|
||||
// For rushx, rely solely on MITM.
|
||||
isSupportedCommand: () => false,
|
||||
getDependencyUpdatesForCommand: () => [],
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
import { test } from "node:test";
|
||||
import assert from "node:assert";
|
||||
import { createRushxPackageManager } from "./createRushxPackageManager.js";
|
||||
|
||||
test("createRushxPackageManager returns valid package manager interface", () => {
|
||||
const pm = createRushxPackageManager();
|
||||
|
||||
assert.ok(pm);
|
||||
assert.strictEqual(typeof pm.runCommand, "function");
|
||||
assert.strictEqual(typeof pm.isSupportedCommand, "function");
|
||||
assert.strictEqual(typeof pm.getDependencyUpdatesForCommand, "function");
|
||||
assert.strictEqual(pm.isSupportedCommand(), false);
|
||||
assert.deepStrictEqual(pm.getDependencyUpdatesForCommand(), []);
|
||||
});
|
||||
|
|
@ -54,6 +54,12 @@ export const knownAikidoTools = [
|
|||
ecoSystem: ECOSYSTEM_JS,
|
||||
internalPackageManagerName: "rush",
|
||||
},
|
||||
{
|
||||
tool: "rushx",
|
||||
aikidoCommand: "aikido-rushx",
|
||||
ecoSystem: ECOSYSTEM_JS,
|
||||
internalPackageManagerName: "rushx",
|
||||
},
|
||||
{
|
||||
tool: "bun",
|
||||
aikidoCommand: "aikido-bun",
|
||||
|
|
|
|||
|
|
@ -48,9 +48,8 @@ describe("Setup CI shell integration", () => {
|
|||
knownAikidoTools: [
|
||||
{ tool: "npm", aikidoCommand: "aikido-npm" },
|
||||
{ tool: "yarn", aikidoCommand: "aikido-yarn" },
|
||||
{ tool: "rush", aikidoCommand: "aikido-rush" },
|
||||
],
|
||||
getPackageManagerList: () => "npm, yarn, rush",
|
||||
getPackageManagerList: () => "npm, yarn",
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -108,10 +107,6 @@ describe("Setup CI shell integration", () => {
|
|||
const yarnShimPath = path.join(mockShimsDir, "yarn");
|
||||
assert.ok(fs.existsSync(yarnShimPath), "yarn shim should exist");
|
||||
|
||||
// Check if rush shim was created
|
||||
const rushShimPath = path.join(mockShimsDir, "rush");
|
||||
assert.ok(fs.existsSync(rushShimPath), "rush shim should exist");
|
||||
|
||||
// Check content of npm shim
|
||||
const npmShimContent = fs.readFileSync(npmShimPath, "utf-8");
|
||||
assert.ok(npmShimContent.includes("aikido-npm"), "npm shim should contain aikido-npm");
|
||||
|
|
@ -138,9 +133,6 @@ describe("Setup CI shell integration", () => {
|
|||
const yarnShimPath = path.join(mockShimsDir, "yarn.cmd");
|
||||
assert.ok(fs.existsSync(yarnShimPath), "yarn.cmd shim should exist");
|
||||
|
||||
const rushShimPath = path.join(mockShimsDir, "rush.cmd");
|
||||
assert.ok(fs.existsSync(rushShimPath), "rush.cmd shim should exist");
|
||||
|
||||
// Check content of npm.cmd shim
|
||||
const npmShimContent = fs.readFileSync(npmShimPath, "utf-8");
|
||||
assert.ok(npmShimContent.includes("aikido-npm"), "npm.cmd should contain aikido-npm");
|
||||
|
|
|
|||
|
|
@ -19,6 +19,14 @@ function pnpx
|
|||
wrapSafeChainCommand "pnpx" $argv
|
||||
end
|
||||
|
||||
function rush
|
||||
wrapSafeChainCommand "rush" $argv
|
||||
end
|
||||
|
||||
function rushx
|
||||
wrapSafeChainCommand "rushx" $argv
|
||||
end
|
||||
|
||||
function bun
|
||||
wrapSafeChainCommand "bun" $argv
|
||||
end
|
||||
|
|
|
|||
|
|
@ -28,6 +28,14 @@ function pnpx() {
|
|||
wrapSafeChainCommand "pnpx" "$@"
|
||||
}
|
||||
|
||||
function rush() {
|
||||
wrapSafeChainCommand "rush" "$@"
|
||||
}
|
||||
|
||||
function rushx() {
|
||||
wrapSafeChainCommand "rushx" "$@"
|
||||
}
|
||||
|
||||
function bun() {
|
||||
wrapSafeChainCommand "bun" "$@"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,14 @@ function pnpx {
|
|||
Invoke-WrappedCommand "pnpx" $args $MyInvocation.Line $MyInvocation.OffsetInLine
|
||||
}
|
||||
|
||||
function rush {
|
||||
Invoke-WrappedCommand "rush" $args $MyInvocation.Line $MyInvocation.OffsetInLine
|
||||
}
|
||||
|
||||
function rushx {
|
||||
Invoke-WrappedCommand "rushx" $args $MyInvocation.Line $MyInvocation.OffsetInLine
|
||||
}
|
||||
|
||||
function bun {
|
||||
Invoke-WrappedCommand "bun" $args $MyInvocation.Line $MyInvocation.OffsetInLine
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue