Skip to content

Commit 0da1e68

Browse files
authored
Merge pull request github#3498 from max-schaefer/js/remote-exec
Approved by esbena
2 parents 14664be + bdd778f commit 0da1e68

File tree

4 files changed

+57
-7
lines changed

4 files changed

+57
-7
lines changed

change-notes/1.25/analysis-javascript.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
- [jQuery](https://jquery.com/)
1010
- [marsdb](https://www.npmjs.com/package/marsdb)
1111
- [minimongo](https://www.npmjs.com/package/minimongo/)
12+
- [ssh2](https://www.npmjs.com/package/ssh2)
13+
- [ssh2-streams](https://www.npmjs.com/package/ssh2-streams)
1214

1315
## New queries
1416

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

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,9 @@ private class SystemCommandExecutors extends SystemCommandExecution, DataFlow::I
5151
)
5252
or
5353
shell = true and
54-
(
55-
mod = "exec" and
56-
optionsArg = -2 and
57-
cmdArg = 0
58-
or
59-
mod = "remote-exec" and cmdArg = 1 and optionsArg = -1
60-
)
54+
mod = "exec" and
55+
optionsArg = -2 and
56+
cmdArg = 0
6157
) and
6258
callee = DataFlow::moduleImport(mod)
6359
|
@@ -97,3 +93,33 @@ private boolean getSync(string name) {
9793
then result = true
9894
else result = false
9995
}
96+
97+
private class RemoteCommandExecutor extends SystemCommandExecution, DataFlow::InvokeNode {
98+
int cmdArg;
99+
100+
RemoteCommandExecutor() {
101+
this = DataFlow::moduleImport("remote-exec").getACall() and
102+
cmdArg = 1
103+
or
104+
exists(DataFlow::SourceNode ssh2, DataFlow::SourceNode client |
105+
ssh2 = DataFlow::moduleImport("ssh2") and
106+
(client = ssh2 or client = ssh2.getAPropertyRead("Client")) and
107+
this = client.getAnInstantiation().getAMethodCall("exec") and
108+
cmdArg = 0
109+
)
110+
or
111+
exists(DataFlow::SourceNode ssh2stream |
112+
ssh2stream = DataFlow::moduleMember("ssh2-streams", "SSH2Stream") and
113+
this = ssh2stream.getAnInstantiation().getAMethodCall("exec") and
114+
cmdArg = 1
115+
)
116+
}
117+
118+
override DataFlow::Node getACommandArgument() { result = getArgument(cmdArg) }
119+
120+
override predicate isShellInterpreted(DataFlow::Node arg) { arg = getACommandArgument() }
121+
122+
override predicate isSync() { none() }
123+
124+
override DataFlow::Node getOptionsArg() { none() }
125+
}

javascript/ql/test/query-tests/Security/CWE-078/CommandInjection.expected

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,12 @@ nodes
9393
| other.js:18:22:18:24 | cmd |
9494
| other.js:19:36:19:38 | cmd |
9595
| other.js:19:36:19:38 | cmd |
96+
| other.js:22:21:22:23 | cmd |
97+
| other.js:22:21:22:23 | cmd |
98+
| other.js:23:28:23:30 | cmd |
99+
| other.js:23:28:23:30 | cmd |
100+
| other.js:26:34:26:36 | cmd |
101+
| other.js:26:34:26:36 | cmd |
96102
| third-party-command-injection.js:5:20:5:26 | command |
97103
| third-party-command-injection.js:5:20:5:26 | command |
98104
| third-party-command-injection.js:6:21:6:27 | command |
@@ -184,6 +190,12 @@ edges
184190
| other.js:5:9:5:49 | cmd | other.js:18:22:18:24 | cmd |
185191
| other.js:5:9:5:49 | cmd | other.js:19:36:19:38 | cmd |
186192
| other.js:5:9:5:49 | cmd | other.js:19:36:19:38 | cmd |
193+
| other.js:5:9:5:49 | cmd | other.js:22:21:22:23 | cmd |
194+
| other.js:5:9:5:49 | cmd | other.js:22:21:22:23 | cmd |
195+
| other.js:5:9:5:49 | cmd | other.js:23:28:23:30 | cmd |
196+
| other.js:5:9:5:49 | cmd | other.js:23:28:23:30 | cmd |
197+
| other.js:5:9:5:49 | cmd | other.js:26:34:26:36 | cmd |
198+
| other.js:5:9:5:49 | cmd | other.js:26:34:26:36 | cmd |
187199
| other.js:5:15:5:38 | url.par ... , true) | other.js:5:15:5:44 | url.par ... ).query |
188200
| other.js:5:15:5:44 | url.par ... ).query | other.js:5:15:5:49 | url.par ... ry.path |
189201
| other.js:5:15:5:49 | url.par ... ry.path | other.js:5:9:5:49 | cmd |
@@ -226,4 +238,7 @@ edges
226238
| other.js:17:27:17:29 | cmd | other.js:5:25:5:31 | req.url | other.js:17:27:17:29 | cmd | This command depends on $@. | other.js:5:25:5:31 | req.url | a user-provided value |
227239
| other.js:18:22:18:24 | cmd | other.js:5:25:5:31 | req.url | other.js:18:22:18:24 | cmd | This command depends on $@. | other.js:5:25:5:31 | req.url | a user-provided value |
228240
| other.js:19:36:19:38 | cmd | other.js:5:25:5:31 | req.url | other.js:19:36:19:38 | cmd | This command depends on $@. | other.js:5:25:5:31 | req.url | a user-provided value |
241+
| other.js:22:21:22:23 | cmd | other.js:5:25:5:31 | req.url | other.js:22:21:22:23 | cmd | This command depends on $@. | other.js:5:25:5:31 | req.url | a user-provided value |
242+
| other.js:23:28:23:30 | cmd | other.js:5:25:5:31 | req.url | other.js:23:28:23:30 | cmd | This command depends on $@. | other.js:5:25:5:31 | req.url | a user-provided value |
243+
| other.js:26:34:26:36 | cmd | other.js:5:25:5:31 | req.url | other.js:26:34:26:36 | cmd | This command depends on $@. | other.js:5:25:5:31 | req.url | a user-provided value |
229244
| third-party-command-injection.js:6:21:6:27 | command | third-party-command-injection.js:5:20:5:26 | command | third-party-command-injection.js:6:21:6:27 | command | This command depends on $@. | third-party-command-injection.js:5:20:5:26 | command | a server-provided value |

javascript/ql/test/query-tests/Security/CWE-078/other.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,11 @@ var server = http.createServer(function(req, res) {
1717
require("exec-async")(cmd); // NOT OK
1818
require("execa")(cmd); // NOT OK
1919
require("remote-exec")(target, cmd); // NOT OK
20+
21+
const ssh2 = require("ssh2");
22+
new ssh2().exec(cmd); // NOT OK
23+
new ssh2.Client().exec(cmd); // NOT OK
24+
25+
const SSH2Stream = require("ssh2-streams").SSH2Stream;
26+
new SSH2Stream().exec(false, cmd); // NOT OK
2027
});

0 commit comments

Comments
 (0)