Skip to content

Commit bad5465

Browse files
author
Max Schaefer
authored
Merge pull request #1360 from asger-semmle/customize-window-document
JS: Make some DOM concepts customizable
2 parents d2fa7aa + 9046fd1 commit bad5465

File tree

14 files changed

+106
-36
lines changed

14 files changed

+106
-36
lines changed

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

Lines changed: 64 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -284,12 +284,25 @@ module DOM {
284284
)
285285
}
286286

287+
module DomValueSource {
288+
/**
289+
* A data flow node that should be considered a source of DOM values.
290+
*/
291+
abstract class Range extends DataFlow::Node {}
292+
293+
private class DefaultRange extends Range {
294+
DefaultRange() {
295+
this.asExpr().(VarAccess).getVariable() instanceof DOMGlobalVariable or
296+
this = domValueRef().getAPropertyRead() or
297+
this = domElementCreationOrQuery() or
298+
this = domElementCollection()
299+
}
300+
}
301+
}
302+
287303
/** Gets a data flow node that refers directly to a value from the DOM. */
288304
DataFlow::SourceNode domValueSource() {
289-
result.asExpr().(VarAccess).getVariable() instanceof DOMGlobalVariable or
290-
result = domValueRef().getAPropertyRead() or
291-
result = domElementCreationOrQuery() or
292-
result = domElementCollection()
305+
result instanceof DomValueSource::Range
293306
}
294307

295308
/** Gets a data flow node that may refer to a value from the DOM. */
@@ -303,11 +316,34 @@ module DOM {
303316
/** Gets a data flow node that may refer to a value from the DOM. */
304317
DataFlow::SourceNode domValueRef() { result = domValueRef(DataFlow::TypeTracker::end()) }
305318

319+
module LocationSource {
320+
/**
321+
* A data flow node that should be considered a source of the DOM `location` object.
322+
*
323+
* Can be subclassed to add additional such nodes.
324+
*/
325+
abstract class Range extends DataFlow::Node {}
326+
327+
private class DefaultRange extends Range {
328+
DefaultRange() {
329+
exists(string propName | this = documentRef().getAPropertyRead(propName) |
330+
propName = "documentURI" or
331+
propName = "documentURIObject" or
332+
propName = "location" or
333+
propName = "referrer" or
334+
propName = "URL"
335+
)
336+
or
337+
this = DOM::domValueRef().getAPropertyRead("baseUri")
338+
or
339+
this = DataFlow::globalVarRef("location")
340+
}
341+
}
342+
}
343+
306344
/** Gets a data flow node that directly refers to a DOM `location` object. */
307345
DataFlow::SourceNode locationSource() {
308-
result = domValueRef().getAPropertyRead("location")
309-
or
310-
result = DataFlow::globalVarRef("location")
346+
result instanceof LocationSource::Range
311347
}
312348

313349
/** Gets a reference to a DOM `location` object. */
@@ -321,12 +357,32 @@ module DOM {
321357
/** Gets a reference to a DOM `location` object. */
322358
DataFlow::SourceNode locationRef() { result = locationRef(DataFlow::TypeTracker::end()) }
323359

360+
module DocumentSource {
361+
/**
362+
* A data flow node that should be considered a source of the `document` object.
363+
*
364+
* Can be subclassed to add additional such nodes.
365+
*/
366+
abstract class Range extends DataFlow::Node {}
367+
368+
private class DefaultRange extends Range {
369+
DefaultRange() { this = DataFlow::globalVarRef("document") }
370+
}
371+
}
372+
373+
/**
374+
* Gets a direct reference to the `document` object.
375+
*/
376+
DataFlow::SourceNode documentSource() {
377+
result instanceof DocumentSource::Range
378+
}
379+
324380
/**
325381
* Gets a reference to the `document` object.
326382
*/
327383
private DataFlow::SourceNode documentRef(DataFlow::TypeTracker t) {
328384
t.start() and
329-
result = DataFlow::globalVarRef("document")
385+
result instanceof DocumentSource::Range
330386
or
331387
exists(DataFlow::TypeTracker t2 | result = documentRef(t2).track(t2, t))
332388
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ module ClientSideUrlRedirect {
4040
override predicate isSource(DataFlow::Node source) { source instanceof Source }
4141

4242
override predicate isSource(DataFlow::Node source, DataFlow::FlowLabel lbl) {
43-
isDocumentURL(source.asExpr()) and
43+
source = DOM::locationSource() and
4444
lbl instanceof DocumentUrl
4545
}
4646

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ module CodeInjection {
5151
/**
5252
* An access to a property that may hold (parts of) the document URL.
5353
*/
54-
class LocationSource extends Source, DataFlow::ValueNode {
55-
LocationSource() { isDocumentURL(astNode) }
54+
class LocationSource extends Source {
55+
LocationSource() { this = DOM::locationSource() }
5656
}
5757

5858
/**

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

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,7 @@ predicate isDocument(Expr e) { DOM::documentRef().flowsToExpr(e) }
3838

3939
/** Holds if `e` could refer to the document URL. */
4040
predicate isDocumentURL(Expr e) {
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"
47-
)
48-
or
49-
e = DOM::domValueRef().getAPropertyRead("baseUri").asExpr()
50-
or
51-
e.accessesGlobal("location")
41+
e.flow() = DOM::locationSource()
5242
}
5343

5444
/**

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ module DomBasedXss {
4242
/**
4343
* An access of the URL of this page, or of the referrer to this page.
4444
*/
45-
class LocationSource extends Source, DataFlow::ValueNode {
46-
LocationSource() { isDocumentURL(astNode) }
45+
class LocationSource extends Source {
46+
LocationSource() { this = DOM::locationSource() }
4747
}
4848
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ module UnsafeDynamicMethodAccess {
110110
* The page URL considered as a flow source for unsafe dynamic method access.
111111
*/
112112
class DocumentUrlAsSource extends Source {
113-
DocumentUrlAsSource() { isDocumentURL(asExpr()) }
113+
DocumentUrlAsSource() { this = DOM::locationSource() }
114114
}
115115

116116
/**

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ module UnvalidatedDynamicMethodCall {
102102
* The page URL considered as a flow source for unvalidated dynamic method calls.
103103
*/
104104
class DocumentUrlAsSource extends Source {
105-
DocumentUrlAsSource() { isDocumentURL(asExpr()) }
105+
DocumentUrlAsSource() { this = DOM::locationSource() }
106106
}
107107

108108
/**

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ module XpathInjection {
6464
}
6565

6666
/** A part of the document URL, considered as a flow source for XPath injection. */
67-
class DocumentUrlSource extends Source, DataFlow::ValueNode {
68-
DocumentUrlSource() { isDocumentURL(astNode) }
67+
class DocumentUrlSource extends Source {
68+
DocumentUrlSource() { this = DOM::locationSource() }
6969
}
7070

7171
/**

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ module DomBasedXss {
7070
strval = prefix.getStringValue() and
7171
not strval.regexpMatch("\\s*<.*")
7272
) and
73-
not isDocumentURL(astNode)
73+
not DOM::locationRef().flowsTo(this)
7474
)
7575
or
7676
// call to an Angular method that interprets its argument as HTML
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
test_documentRef
2+
| customization.js:2:13:2:31 | customGetDocument() |
3+
test_locationRef
4+
| customization.js:3:3:3:14 | doc.location |
5+
test_domValueRef
6+
| customization.js:4:3:4:28 | doc.get ... 'test') |

0 commit comments

Comments
 (0)