Skip to content

Commit 4ec2df6

Browse files
authored
Merge pull request #1179 from asger-semmle/js-windoc
Approved by xiemaisi
2 parents 02f4695 + 4c99c01 commit 4ec2df6

File tree

5 files changed

+102
-59
lines changed

5 files changed

+102
-59
lines changed

javascript/ql/src/semmle/javascript/DOM.qll

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,4 +253,86 @@ module DOM {
253253
reason = "must not contain any space characters"
254254
)
255255
}
256+
257+
/** Gets a call that queries the DOM for a collection of DOM nodes. */
258+
private DataFlow::SourceNode domElementCollection() {
259+
exists(string collectionName |
260+
collectionName = "getElementsByClassName" or
261+
collectionName = "getElementsByName" or
262+
collectionName = "getElementsByTagName" or
263+
collectionName = "getElementsByTagNameNS" or
264+
collectionName = "querySelectorAll"
265+
|
266+
(
267+
result = documentRef().getAMethodCall(collectionName) or
268+
result = DataFlow::globalVarRef(collectionName).getACall()
269+
)
270+
)
271+
}
272+
273+
/** Gets a call that creates a DOM node or queries the DOM for a DOM node. */
274+
private DataFlow::SourceNode domElementCreationOrQuery() {
275+
exists(string methodName |
276+
methodName = "createElement" or
277+
methodName = "createElementNS" or
278+
methodName = "createRange" or
279+
methodName = "getElementById" or
280+
methodName = "querySelector"
281+
|
282+
result = documentRef().getAMethodCall(methodName) or
283+
result = DataFlow::globalVarRef(methodName).getACall()
284+
)
285+
}
286+
287+
/** Gets a data flow node that refers directly to a value from the DOM. */
288+
DataFlow::SourceNode domValueSource() {
289+
result.asExpr().(VarAccess).getVariable() instanceof DOMGlobalVariable or
290+
result = domValueRef().getAPropertyRead() or
291+
result = domElementCreationOrQuery() or
292+
result = domElementCollection()
293+
}
294+
295+
/** Gets a data flow node that may refer to a value from the DOM. */
296+
private DataFlow::SourceNode domValueRef(DataFlow::TypeTracker t) {
297+
t.start() and
298+
result = domValueSource()
299+
or
300+
exists(DataFlow::TypeTracker t2 | result = domValueRef(t2).track(t2, t))
301+
}
302+
303+
/** Gets a data flow node that may refer to a value from the DOM. */
304+
DataFlow::SourceNode domValueRef() { result = domValueRef(DataFlow::TypeTracker::end()) }
305+
306+
/** Gets a data flow node that directly refers to a DOM `location` object. */
307+
DataFlow::SourceNode locationSource() {
308+
result = domValueRef().getAPropertyRead("location")
309+
or
310+
result = DataFlow::globalVarRef("location")
311+
}
312+
313+
/** Gets a reference to a DOM `location` object. */
314+
private DataFlow::SourceNode locationRef(DataFlow::TypeTracker t) {
315+
t.start() and
316+
result = locationSource()
317+
or
318+
exists(DataFlow::TypeTracker t2 | result = locationRef(t2).track(t2, t))
319+
}
320+
321+
/** Gets a reference to a DOM `location` object. */
322+
DataFlow::SourceNode locationRef() { result = locationRef(DataFlow::TypeTracker::end()) }
323+
324+
/**
325+
* Gets a reference to the `document` object.
326+
*/
327+
private DataFlow::SourceNode documentRef(DataFlow::TypeTracker t) {
328+
t.start() and
329+
result = DataFlow::globalVarRef("document")
330+
or
331+
exists(DataFlow::TypeTracker t2 | result = documentRef(t2).track(t2, t))
332+
}
333+
334+
/**
335+
* Gets a reference to the 'document' object.
336+
*/
337+
DataFlow::SourceNode documentRef() { result = documentRef(DataFlow::TypeTracker::end()) }
256338
}

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

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -121,10 +121,9 @@ module ClientSideUrlRedirect {
121121
)
122122
or
123123
// A call to `location.replace` or `location.assign`
124-
exists(MethodCallExpr locationCall, string name |
125-
isLocation(locationCall.getReceiver()) and
126-
name = locationCall.getMethodName() and
127-
astNode = locationCall.getArgument(0)
124+
exists(DataFlow::MethodCallNode locationCall, string name |
125+
locationCall = DOM::locationRef().getAMethodCall(name) and
126+
this = locationCall.getArgument(0)
128127
|
129128
name = "replace" or name = "assign"
130129
)
@@ -134,8 +133,7 @@ module ClientSideUrlRedirect {
134133
or
135134
// An assignment to `location.href`, `location.protocol` or `location.hostname`
136135
exists(DataFlow::PropWrite pw, string propName |
137-
isLocation(pw.getBase().asExpr()) and
138-
propName = pw.getPropertyName() and
136+
pw = DOM::locationRef().getAPropertyWrite(propName) and
139137
this = pw.getRhs()
140138
|
141139
propName = "href" or propName = "protocol" or propName = "hostname"

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

Lines changed: 14 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -18,73 +18,36 @@ class DOMGlobalVariable extends GlobalVariable {
1818
}
1919
}
2020

21-
private DataFlow::SourceNode domElementCollection() {
22-
exists(string collectionName |
23-
collectionName = "getElementsByClassName" or
24-
collectionName = "getElementsByName" or
25-
collectionName = "getElementsByTagName" or
26-
collectionName = "getElementsByTagNameNS" or
27-
collectionName = "querySelectorAll"
28-
|
29-
(
30-
result = document().getAMethodCall(collectionName) or
31-
result = DataFlow::globalVarRef(collectionName).getACall()
32-
)
33-
)
34-
}
35-
36-
private DataFlow::SourceNode domElementCreationOrQuery() {
37-
exists(string methodName |
38-
methodName = "createElement" or
39-
methodName = "createElementNS" or
40-
methodName = "createRange" or
41-
methodName = "getElementById" or
42-
methodName = "querySelector"
43-
|
44-
result = document().getAMethodCall(methodName) or
45-
result = DataFlow::globalVarRef(methodName).getACall()
46-
)
47-
}
48-
49-
private DataFlow::SourceNode domValueSource() {
50-
result.asExpr().(VarAccess).getVariable() instanceof DOMGlobalVariable or
51-
result = domValueSource().getAPropertyRead(_) or
52-
result = domElementCreationOrQuery() or
53-
result = domElementCollection()
54-
}
55-
5621
/** Holds if `e` could hold a value that comes from the DOM. */
57-
predicate isDomValue(Expr e) { domValueSource().flowsToExpr(e) }
22+
predicate isDomValue(Expr e) { DOM::domValueRef().flowsToExpr(e) }
5823

5924
/** Holds if `e` could refer to the `location` property of a DOM node. */
6025
predicate isLocation(Expr e) {
61-
e = domValueSource().getAPropertyReference("location").asExpr() or
26+
e = DOM::domValueRef().getAPropertyReference("location").asExpr()
27+
or
6228
e.accessesGlobal("location")
6329
}
6430

6531
/**
6632
* Gets a reference to the 'document' object.
6733
*/
68-
DataFlow::SourceNode document() { result = DataFlow::globalVarRef("document") }
34+
DataFlow::SourceNode document() { result = DOM::documentRef() }
6935

7036
/** Holds if `e` could refer to the `document` object. */
71-
predicate isDocument(Expr e) { document().flowsToExpr(e) }
37+
predicate isDocument(Expr e) { DOM::documentRef().flowsToExpr(e) }
7238

7339
/** Holds if `e` could refer to the document URL. */
7440
predicate isDocumentURL(Expr e) {
75-
exists(Expr base, string propName | e.(PropAccess).accesses(base, propName) |
76-
isDocument(base) and
77-
(
78-
propName = "documentURI" or
79-
propName = "documentURIObject" or
80-
propName = "location" or
81-
propName = "referrer" or
82-
propName = "URL"
83-
)
84-
or
85-
isDomValue(base) and propName = "baseUri"
41+
exists(string propName | e = DOM::documentRef().getAPropertyRead(propName).asExpr() |
42+
propName = "documentURI" or
43+
propName = "documentURIObject" or
44+
propName = "location" or
45+
propName = "referrer" or
46+
propName = "URL"
8647
)
8748
or
49+
e = DOM::domValueRef().getAPropertyRead("baseUri").asExpr()
50+
or
8851
e.accessesGlobal("location")
8952
}
9053

@@ -94,7 +57,7 @@ predicate isDocumentURL(Expr e) {
9457
* `href`, `hash` and `search`.
9558
*/
9659
predicate isSafeLocationProperty(PropAccess pacc) {
97-
exists(Expr loc, string prop | isLocation(loc) and pacc.accesses(loc, prop) |
60+
exists(string prop | pacc = DOM::locationRef().getAPropertyRead(prop).asExpr() |
9861
prop != "href" and prop != "hash" and prop != "search"
9962
)
10063
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ module PropertyInjection {
1414
node = DataFlow::globalObjectRef()
1515
or
1616
// document.write can be accessed
17-
isDocument(node.asExpr())
17+
node = DOM::documentRef()
1818
or
1919
// 'constructor' property leads to the Function constructor.
2020
node.analyze().getAValue() instanceof AbstractCallable

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ module XpathInjection {
7575
class DomXpathSink extends Sink {
7676
DomXpathSink() {
7777
exists(string m | m = "evaluate" or m = "createExpression" |
78-
this = document().getAMethodCall(m).getArgument(0)
78+
this = DOM::documentRef().getAMethodCall(m).getArgument(0)
7979
)
8080
}
8181
}

0 commit comments

Comments
 (0)