Skip to content

Commit

Permalink
Merge pull request #155 from AikidoSec/shell_inj_add_sinks
Browse files Browse the repository at this point in the history
Add missing sinks spawn and spawnSync for shell injection
  • Loading branch information
willem-delbare authored Apr 11, 2024
2 parents 2f3d3e0 + ef882c8 commit 1e4edd6
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 2 deletions.
64 changes: 63 additions & 1 deletion library/sinks/ChildProcess.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ t.test("it works", async (t) => {

agent.start([new ChildProcess()]);

const { exec, execSync } = require("child_process");
const { exec, execSync, spawn, spawnSync } = require("child_process");

const runCommandsWithInvalidArgs = () => {
throws(
Expand All @@ -50,6 +50,16 @@ t.test("it works", async (t) => {
() => execSync(),
/argument must be of type string. Received undefined/
);

throws(
() => spawn().unref(),
/argument must be of type string. Received undefined/
);

throws(
() => spawnSync(),
/argument must be of type string. Received undefined/
);
};

runCommandsWithInvalidArgs();
Expand All @@ -61,6 +71,12 @@ t.test("it works", async (t) => {
const runSafeCommands = () => {
exec("ls", (err, stdout, stderr) => {}).unref();
execSync("ls", (err, stdout, stderr) => {});

spawn("ls", ["-la"], {}, (err, stdout, stderr) => {}).unref();
spawnSync("ls", ["-la"], {}, (err, stdout, stderr) => {});

spawn("ls", ["-la"], { shell: false }, (err, stdout, stderr) => {}).unref();
spawnSync("ls", ["-la"], { shell: false }, (err, stdout, stderr) => {});
};

runSafeCommands();
Expand All @@ -80,4 +96,50 @@ t.test("it works", async (t) => {
"Aikido runtime has blocked a Shell injection: child_process.execSync(...) originating from body.file.matches"
);
});

runWithContext(unsafeContext, () => {
throws(
() =>
spawn(
"ls `echo .`",
[],
{ shell: true },
(err, stdout, stderr) => {}
).unref(),
"Aikido runtime has blocked a Shell injection: child_process.spawn(...) originating from body.file.matches"
);

throws(
() =>
spawn(
"ls `echo .`",
[],
{ shell: "/bin/sh" },
(err, stdout, stderr) => {}
).unref(),
"Aikido runtime has blocked a Shell injection: child_process.spawn(...) originating from body.file.matches"
);

throws(
() =>
spawnSync(
"ls `echo .`",
[],
{ shell: true },
(err, stdout, stderr) => {}
),
"Aikido runtime has blocked a Shell injection: child_process.spawnSync(...) originating from body.file.matches"
);

throws(
() =>
spawnSync(
"ls `echo .`",
[],
{ shell: "/bin/sh" },
(err, stdout, stderr) => {}
),
"Aikido runtime has blocked a Shell injection: child_process.spawnSync(...) originating from body.file.matches"
);
});
});
19 changes: 18 additions & 1 deletion library/sinks/ChildProcess.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { getContext } from "../agent/Context";
import { Hooks } from "../agent/hooks/Hooks";
import { InterceptorResult } from "../agent/hooks/MethodInterceptor";
import { Wrapper } from "../agent/Wrapper";
import { isPlainObject } from "../helpers/isPlainObject";
import { checkContextForShellInjection } from "../vulnerabilities/shell-injection/checkContextForShellInjection";

export class ChildProcess implements Wrapper {
Expand All @@ -12,6 +13,20 @@ export class ChildProcess implements Wrapper {
return undefined;
}

// Ignore calls to spawn or spawnSync if shell option is not enabled
if (name === "spawn" || name === "spawnSync") {
const unsafeShellOption = args.find(
(arg) =>
isPlainObject(arg) &&
"shell" in arg &&
(arg.shell === true || typeof arg.shell === "string")
);

if (!unsafeShellOption) {
return undefined;
}
}

if (args.length > 0 && typeof args[0] === "string") {
const command = args[0];

Expand All @@ -31,6 +46,8 @@ export class ChildProcess implements Wrapper {
childProcess
.addSubject((exports) => exports)
.inspect("exec", (args) => this.inspectExec(args, "exec"))
.inspect("execSync", (args) => this.inspectExec(args, "execSync"));
.inspect("execSync", (args) => this.inspectExec(args, "execSync"))
.inspect("spawn", (args) => this.inspectExec(args, "spawn"))
.inspect("spawnSync", (args) => this.inspectExec(args, "spawnSync"));
}
}

0 comments on commit 1e4edd6

Please sign in to comment.