Skip to content

Commit b7c0d18

Browse files
authored
Merge pull request github#5278 from erik-krogh/formData
Approved by asgerf
2 parents 0e70b58 + c59e6fe commit b7c0d18

File tree

4 files changed

+60
-1
lines changed

4 files changed

+60
-1
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
lgtm,codescanning
2+
* URIs used in the form-data library are now recognized as sinks for `js/request-forgery`.
3+
Affected packages are
4+
[form-data](https://www.npmjs.com/package/form-data)

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -854,4 +854,24 @@ module ClientRequest {
854854
override DataFlow::Node getADataNode() { none() }
855855
}
856856
}
857+
858+
/**
859+
* A model of a URL request made using [form-data](https://www.npmjs.com/package/form-data).
860+
*/
861+
class FormDataRequest extends ClientRequest::Range, API::InvokeNode {
862+
API::Node form;
863+
864+
FormDataRequest() {
865+
form = API::moduleImport("form-data").getInstance() and
866+
this = form.getMember("submit").getACall()
867+
}
868+
869+
override DataFlow::Node getUrl() { result = getArgument(0) }
870+
871+
override DataFlow::Node getHost() { result = getParameter(0).getMember("host").getARhs() }
872+
873+
override DataFlow::Node getADataNode() {
874+
result = form.getMember("append").getACall().getParameter(1).getARhs()
875+
}
876+
}
857877
}

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ test_ClientRequest
7676
| tst.js:229:5:229:67 | needle( ... ptions) |
7777
| tst.js:231:5:233:6 | needle. ... \\n }) |
7878
| tst.js:235:5:237:6 | needle. ... \\n }) |
79+
| tst.js:247:24:247:68 | request ... o.png') |
80+
| tst.js:249:1:251:2 | form.su ... e();\\n}) |
81+
| tst.js:257:1:262:2 | form.su ... rs()\\n}) |
7982
test_getADataNode
8083
| tst.js:53:5:53:23 | axios({data: data}) | tst.js:53:18:53:21 | data |
8184
| tst.js:57:5:57:39 | axios.p ... data2}) | tst.js:57:19:57:23 | data1 |
@@ -110,12 +113,17 @@ test_getADataNode
110113
| tst.js:229:5:229:67 | needle( ... ptions) | tst.js:229:50:229:57 | "MyData" |
111114
| tst.js:235:5:237:6 | needle. ... \\n }) | tst.js:228:32:228:70 | { 'X-Cu ... tuna' } |
112115
| tst.js:235:5:237:6 | needle. ... \\n }) | tst.js:235:44:235:49 | "data" |
116+
| tst.js:249:1:251:2 | form.su ... e();\\n}) | tst.js:245:25:245:34 | 'my value' |
117+
| tst.js:249:1:251:2 | form.su ... e();\\n}) | tst.js:246:26:246:43 | Buffer.from("foo") |
118+
| tst.js:249:1:251:2 | form.su ... e();\\n}) | tst.js:247:24:247:68 | request ... o.png') |
119+
| tst.js:257:1:262:2 | form.su ... rs()\\n}) | tst.js:255:25:255:35 | 'new_value' |
113120
test_getHost
114121
| tst.js:87:5:87:39 | http.ge ... host}) | tst.js:87:34:87:37 | host |
115122
| tst.js:89:5:89:23 | axios({host: host}) | tst.js:89:18:89:21 | host |
116123
| tst.js:91:5:91:34 | got(rel ... host}) | tst.js:91:29:91:32 | host |
117124
| tst.js:93:5:93:35 | net.req ... host }) | tst.js:93:29:93:32 | host |
118125
| tst.js:219:5:219:41 | data.so ... Host"}) | tst.js:219:32:219:39 | "myHost" |
126+
| tst.js:257:1:262:2 | form.su ... rs()\\n}) | tst.js:259:11:259:23 | 'example.org' |
119127
test_getUrl
120128
| apollo.js:5:18:5:78 | new cre ... hql' }) | apollo.js:5:44:5:75 | 'https: ... raphql' |
121129
| apollo.js:10:1:10:54 | new Htt ... hql' }) | apollo.js:10:21:10:51 | 'http:/ ... raphql' |
@@ -199,6 +207,9 @@ test_getUrl
199207
| tst.js:229:5:229:67 | needle( ... ptions) | tst.js:229:20:229:47 | "http:/ ... oo/bar" |
200208
| tst.js:231:5:233:6 | needle. ... \\n }) | tst.js:231:16:231:35 | "http://example.org" |
201209
| tst.js:235:5:237:6 | needle. ... \\n }) | tst.js:235:17:235:41 | "http:/ ... g/post" |
210+
| tst.js:247:24:247:68 | request ... o.png') | tst.js:247:32:247:67 | 'http:/ ... go.png' |
211+
| tst.js:249:1:251:2 | form.su ... e();\\n}) | tst.js:249:13:249:33 | 'http:/ ... e.org/' |
212+
| tst.js:257:1:262:2 | form.su ... rs()\\n}) | tst.js:257:13:262:1 | {\\n m ... ers()\\n} |
202213
test_getAResponseDataNode
203214
| tst.js:19:5:19:23 | requestPromise(url) | tst.js:19:5:19:23 | requestPromise(url) | text | true |
204215
| tst.js:21:5:21:23 | superagent.get(url) | tst.js:21:5:21:23 | superagent.get(url) | stream | true |

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

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,4 +235,28 @@ const needle = require("needle");
235235
needle.post("http://example.org/post", "data", options, (err, resp, body) => {
236236

237237
});
238-
})();
238+
})();
239+
240+
var FormData = require('form-data');
241+
var request = require('request');
242+
243+
var form = new FormData();
244+
245+
form.append('my_field', 'my value');
246+
form.append('my_buffer', Buffer.from("foo"));
247+
form.append('my_logo', request('http://example.org/images/logo.png'));
248+
249+
form.submit('http://example.org/', (err, res) => {
250+
res.resume();
251+
});
252+
253+
254+
var form = new FormData();
255+
form.append('new_form', 'new_value');
256+
257+
form.submit({
258+
method: 'post',
259+
host: 'example.org',
260+
path: '/upload',
261+
headers: form.getHeaders()
262+
});

0 commit comments

Comments
 (0)