Skip to content

Commit e8233f1

Browse files
authored
Merge pull request #635 from AikidoSec/child-process-exec
Do not wrap exec as it internally uses execFile
2 parents f69ef07 + c456265 commit e8233f1

File tree

4 files changed

+67
-10
lines changed

4 files changed

+67
-10
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import * as t from "tap";
2+
import { Context, runWithContext } from "../agent/Context";
3+
import { ChildProcess } from "./ChildProcess";
4+
import { execFile, execFileSync } from "child_process";
5+
import { createTestAgent } from "../helpers/createTestAgent";
6+
import { ReportingAPIForTesting } from "../agent/api/ReportingAPIForTesting";
7+
import * as FakeTimers from "@sinonjs/fake-timers";
8+
import { Token } from "../agent/api/Token";
9+
10+
const unsafeContext: Context = {
11+
remoteAddress: "::1",
12+
method: "POST",
13+
url: "http://localhost:4000",
14+
query: {},
15+
headers: {},
16+
body: {
17+
file: {
18+
matches: "`echo .`",
19+
},
20+
},
21+
cookies: {},
22+
routeParams: {},
23+
source: "express",
24+
route: "/posts/:id",
25+
};
26+
27+
t.test("it does not report the attack twice", async (t) => {
28+
const clock = FakeTimers.install();
29+
30+
const api = new ReportingAPIForTesting();
31+
const agent = createTestAgent({
32+
block: false,
33+
api: api,
34+
token: new Token("test"),
35+
});
36+
37+
agent.start([new ChildProcess()]);
38+
39+
const { exec } = require("child_process") as typeof import("child_process");
40+
41+
runWithContext(unsafeContext, () => {
42+
exec("ls `echo .`", (err, stdout, stderr) => {}).unref();
43+
});
44+
45+
clock.tick(60 * 1000);
46+
47+
const attacks = api.getEvents().filter((e) => e.type === "detected_attack");
48+
49+
t.match(attacks, [
50+
{
51+
type: "detected_attack",
52+
attack: {
53+
module: "child_process",
54+
operation: "child_process.execFile",
55+
},
56+
},
57+
]);
58+
59+
t.same(attacks.length, 1);
60+
61+
clock.uninstall();
62+
});

library/sinks/ChildProcess.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ t.test("it works", async (t) => {
9595
runWithContext(unsafeContext, () => {
9696
throws(
9797
() => exec("ls `echo .`", (err, stdout, stderr) => {}).unref(),
98-
"Zen has blocked a shell injection: child_process.exec(...) originating from body.file.matches"
98+
"Zen has blocked a shell injection: child_process.execFile(...) originating from body.file.matches"
9999
);
100100

101101
throws(

library/sinks/ChildProcess.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,6 @@ const PATH_PREFIXES = [
1919
export class ChildProcess implements Wrapper {
2020
wrap(hooks: Hooks) {
2121
hooks.addBuiltinModule("child_process").onRequire((exports, pkgInfo) => {
22-
wrapExport(exports, "exec", pkgInfo, {
23-
kind: "exec_op",
24-
inspectArgs: (args) => {
25-
return this.inspectExec(args, "exec");
26-
},
27-
});
2822
wrapExport(exports, "execSync", pkgInfo, {
2923
kind: "exec_op",
3024
inspectArgs: (args) => {
@@ -44,6 +38,7 @@ export class ChildProcess implements Wrapper {
4438
},
4539
});
4640
wrapExport(exports, "execFile", pkgInfo, {
41+
// Also protects exec, see https://github.com/nodejs/node/blob/main/lib/child_process.js
4742
kind: "exec_op",
4843
inspectArgs: (args) => {
4944
return this.inspectExecFile(args, "execFile");

library/sinks/Shelljs.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ t.test("it detects async shell injections", async (t) => {
106106
if (error instanceof Error) {
107107
t.same(
108108
error.message,
109-
"Zen has blocked a shell injection: child_process.exec(...) originating from body.myTitle"
109+
"Zen has blocked a shell injection: child_process.execFile(...) originating from body.myTitle"
110110
);
111111
}
112112

@@ -120,7 +120,7 @@ t.test("it detects async shell injections", async (t) => {
120120
if (error2 instanceof Error) {
121121
t.same(
122122
error2.message,
123-
"Zen has blocked a shell injection: child_process.exec(...) originating from body.myTitle"
123+
"Zen has blocked a shell injection: child_process.execFile(...) originating from body.myTitle"
124124
);
125125
}
126126

@@ -134,7 +134,7 @@ t.test("it detects async shell injections", async (t) => {
134134
if (error3 instanceof Error) {
135135
t.same(
136136
error3.message,
137-
"Zen has blocked a shell injection: child_process.exec(...) originating from body.myTitle"
137+
"Zen has blocked a shell injection: child_process.execFile(...) originating from body.myTitle"
138138
);
139139
}
140140
});

0 commit comments

Comments
 (0)