Skip to content

Commit 80a32ae

Browse files
author
Esben Sparre Andreasen
committed
JS: add SystemCommandExecution::isShellInterpreted
1 parent 9995c12 commit 80a32ae

File tree

4 files changed

+59
-24
lines changed

4 files changed

+59
-24
lines changed

javascript/ql/src/semmle/javascript/Concepts.qll

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ abstract class SystemCommandExecution extends DataFlow::Node {
1414
/** Gets an argument to this execution that specifies the command. */
1515
abstract DataFlow::Node getACommandArgument();
1616

17+
/** Holds if a shell interprets `arg`. */
18+
predicate isShellInterpreted(DataFlow::Node arg) { none() }
19+
1720
/**
1821
* Gets an argument to this command execution that specifies the argument list
1922
* to the command.

javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -573,21 +573,32 @@ module NodeJSLib {
573573
this = DataFlow::moduleMember("child_process", methodName).getACall()
574574
}
575575

576-
override DataFlow::Node getACommandArgument() {
576+
private DataFlow::Node getACommandArgument(boolean shell) {
577577
// check whether this is an invocation of an exec/spawn/fork method
578578
(
579-
methodName = "exec" or
580-
methodName = "execSync" or
581-
methodName = "execFile" or
582-
methodName = "execFileSync" or
583-
methodName = "spawn" or
584-
methodName = "spawnSync" or
585-
methodName = "fork"
579+
shell = true and
580+
(
581+
methodName = "exec" or
582+
methodName = "execSync"
583+
)
584+
or
585+
shell = false and
586+
(
587+
methodName = "execFile" or
588+
methodName = "execFileSync" or
589+
methodName = "spawn" or
590+
methodName = "spawnSync" or
591+
methodName = "fork"
592+
)
586593
) and
587594
// all of the above methods take the command as their first argument
588595
result = getArgument(0)
589596
}
590597

598+
override DataFlow::Node getACommandArgument() { result = getACommandArgument(_) }
599+
600+
override predicate isShellInterpreted(DataFlow::Node arg) { arg = getACommandArgument(true) }
601+
591602
override DataFlow::Node getArgumentList() {
592603
(
593604
methodName = "execFile" or

javascript/ql/src/semmle/javascript/frameworks/ShellJS.qll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,8 @@ module ShellJS {
158158
ShellJSExec() { name = "exec" }
159159

160160
override DataFlow::Node getACommandArgument() { result = getArgument(0) }
161+
162+
override predicate isShellInterpreted(DataFlow::Node arg) { arg = getACommandArgument() }
161163
}
162164

163165
/**

javascript/ql/src/semmle/javascript/frameworks/SystemCommandExecutors.qll

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,36 +8,51 @@ import javascript
88
private class SystemCommandExecutors extends SystemCommandExecution, DataFlow::InvokeNode {
99
int cmdArg;
1010

11+
boolean shell;
12+
1113
SystemCommandExecutors() {
1214
exists(string mod, DataFlow::SourceNode callee |
1315
exists(string method |
14-
mod = "cross-spawn" and method = "sync" and cmdArg = 0
16+
mod = "cross-spawn" and method = "sync" and cmdArg = 0 and shell = false
1517
or
1618
mod = "execa" and
1719
(
18-
method = "shell" or
19-
method = "shellSync" or
20-
method = "stdout" or
21-
method = "stderr" or
22-
method = "sync"
20+
shell = false and
21+
(
22+
method = "shell" or
23+
method = "shellSync" or
24+
method = "stdout" or
25+
method = "stderr" or
26+
method = "sync"
27+
)
28+
or
29+
shell = true and
30+
(method = "command" or method = "commandSync")
2331
) and
2432
cmdArg = 0
2533
|
2634
callee = DataFlow::moduleMember(mod, method)
2735
)
2836
or
2937
(
30-
mod = "cross-spawn" and cmdArg = 0
31-
or
32-
mod = "cross-spawn-async" and cmdArg = 0
33-
or
34-
mod = "exec" and cmdArg = 0
35-
or
36-
mod = "exec-async" and cmdArg = 0
37-
or
38-
mod = "execa" and cmdArg = 0
38+
shell = false and
39+
(
40+
mod = "cross-spawn" and cmdArg = 0
41+
or
42+
mod = "cross-spawn-async" and cmdArg = 0
43+
or
44+
mod = "exec-async" and cmdArg = 0
45+
or
46+
mod = "execa" and cmdArg = 0
47+
)
3948
or
40-
mod = "remote-exec" and cmdArg = 1
49+
shell = true and
50+
(
51+
mod = "exec" and
52+
cmdArg = 0
53+
or
54+
mod = "remote-exec" and cmdArg = 1
55+
)
4156
) and
4257
callee = DataFlow::moduleImport(mod)
4358
|
@@ -46,4 +61,8 @@ private class SystemCommandExecutors extends SystemCommandExecution, DataFlow::I
4661
}
4762

4863
override DataFlow::Node getACommandArgument() { result = getArgument(cmdArg) }
64+
65+
override predicate isShellInterpreted(DataFlow::Node arg) {
66+
arg = getACommandArgument() and shell = true
67+
}
4968
}

0 commit comments

Comments
 (0)