mirror of
https://github.com/AikidoSec/safe-chain.git
synced 2026-05-26 12:10:49 +00:00
Test if command is safe to execute
This commit is contained in:
parent
7a55be49f4
commit
f5f3b91b40
2 changed files with 37 additions and 1 deletions
|
|
@ -27,6 +27,10 @@ function escapeDoubleQuoteContent(arg) {
|
|||
}
|
||||
|
||||
function buildCommand(command, args) {
|
||||
if (args.length === 0) {
|
||||
return command;
|
||||
}
|
||||
|
||||
const escapedArgs = args.map(sanitizeShellArgument);
|
||||
|
||||
return `${command} ${escapedArgs.join(" ")}`;
|
||||
|
|
@ -48,6 +52,11 @@ function resolveCommandPath(command) {
|
|||
}
|
||||
|
||||
export async function safeSpawn(command, args, options = {}) {
|
||||
// command should always be alphanumeric or _ or - to avoid injection
|
||||
if (!/^[a-zA-Z0-9_-]+$/.test(command)) {
|
||||
throw new Error(`Invalid command name: ${command}`);
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
// Windows requires shell: true because .bat and .cmd files are not executable
|
||||
// without a terminal. On Unix/macOS, we resolve the full path first, then use
|
||||
|
|
|
|||
|
|
@ -4,9 +4,11 @@ import assert from "node:assert";
|
|||
describe("safeSpawn", () => {
|
||||
let safeSpawn;
|
||||
let spawnCalls = [];
|
||||
let os;
|
||||
|
||||
beforeEach(async () => {
|
||||
spawnCalls = [];
|
||||
os = "win32"; // Test Windows behavior by default
|
||||
|
||||
// Mock child_process module to capture what command string gets built
|
||||
mock.module("child_process", {
|
||||
|
|
@ -35,7 +37,7 @@ describe("safeSpawn", () => {
|
|||
|
||||
mock.module("os", {
|
||||
namedExports: {
|
||||
platform: () => "win32",
|
||||
platform: () => os,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -184,4 +186,29 @@ describe("safeSpawn", () => {
|
|||
'cmd safe "needs space" "\\$dangerous" also-safe'
|
||||
);
|
||||
});
|
||||
|
||||
it("should reject command names with special characters", async () => {
|
||||
await assert.rejects(async () => await safeSpawn("npm; echo hacked", []), {
|
||||
message: "Invalid command name: npm; echo hacked",
|
||||
});
|
||||
});
|
||||
|
||||
it("should reject command names with spaces", async () => {
|
||||
await assert.rejects(async () => await safeSpawn("npm install", []), {
|
||||
message: "Invalid command name: npm install",
|
||||
});
|
||||
});
|
||||
|
||||
it("should reject command names with slashes", async () => {
|
||||
await assert.rejects(async () => await safeSpawn("../../malicious", []), {
|
||||
message: "Invalid command name: ../../malicious",
|
||||
});
|
||||
});
|
||||
|
||||
it("should accept valid command names with letters, numbers, underscores and hyphens", async () => {
|
||||
await safeSpawn("valid_command-123", []);
|
||||
|
||||
assert.strictEqual(spawnCalls.length, 1);
|
||||
assert.strictEqual(spawnCalls[0].command, "valid_command-123");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue