Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e55eaef

Browse files
authored
Merge pull request #310 from esben-semmle/js/additional-client-request-data-nodes
Approved by xiemaisi
2 parents e319159 + 870811a commit e55eaef

14 files changed

Lines changed: 139 additions & 42 deletions

change-notes/1.19/analysis-javascript.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
* Support for popular libraries has been improved. Consequently, queries may produce more results on code bases that use the following features:
1010
- file system access, for example through [fs-extra](https://github.com/jprichardson/node-fs-extra) or [globby](https://www.npmjs.com/package/globby)
11+
- outbound network access, for example through the [fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)
1112
- the [Google Cloud Spanner](https://cloud.google.com/spanner) database
1213

1314
* The type inference now handles nested imports (that is, imports not appearing at the toplevel). This may yield fewer false-positive results on projects that use this non-standard language feature.

javascript/ql/src/semmle/javascript/dataflow/Sources.qll

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,26 @@ abstract class SourceNode extends DataFlow::Node {
131131
)
132132
}
133133

134+
/**
135+
* Gets a method call that invokes a method on this node.
136+
*
137+
* This includes only calls that have the syntactic shape of a method call,
138+
* that is, `o.m(...)` or `o[p](...)`.
139+
*/
140+
DataFlow::CallNode getAMethodCall() {
141+
result = getAMethodCall(_)
142+
}
143+
144+
/**
145+
* Gets a chained method call that invokes `methodName` last.
146+
*
147+
* The chain steps include only calls that have the syntactic shape of a method call,
148+
* that is, `o.m(...)` or `o[p](...)`.
149+
*/
150+
DataFlow::CallNode getAChainedMethodCall(string methodName) {
151+
result = getAMethodCall*().getAMethodCall(methodName)
152+
}
153+
134154
/**
135155
* Gets a `new` call that invokes constructor `constructorName` on this node.
136156
*/

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

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -116,31 +116,36 @@ private class RequestUrlRequest extends CustomClientRequest {
116116
*/
117117
private class AxiosUrlRequest extends CustomClientRequest {
118118

119-
DataFlow::Node url;
119+
string method;
120120

121121
AxiosUrlRequest() {
122122
exists (string moduleName, DataFlow::SourceNode callee |
123123
this = callee.getACall() |
124124
moduleName = "axios" and
125125
(
126-
callee = DataFlow::moduleImport(moduleName) or
127-
callee = DataFlow::moduleMember(moduleName, httpMethodName()) or
128-
callee = DataFlow::moduleMember(moduleName, "request")
129-
) and
130-
(
131-
url = getArgument(0) or
132-
// depends on the method name and the call arity, over-approximating slightly in the name of simplicity
133-
url = getOptionArgument([0..2], urlPropertyName())
126+
callee = DataFlow::moduleImport(moduleName) and method = "request" or
127+
callee = DataFlow::moduleMember(moduleName, method) and (method = httpMethodName() or method = "request")
134128
)
135129
)
136130
}
137131

138132
override DataFlow::Node getUrl() {
139-
result = url
133+
result = getArgument(0) or
134+
// depends on the method name and the call arity, over-approximating slightly in the name of simplicity
135+
result = getOptionArgument([0..2], urlPropertyName())
140136
}
141137

142138
override DataFlow::Node getADataNode() {
143-
none()
139+
method = "request" and
140+
result = getOptionArgument(0, "data")
141+
or
142+
(method = "post" or method = "put" or method = "put") and
143+
(result = getArgument(1) or result = getOptionArgument(2, "data"))
144+
or
145+
exists (string name |
146+
name = "headers" or name = "params"|
147+
result = getOptionArgument([0..2], name)
148+
)
144149
}
145150

146151
}
@@ -175,7 +180,10 @@ private class FetchUrlRequest extends CustomClientRequest {
175180
}
176181

177182
override DataFlow::Node getADataNode() {
178-
none()
183+
exists (string name |
184+
name = "headers" or name = "body" |
185+
result = getOptionArgument(1, name)
186+
)
179187
}
180188

181189
}
@@ -185,26 +193,27 @@ private class FetchUrlRequest extends CustomClientRequest {
185193
*/
186194
private class GotUrlRequest extends CustomClientRequest {
187195

188-
DataFlow::Node url;
189-
190196
GotUrlRequest() {
191197
exists (string moduleName, DataFlow::SourceNode callee |
192198
this = callee.getACall() |
193199
moduleName = "got" and
194200
(
195201
callee = DataFlow::moduleImport(moduleName) or
196202
callee = DataFlow::moduleMember(moduleName, "stream")
197-
) and
198-
url = getArgument(0) and not exists (getOptionArgument(1, "baseUrl"))
203+
)
199204
)
200205
}
201206

202207
override DataFlow::Node getUrl() {
203-
result = url
208+
result = getArgument(0) and
209+
not exists (getOptionArgument(1, "baseUrl"))
204210
}
205211

206212
override DataFlow::Node getADataNode() {
207-
none()
213+
exists (string name |
214+
name = "headers" or name = "body" or name = "query" |
215+
result = getOptionArgument(1, name)
216+
)
208217
}
209218

210219
}
@@ -230,7 +239,10 @@ private class SuperAgentUrlRequest extends CustomClientRequest {
230239
}
231240

232241
override DataFlow::Node getADataNode() {
233-
none()
242+
exists (string name |
243+
name = "set" or name = "send" or name = "query" |
244+
result = this.getAChainedMethodCall(name).getAnArgument()
245+
)
234246
}
235247

236248
}

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

Lines changed: 7 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -49,32 +49,14 @@ module Electron {
4949
}
5050

5151
}
52-
53-
/**
54-
* A Node.js-style HTTP or HTTPS request made using `electron.net`, for example `net.request(url)`.
55-
*/
56-
private class NetRequest extends CustomElectronClientRequest {
57-
NetRequest() {
58-
this = DataFlow::moduleMember("electron", "net").getAMemberCall("request")
59-
}
60-
61-
override DataFlow::Node getUrl() {
62-
result = getArgument(0) or
63-
result = getOptionArgument(0, "url")
64-
}
65-
66-
override DataFlow::Node getADataNode() {
67-
none()
68-
}
69-
70-
}
7152

7253
/**
73-
* A Node.js-style HTTP or HTTPS request made using `electron.client`, for example `new client(url)`.
54+
* A Node.js-style HTTP or HTTPS request made using `electron.ClientRequest`.
7455
*/
7556
private class NewClientRequest extends CustomElectronClientRequest {
7657
NewClientRequest() {
77-
this = DataFlow::moduleMember("electron", "ClientRequest").getAnInstantiation()
58+
this = DataFlow::moduleMember("electron", "ClientRequest").getAnInstantiation() or
59+
this = DataFlow::moduleMember("electron", "net").getAMemberCall("request") // alias
7860
}
7961

8062
override DataFlow::Node getUrl() {
@@ -83,7 +65,10 @@ module Electron {
8365
}
8466

8567
override DataFlow::Node getADataNode() {
86-
none()
68+
exists (string name |
69+
name = "write" or name = "end" |
70+
result =this.(DataFlow::SourceNode).getAMethodCall(name).getArgument(0)
71+
)
8772
}
8873

8974
}

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -740,7 +740,10 @@ module NodeJSLib {
740740
}
741741

742742
override DataFlow::Node getADataNode() {
743-
result = getAMethodCall("write").getArgument(0)
743+
exists (string name |
744+
name = "write" or name = "end" |
745+
result =this.(DataFlow::SourceNode).getAMethodCall(name).getArgument(0)
746+
)
744747
}
745748

746749
}

javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequest.expected

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,14 @@
1616
| tst.js:41:5:41:29 | net.req ... url }) |
1717
| tst.js:43:5:43:26 | new Cli ... st(url) |
1818
| tst.js:45:5:45:35 | new Cli ... url }) |
19+
| tst.js:53:5:53:23 | axios({data: data}) |
20+
| tst.js:55:5:55:34 | axios.g ... _data}) |
21+
| tst.js:57:5:57:39 | axios.p ... data2}) |
22+
| tst.js:59:5:59:52 | axios({ ... sData}) |
23+
| tst.js:61:5:61:60 | window. ... yData}) |
24+
| tst.js:63:5:63:68 | got(url ... yData}) |
25+
| tst.js:65:5:65:23 | superagent.get(url) |
26+
| tst.js:66:5:66:23 | superagent.get(url) |
27+
| tst.js:67:5:67:24 | superagent.post(url) |
28+
| tst.js:68:5:68:23 | superagent.get(url) |
29+
| tst.js:69:5:69:23 | superagent.get(url) |
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
| tst.js:53:5:53:23 | axios({data: data}) | tst.js:53:18:53:21 | data |
2+
| tst.js:57:5:57:39 | axios.p ... data2}) | tst.js:57:19:57:23 | data1 |
3+
| tst.js:57:5:57:39 | axios.p ... data2}) | tst.js:57:33:57:37 | data2 |
4+
| tst.js:59:5:59:52 | axios({ ... sData}) | tst.js:59:21:59:30 | headerData |
5+
| tst.js:59:5:59:52 | axios({ ... sData}) | tst.js:59:41:59:50 | paramsData |
6+
| tst.js:61:5:61:60 | window. ... yData}) | tst.js:61:33:61:42 | headerData |
7+
| tst.js:61:5:61:60 | window. ... yData}) | tst.js:61:51:61:58 | bodyData |
8+
| tst.js:63:5:63:68 | got(url ... yData}) | tst.js:63:24:63:33 | headerData |
9+
| tst.js:63:5:63:68 | got(url ... yData}) | tst.js:63:42:63:49 | bodyData |
10+
| tst.js:65:5:65:23 | superagent.get(url) | tst.js:65:31:65:34 | data |
11+
| tst.js:66:5:66:23 | superagent.get(url) | tst.js:66:29:66:31 | 'x' |
12+
| tst.js:66:5:66:23 | superagent.get(url) | tst.js:66:34:66:43 | headerData |
13+
| tst.js:67:5:67:24 | superagent.post(url) | tst.js:67:31:67:38 | bodyData |
14+
| tst.js:68:5:68:23 | superagent.get(url) | tst.js:68:29:68:31 | 'x' |
15+
| tst.js:68:5:68:23 | superagent.get(url) | tst.js:68:34:68:43 | headerData |
16+
| tst.js:68:5:68:23 | superagent.get(url) | tst.js:68:52:68:60 | queryData |
17+
| tst.js:69:5:69:23 | superagent.get(url) | tst.js:69:48:69:56 | queryData |
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import javascript
2+
3+
from ClientRequest r
4+
select r, r.getADataNode()

javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequest_getUrl.expected

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,14 @@
2020
| tst.js:43:5:43:26 | new Cli ... st(url) | tst.js:43:23:43:25 | url |
2121
| tst.js:45:5:45:35 | new Cli ... url }) | tst.js:45:23:45:34 | { url: url } |
2222
| tst.js:45:5:45:35 | new Cli ... url }) | tst.js:45:30:45:32 | url |
23+
| tst.js:53:5:53:23 | axios({data: data}) | tst.js:53:11:53:22 | {data: data} |
24+
| tst.js:55:5:55:34 | axios.g ... _data}) | tst.js:55:15:55:15 | x |
25+
| tst.js:57:5:57:39 | axios.p ... data2}) | tst.js:57:16:57:16 | x |
26+
| tst.js:59:5:59:52 | axios({ ... sData}) | tst.js:59:11:59:51 | {header ... msData} |
27+
| tst.js:61:5:61:60 | window. ... yData}) | tst.js:61:18:61:20 | url |
28+
| tst.js:63:5:63:68 | got(url ... yData}) | tst.js:63:9:63:11 | url |
29+
| tst.js:65:5:65:23 | superagent.get(url) | tst.js:65:20:65:22 | url |
30+
| tst.js:66:5:66:23 | superagent.get(url) | tst.js:66:20:66:22 | url |
31+
| tst.js:67:5:67:24 | superagent.post(url) | tst.js:67:21:67:23 | url |
32+
| tst.js:68:5:68:23 | superagent.get(url) | tst.js:68:20:68:22 | url |
33+
| tst.js:69:5:69:23 | superagent.get(url) | tst.js:69:20:69:22 | url |

javascript/ql/test/library-tests/frameworks/ClientRequests/tst.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,24 @@ import {ClientRequest, net} from 'electron';
4848

4949
unknown({ url:url });
5050
});
51+
52+
(function() {
53+
axios({data: data});
54+
55+
axios.get(x, {data: not_data});
56+
57+
axios.post(x, data1, {data: data2});
58+
59+
axios({headers: headerData, params: paramsData});
60+
61+
window.fetch(url, {headers: headerData, body: bodyData});
62+
63+
got(url, {headers: headerData, body: bodyData, quer: queryData});
64+
65+
superagent.get(url).query(data);
66+
superagent.get(url).set('x', headerData)
67+
superagent.post(url).send(bodyData);
68+
superagent.get(url).set('x', headerData).query(queryData);
69+
superagent.get(url).unknown(nonData).query(queryData);
70+
71+
});

0 commit comments

Comments
 (0)