Skip to content

Commit 10b0025

Browse files
authored
Merge pull request #915 from asger-semmle/closure-uri-methods
Approved by xiemaisi
2 parents e4ba5ce + f6e0ccf commit 10b0025

File tree

13 files changed

+292
-86
lines changed

13 files changed

+292
-86
lines changed

javascript/ql/src/semmle/javascript/Closure.qll

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -170,9 +170,11 @@ module Closure {
170170
isLibraryNamespacePath(result) and
171171
node = DataFlow::globalVarRef(result)
172172
or
173-
isLibraryNamespacePath(result) and
174-
exists(DataFlow::PropRead read | node = read |
175-
result = getLibraryAccessPath(read.getBase().getALocalSource()) + "." + read.getPropertyName()
173+
exists(DataFlow::SourceNode base, string basePath, string prop |
174+
basePath = getLibraryAccessPath(base) and
175+
isLibraryNamespacePath(basePath) and
176+
node = base.getAPropertyRead(prop) and
177+
result = basePath + "." + prop
176178
)
177179
or
178180
// Associate an access path with the immediate RHS of a store on a closure namespace.
@@ -194,16 +196,7 @@ module Closure {
194196
}
195197

196198
/**
197-
* Gets a dataflow node that refers to the given Closure module.
198-
*/
199-
DataFlow::SourceNode moduleImport(string moduleName) {
200-
getLibraryAccessPath(result) = moduleName
201-
}
202-
203-
/**
204-
* Gets a dataflow node that refers to the given member of a Closure module.
199+
* Gets a dataflow node that refers to the given value exported from a Closure module.
205200
*/
206-
DataFlow::SourceNode moduleMember(string moduleName, string memberName) {
207-
result = moduleImport(moduleName).getAPropertyRead(memberName)
208-
}
201+
DataFlow::SourceNode moduleImport(string moduleName) { getLibraryAccessPath(result) = moduleName }
209202
}

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

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,11 +240,38 @@ private class SuperAgentUrlRequest extends CustomClientRequest {
240240
* A model of a URL request made using the `XMLHttpRequest` browser class.
241241
*/
242242
private class XMLHttpRequest extends CustomClientRequest {
243-
XMLHttpRequest() { this = DataFlow::globalVarRef("XMLHttpRequest").getAnInstantiation() }
243+
XMLHttpRequest() {
244+
this = DataFlow::globalVarRef("XMLHttpRequest").getAnInstantiation()
245+
or
246+
// closure shim for XMLHttpRequest
247+
this = Closure::moduleImport("goog.net.XmlHttp").getAnInstantiation()
248+
}
244249

245250
override DataFlow::Node getUrl() { result = getAMethodCall("open").getArgument(1) }
246251

247252
override DataFlow::Node getHost() { none() }
248253

249254
override DataFlow::Node getADataNode() { result = getAMethodCall("send").getArgument(0) }
250255
}
256+
257+
/**
258+
* A model of a URL request made using the `XhrIo` class from the closure library.
259+
*/
260+
private class ClosureXhrIoRequest extends CustomClientRequest {
261+
ClosureXhrIoRequest() {
262+
exists(DataFlow::SourceNode xhrIo | xhrIo = Closure::moduleImport("goog.net.XhrIo") |
263+
this = xhrIo.getAMethodCall("send")
264+
or
265+
this = xhrIo.getAnInstantiation().getAMethodCall("send")
266+
)
267+
}
268+
269+
override DataFlow::Node getUrl() { result = getArgument(0) }
270+
271+
override DataFlow::Node getHost() { none() }
272+
273+
override DataFlow::Node getADataNode() {
274+
result = getArgument(2) or
275+
result = getArgument(3)
276+
}
277+
}

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

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,3 +309,88 @@ module querystring {
309309
override predicate step(DataFlow::Node pred, DataFlow::Node succ) { pred = src and succ = this }
310310
}
311311
}
312+
313+
/**
314+
* Provides steps for the `goog.Uri` class in the closure library.
315+
*/
316+
private module ClosureLibraryUri {
317+
/**
318+
* Taint step from an argument of a `goog.Uri` call to the return value.
319+
*/
320+
private class ArgumentStep extends UriLibraryStep, DataFlow::InvokeNode {
321+
int arg;
322+
323+
ArgumentStep() {
324+
// goog.Uri constructor
325+
this = Closure::moduleImport("goog.Uri").getAnInstantiation() and arg = 0
326+
or
327+
// static methods on goog.Uri
328+
exists(string name | this = Closure::moduleImport("goog.Uri." + name).getACall() |
329+
name = "parse" and arg = 0
330+
or
331+
name = "create" and
332+
(arg = 0 or arg = 2 or arg = 4)
333+
or
334+
name = "resolve" and
335+
(arg = 0 or arg = 1)
336+
)
337+
or
338+
// static methods in goog.uri.utils
339+
arg = 0 and
340+
exists(string name | this = Closure::moduleImport("goog.uri.utils." + name).getACall() |
341+
name = "appendParam" or // preserve taint from the original URI, but not from the appended param
342+
name = "appendParams" or
343+
name = "appendParamsFromMap" or
344+
name = "appendPath" or
345+
name = "getParamValue" or
346+
name = "getParamValues" or
347+
name = "getPath" or
348+
name = "getPathAndAfter" or
349+
name = "getQueryData" or
350+
name = "parseQueryData" or
351+
name = "removeFragment" or
352+
name = "removeParam" or
353+
name = "setParam" or
354+
name = "setParamsFromMap" or
355+
name = "setPath" or
356+
name = "split"
357+
)
358+
}
359+
360+
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
361+
pred = getArgument(arg) and
362+
succ = this
363+
}
364+
}
365+
366+
/**
367+
* Taint steps through chainable setter calls.
368+
*
369+
* Setters mutate the URI object and return the same instance.
370+
*/
371+
private class SetterCall extends DataFlow::MethodCallNode, UriLibraryStep {
372+
DataFlow::NewNode uri;
373+
string name;
374+
375+
SetterCall() {
376+
exists(DataFlow::SourceNode base |
377+
base = Closure::moduleImport("goog.Uri").getAnInstantiation() and
378+
uri = base
379+
or
380+
base.(SetterCall).getUri() = uri
381+
|
382+
this = base.getAMethodCall(name) and
383+
name.matches("set%")
384+
)
385+
}
386+
387+
DataFlow::NewNode getUri() { result = uri }
388+
389+
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
390+
pred = getReceiver() and succ = this
391+
or
392+
(name = "setDomain" or name = "setPath" or name = "setScheme") and
393+
pred = getArgument(0) and succ = uri
394+
}
395+
}
396+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
| tests/uri.js:5:5:5:11 | net.Uri |
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import javascript
2+
3+
select Closure::moduleImport("goog.net.Uri")

javascript/ql/test/library-tests/Closure/moduleImport.expected

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,59 @@
1+
| goog | tests/es6Module.js:1:1:1:4 | goog |
2+
| goog | tests/es6ModuleDefault.js:1:1:1:4 | goog |
3+
| goog | tests/globalModule.js:1:1:1:4 | goog |
4+
| goog | tests/globalModuleDefault.js:1:1:1:4 | goog |
5+
| goog | tests/googModule.js:1:1:1:4 | goog |
6+
| goog | tests/googModuleDefault.js:1:1:1:4 | goog |
7+
| goog | tests/requireFromEs6.js:3:20:3:23 | goog |
8+
| goog | tests/requireFromEs6.js:4:27:4:30 | goog |
9+
| goog | tests/requireFromEs6.js:6:17:6:20 | goog |
10+
| goog | tests/requireFromEs6.js:7:24:7:27 | goog |
11+
| goog | tests/requireFromEs6.js:9:18:9:21 | goog |
12+
| goog | tests/requireFromEs6.js:10:25:10:28 | goog |
13+
| goog | tests/requireFromGlobalModule.js:1:1:1:4 | goog |
14+
| goog | tests/requireFromGlobalModule.js:2:1:2:4 | goog |
15+
| goog | tests/requireFromGlobalModule.js:4:1:4:4 | goog |
16+
| goog | tests/requireFromGlobalModule.js:5:1:5:4 | goog |
17+
| goog | tests/requireFromGlobalModule.js:7:1:7:4 | goog |
18+
| goog | tests/requireFromGlobalModule.js:8:1:8:4 | goog |
19+
| goog | tests/requireFromGoogModule.js:1:1:1:4 | goog |
20+
| goog | tests/requireFromGoogModule.js:3:20:3:23 | goog |
21+
| goog | tests/requireFromGoogModule.js:4:27:4:30 | goog |
22+
| goog | tests/requireFromGoogModule.js:6:17:6:20 | goog |
23+
| goog | tests/requireFromGoogModule.js:7:24:7:27 | goog |
24+
| goog | tests/requireFromGoogModule.js:9:18:9:21 | goog |
25+
| goog | tests/requireFromGoogModule.js:10:25:10:28 | goog |
26+
| goog | tests/uri.js:1:1:1:4 | goog |
27+
| goog | tests/uri.js:3:11:3:14 | goog |
28+
| goog.declareModuleId | tests/es6Module.js:1:1:1:20 | goog.declareModuleId |
29+
| goog.declareModuleId | tests/es6ModuleDefault.js:1:1:1:20 | goog.declareModuleId |
30+
| goog.module | tests/googModule.js:1:1:1:11 | goog.module |
31+
| goog.module | tests/googModuleDefault.js:1:1:1:11 | goog.module |
32+
| goog.module | tests/requireFromGoogModule.js:1:1:1:11 | goog.module |
33+
| goog.module | tests/uri.js:1:1:1:11 | goog.module |
34+
| goog.net | tests/uri.js:3:11:3:34 | goog.re ... g.net') |
35+
| goog.net.Uri | tests/uri.js:5:5:5:11 | net.Uri |
36+
| goog.provide | tests/globalModule.js:1:1:1:12 | goog.provide |
37+
| goog.provide | tests/globalModuleDefault.js:1:1:1:12 | goog.provide |
38+
| goog.require | tests/requireFromEs6.js:3:20:3:31 | goog.require |
39+
| goog.require | tests/requireFromEs6.js:4:27:4:38 | goog.require |
40+
| goog.require | tests/requireFromEs6.js:6:17:6:28 | goog.require |
41+
| goog.require | tests/requireFromEs6.js:7:24:7:35 | goog.require |
42+
| goog.require | tests/requireFromEs6.js:9:18:9:29 | goog.require |
43+
| goog.require | tests/requireFromEs6.js:10:25:10:36 | goog.require |
44+
| goog.require | tests/requireFromGlobalModule.js:1:1:1:12 | goog.require |
45+
| goog.require | tests/requireFromGlobalModule.js:2:1:2:12 | goog.require |
46+
| goog.require | tests/requireFromGlobalModule.js:4:1:4:12 | goog.require |
47+
| goog.require | tests/requireFromGlobalModule.js:5:1:5:12 | goog.require |
48+
| goog.require | tests/requireFromGlobalModule.js:7:1:7:12 | goog.require |
49+
| goog.require | tests/requireFromGlobalModule.js:8:1:8:12 | goog.require |
50+
| goog.require | tests/requireFromGoogModule.js:3:20:3:31 | goog.require |
51+
| goog.require | tests/requireFromGoogModule.js:4:27:4:38 | goog.require |
52+
| goog.require | tests/requireFromGoogModule.js:6:17:6:28 | goog.require |
53+
| goog.require | tests/requireFromGoogModule.js:7:24:7:35 | goog.require |
54+
| goog.require | tests/requireFromGoogModule.js:9:18:9:29 | goog.require |
55+
| goog.require | tests/requireFromGoogModule.js:10:25:10:36 | goog.require |
56+
| goog.require | tests/uri.js:3:11:3:22 | goog.require |
157
| x | tests/globalModule.js:3:1:3:1 | x |
258
| x | tests/globalModuleDefault.js:3:1:3:1 | x |
359
| x | tests/requireFromGlobalModule.js:10:1:10:1 | x |
@@ -26,6 +82,9 @@
2682
| x.y.z.es6 | tests/requireFromGlobalModule.js:7:1:7:25 | goog.re ... z.es6') |
2783
| x.y.z.es6 | tests/requireFromGlobalModule.js:16:1:16:9 | x.y.z.es6 |
2884
| x.y.z.es6 | tests/requireFromGoogModule.js:6:17:6:41 | goog.re ... z.es6') |
85+
| x.y.z.es6.fun | tests/requireFromEs6.js:15:1:15:13 | es6Module.fun |
86+
| x.y.z.es6.fun | tests/requireFromGlobalModule.js:16:1:16:13 | x.y.z.es6.fun |
87+
| x.y.z.es6.fun | tests/requireFromGoogModule.js:15:1:15:13 | es6Module.fun |
2988
| x.y.z.es6default | tests/requireFromEs6.js:7:24:7:55 | goog.re ... fault') |
3089
| x.y.z.es6default | tests/requireFromGlobalModule.js:8:1:8:32 | goog.re ... fault') |
3190
| x.y.z.es6default | tests/requireFromGlobalModule.js:17:1:17:16 | x.y.z.es6default |
@@ -36,6 +95,9 @@
3695
| x.y.z.global | tests/requireFromGlobalModule.js:10:1:10:12 | x.y.z.global |
3796
| x.y.z.global | tests/requireFromGoogModule.js:3:20:3:47 | goog.re ... lobal') |
3897
| x.y.z.global.fun | tests/globalModule.js:4:6:4:10 | () {} |
98+
| x.y.z.global.fun | tests/requireFromEs6.js:12:1:12:16 | globalModule.fun |
99+
| x.y.z.global.fun | tests/requireFromGlobalModule.js:10:1:10:16 | x.y.z.global.fun |
100+
| x.y.z.global.fun | tests/requireFromGoogModule.js:12:1:12:16 | globalModule.fun |
39101
| x.y.z.globaldefault | tests/globalModuleDefault.js:3:23:3:39 | function fun() {} |
40102
| x.y.z.globaldefault | tests/requireFromEs6.js:4:27:4:61 | goog.re ... fault') |
41103
| x.y.z.globaldefault | tests/requireFromGlobalModule.js:2:1:2:35 | goog.re ... fault') |
@@ -45,6 +107,9 @@
45107
| x.y.z.goog | tests/requireFromGlobalModule.js:4:1:4:26 | goog.re ... .goog') |
46108
| x.y.z.goog | tests/requireFromGlobalModule.js:13:1:13:10 | x.y.z.goog |
47109
| x.y.z.goog | tests/requireFromGoogModule.js:9:18:9:43 | goog.re ... .goog') |
110+
| x.y.z.goog.fun | tests/requireFromEs6.js:18:1:18:14 | googModule.fun |
111+
| x.y.z.goog.fun | tests/requireFromGlobalModule.js:13:1:13:14 | x.y.z.goog.fun |
112+
| x.y.z.goog.fun | tests/requireFromGoogModule.js:18:1:18:14 | googModule.fun |
48113
| x.y.z.googdefault | tests/requireFromEs6.js:10:25:10:57 | goog.re ... fault') |
49114
| x.y.z.googdefault | tests/requireFromGlobalModule.js:5:1:5:33 | goog.re ... fault') |
50115
| x.y.z.googdefault | tests/requireFromGlobalModule.js:14:1:14:17 | x.y.z.googdefault |

javascript/ql/test/library-tests/Closure/moduleMember.expected

Lines changed: 0 additions & 31 deletions
This file was deleted.

javascript/ql/test/library-tests/Closure/moduleMember.ql

Lines changed: 0 additions & 4 deletions
This file was deleted.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
goog.module('uritest');
2+
3+
let net = goog.require('goog.net');
4+
5+
new net.Uri();

javascript/ql/test/library-tests/frameworks/UriLibraries/UriLibraryStep.expected

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,27 @@
1+
| closureUri.js:5:11:5:20 | new Uri(x) | closureUri.js:5:19:5:19 | x | closureUri.js:5:11:5:20 | new Uri(x) |
2+
| closureUri.js:6:1:6:12 | Uri.parse(x) | closureUri.js:6:11:6:11 | x | closureUri.js:6:1:6:12 | Uri.parse(x) |
3+
| closureUri.js:7:1:7:17 | Uri.resolve(x, y) | closureUri.js:7:13:7:13 | x | closureUri.js:7:1:7:17 | Uri.resolve(x, y) |
4+
| closureUri.js:7:1:7:17 | Uri.resolve(x, y) | closureUri.js:7:16:7:16 | y | closureUri.js:7:1:7:17 | Uri.resolve(x, y) |
5+
| closureUri.js:8:1:8:57 | Uri.cre ... , frag) | closureUri.js:8:12:8:17 | scheme | closureUri.js:8:1:8:57 | Uri.cre ... , frag) |
6+
| closureUri.js:8:1:8:57 | Uri.cre ... , frag) | closureUri.js:8:26:8:31 | domain | closureUri.js:8:1:8:57 | Uri.cre ... , frag) |
7+
| closureUri.js:8:1:8:57 | Uri.cre ... , frag) | closureUri.js:8:40:8:43 | path | closureUri.js:8:1:8:57 | Uri.cre ... , frag) |
8+
| closureUri.js:10:1:10:16 | uri.setScheme(x) | closureUri.js:10:1:10:3 | uri | closureUri.js:10:1:10:16 | uri.setScheme(x) |
9+
| closureUri.js:10:1:10:16 | uri.setScheme(x) | closureUri.js:10:15:10:15 | x | closureUri.js:5:11:5:20 | new Uri(x) |
10+
| closureUri.js:11:1:11:18 | uri.setUserInfo(x) | closureUri.js:11:1:11:3 | uri | closureUri.js:11:1:11:18 | uri.setUserInfo(x) |
11+
| closureUri.js:12:1:12:16 | uri.setDomain(x) | closureUri.js:12:1:12:3 | uri | closureUri.js:12:1:12:16 | uri.setDomain(x) |
12+
| closureUri.js:12:1:12:16 | uri.setDomain(x) | closureUri.js:12:15:12:15 | x | closureUri.js:5:11:5:20 | new Uri(x) |
13+
| closureUri.js:13:1:13:14 | uri.setPort(x) | closureUri.js:13:1:13:3 | uri | closureUri.js:13:1:13:14 | uri.setPort(x) |
14+
| closureUri.js:14:1:14:14 | uri.setPath(x) | closureUri.js:14:1:14:3 | uri | closureUri.js:14:1:14:14 | uri.setPath(x) |
15+
| closureUri.js:14:1:14:14 | uri.setPath(x) | closureUri.js:14:13:14:13 | x | closureUri.js:5:11:5:20 | new Uri(x) |
16+
| closureUri.js:15:1:15:15 | uri.setQuery(x) | closureUri.js:15:1:15:3 | uri | closureUri.js:15:1:15:15 | uri.setQuery(x) |
17+
| closureUri.js:16:1:16:18 | uri.setFragment(x) | closureUri.js:16:1:16:3 | uri | closureUri.js:16:1:16:18 | uri.setFragment(x) |
18+
| closureUri.js:18:1:18:15 | uri.setQuery(x) | closureUri.js:18:1:18:3 | uri | closureUri.js:18:1:18:15 | uri.setQuery(x) |
19+
| closureUri.js:18:1:18:26 | uri.set ... Path(y) | closureUri.js:18:1:18:15 | uri.setQuery(x) | closureUri.js:18:1:18:26 | uri.set ... Path(y) |
20+
| closureUri.js:18:1:18:26 | uri.set ... Path(y) | closureUri.js:18:25:18:25 | y | closureUri.js:5:11:5:20 | new Uri(x) |
21+
| closureUri.js:18:1:18:39 | uri.set ... heme(z) | closureUri.js:18:1:18:26 | uri.set ... Path(y) | closureUri.js:18:1:18:39 | uri.set ... heme(z) |
22+
| closureUri.js:18:1:18:39 | uri.set ... heme(z) | closureUri.js:18:38:18:38 | z | closureUri.js:5:11:5:20 | new Uri(x) |
23+
| closureUri.js:22:1:22:25 | utils.a ... uri, z) | closureUri.js:22:19:22:21 | uri | closureUri.js:22:1:22:25 | utils.a ... uri, z) |
24+
| closureUri.js:23:1:23:18 | utils.getPath(uri) | closureUri.js:23:15:23:17 | uri | closureUri.js:23:1:23:18 | utils.getPath(uri) |
125
| punycode.js:3:9:3:26 | punycode.decode(x) | punycode.js:3:25:3:25 | x | punycode.js:3:9:3:26 | punycode.decode(x) |
226
| punycode.js:5:5:5:22 | punycode.encode(x) | punycode.js:5:21:5:21 | x | punycode.js:5:5:5:22 | punycode.encode(x) |
327
| punycode.js:7:5:7:25 | punycod ... code(x) | punycode.js:7:24:7:24 | x | punycode.js:7:5:7:25 | punycod ... code(x) |

0 commit comments

Comments
 (0)