Skip to content

Commit 9bea110

Browse files
committed
convert the DOM model to use DataFlow nodes
1 parent 2f429e7 commit 9bea110

File tree

3 files changed

+100
-29
lines changed

3 files changed

+100
-29
lines changed

javascript/ql/lib/semmle/javascript/security/dataflow/ClientSideUrlRedirectCustomizations.qll

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ module ClientSideUrlRedirect {
179179
)
180180
or
181181
// e.g. node.setAttribute("href", sink)
182-
any(DomMethodCallExpr call).interpretsArgumentsAsUrl(this.asExpr())
182+
any(DomMethodCallNode call).interpretsArgumentsAsUrl(this)
183183
}
184184

185185
override predicate isXssSink() { any() }
@@ -191,9 +191,9 @@ module ClientSideUrlRedirect {
191191
*/
192192
class AttributeWriteUrlSink extends ScriptUrlSink, DataFlow::ValueNode {
193193
AttributeWriteUrlSink() {
194-
exists(DomPropWriteNode pw |
194+
exists(DomPropertyWrite pw |
195195
pw.interpretsValueAsJavaScriptUrl() and
196-
this = DataFlow::valueNode(pw.getRhs())
196+
this = pw.getRhs()
197197
)
198198
}
199199

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

Lines changed: 93 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,28 @@ class DomGlobalVariable extends GlobalVariable {
2121
/** DEPRECATED: Alias for DomGlobalVariable */
2222
deprecated class DOMGlobalVariable = DomGlobalVariable;
2323

24-
/** Holds if `e` could hold a value that comes from the DOM. */
25-
predicate isDomValue(Expr e) { DOM::domValueRef().flowsToExpr(e) }
24+
/**
25+
* DEPRECATED: Use `isDomNode` instead.
26+
* Holds if `e` could hold a value that comes from the DOM.
27+
*/
28+
deprecated predicate isDomValue(Expr e) { isDomNode(e.flow()) }
29+
30+
/**
31+
* Holds if `e` could hold a value that comes from the DOM.
32+
*/
33+
predicate isDomNode(DataFlow::Node e) { DOM::domValueRef().flowsTo(e) }
34+
35+
/**
36+
* DEPRECATED: Use `isLocationNode` instead.
37+
* Holds if `e` could refer to the `location` property of a DOM node.
38+
*/
39+
deprecated predicate isLocation(Expr e) { isLocationNode(e.flow()) }
2640

2741
/** Holds if `e` could refer to the `location` property of a DOM node. */
28-
predicate isLocation(Expr e) {
29-
e = DOM::domValueRef().getAPropertyReference("location").asExpr()
42+
predicate isLocationNode(DataFlow::Node e) {
43+
e = DOM::domValueRef().getAPropertyReference("location")
3044
or
31-
e.accessesGlobal("location")
45+
e = DataFlow::globalVarRef("location")
3246
}
3347

3448
/**
@@ -53,15 +67,52 @@ deprecated predicate isDocumentUrl(Expr e) { e.flow() = DOM::locationSource() }
5367
deprecated predicate isDocumentURL = isDocumentUrl/1;
5468

5569
/**
70+
* DEPRECATED. In most cases, a sanitizer based on this predicate can be removed, as
71+
* taint tracking no longer step through the properties of the location object by default.
72+
*
73+
* Holds if `pacc` accesses a part of `document.location` that is
74+
* not considered user-controlled, that is, anything except
75+
* `href`, `hash` and `search`.
76+
*/
77+
deprecated predicate isSafeLocationProperty(PropAccess pacc) {
78+
exists(string prop | pacc = DOM::locationRef().getAPropertyRead(prop).asExpr() |
79+
prop != "href" and prop != "hash" and prop != "search"
80+
)
81+
}
82+
83+
/**
84+
* DEPRECATED: Use `DomMethodCallNode` instead.
5685
* A call to a DOM method.
5786
*/
58-
class DomMethodCallExpr extends MethodCallExpr {
59-
DomMethodCallExpr() { isDomValue(this.getReceiver()) }
87+
deprecated class DomMethodCallExpr extends MethodCallExpr {
88+
DomMethodCallNode node;
89+
90+
DomMethodCallExpr() { this.flow() = node }
91+
92+
/** Holds if `arg` is an argument that is interpreted as HTML. */
93+
deprecated predicate interpretsArgumentsAsHtml(Expr arg) {
94+
node.interpretsArgumentsAsHtml(arg.flow())
95+
}
96+
97+
/** Holds if `arg` is an argument that is used as an URL. */
98+
deprecated predicate interpretsArgumentsAsURL(Expr arg) {
99+
node.interpretsArgumentsAsURL(arg.flow())
100+
}
101+
102+
/** DEPRECATED: Alias for interpretsArgumentsAsHtml */
103+
deprecated predicate interpretsArgumentsAsHTML(Expr arg) { this.interpretsArgumentsAsHtml(arg) }
104+
}
105+
106+
/**
107+
* A call to a DOM method.
108+
*/
109+
class DomMethodCallNode extends DataFlow::MethodCallNode {
110+
DomMethodCallNode() { isDomNode(this.getReceiver()) }
60111

61112
/**
62113
* Holds if `arg` is an argument that is interpreted as HTML.
63114
*/
64-
predicate interpretsArgumentsAsHtml(Expr arg) {
115+
predicate interpretsArgumentsAsHtml(DataFlow::Node arg) {
65116
exists(int argPos, string name |
66117
arg = this.getArgument(argPos) and
67118
name = this.getMethodName()
@@ -86,7 +137,7 @@ class DomMethodCallExpr extends MethodCallExpr {
86137
/**
87138
* Holds if `arg` is an argument that is used as an URL.
88139
*/
89-
predicate interpretsArgumentsAsUrl(Expr arg) {
140+
predicate interpretsArgumentsAsUrl(DataFlow::Node arg) {
90141
exists(int argPos, string name |
91142
arg = this.getArgument(argPos) and
92143
name = this.getMethodName()
@@ -104,40 +155,60 @@ class DomMethodCallExpr extends MethodCallExpr {
104155
}
105156

106157
/** DEPRECATED: Alias for interpretsArgumentsAsUrl */
107-
deprecated predicate interpretsArgumentsAsURL(Expr arg) { this.interpretsArgumentsAsUrl(arg) }
158+
deprecated predicate interpretsArgumentsAsURL(DataFlow::Node arg) { this.interpretsArgumentsAsUrl(arg) }
108159

109160
/** DEPRECATED: Alias for interpretsArgumentsAsHtml */
110-
deprecated predicate interpretsArgumentsAsHTML(Expr arg) { this.interpretsArgumentsAsHtml(arg) }
161+
deprecated predicate interpretsArgumentsAsHTML(DataFlow::Node arg) { this.interpretsArgumentsAsHtml(arg) }
111162
}
112163

113164
/**
165+
* DEPRECATED: Use `DomPropertyWrite` instead.
114166
* An assignment to a property of a DOM object.
115167
*/
116-
class DomPropWriteNode extends Assignment {
117-
PropAccess lhs;
168+
deprecated class DomPropWriteNode extends Assignment {
169+
DomPropertyWrite node;
118170

119-
DomPropWriteNode() {
120-
lhs = this.getLhs() and
121-
isDomValue(lhs.getBase())
122-
}
171+
DomPropWriteNode() { this.flow() = node }
123172

124173
/**
125174
* Holds if the assigned value is interpreted as HTML.
126175
*/
127-
predicate interpretsValueAsHtml() {
128-
lhs.getPropertyName() = "innerHTML" or
129-
lhs.getPropertyName() = "outerHTML"
130-
}
176+
predicate interpretsValueAsHtml() { node.interpretsValueAsHtml() }
131177

132178
/** DEPRECATED: Alias for interpretsValueAsHtml */
133179
deprecated predicate interpretsValueAsHTML() { this.interpretsValueAsHtml() }
134180

181+
/**
182+
* Holds if the assigned value is interpreted as JavaScript via javascript: protocol.
183+
*/
184+
predicate interpretsValueAsJavaScriptUrl() { node.interpretsValueAsJavaScriptUrl() }
185+
}
186+
187+
/**
188+
* An assignment to a property of a DOM object.
189+
*/
190+
class DomPropertyWrite extends DataFlow::Node instanceof DataFlow::PropWrite {
191+
DomPropertyWrite() { isDomNode(super.getBase()) }
192+
193+
/**
194+
* Holds if the assigned value is interpreted as HTML.
195+
*/
196+
predicate interpretsValueAsHtml() {
197+
super.getPropertyName() = "innerHTML" or
198+
super.getPropertyName() = "outerHTML"
199+
}
200+
135201
/**
136202
* Holds if the assigned value is interpreted as JavaScript via javascript: protocol.
137203
*/
138204
predicate interpretsValueAsJavaScriptUrl() {
139-
lhs.getPropertyName() = DOM::getAPropertyNameInterpretedAsJavaScriptUrl()
205+
super.getPropertyName() = DOM::getAPropertyNameInterpretedAsJavaScriptUrl()
140206
}
207+
208+
/**
209+
* Gets the data flow node corresponding to the value being written,
210+
*/
211+
DataFlow::Node getRhs() { result = super.getRhs() }
141212
}
142213

143214
/**

javascript/ql/lib/semmle/javascript/security/dataflow/DomBasedXssCustomizations.qll

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,12 +130,12 @@ module DomBasedXss {
130130
class DomSink extends Sink {
131131
DomSink() {
132132
// Call to a DOM function that inserts its argument into the DOM
133-
any(DomMethodCallExpr call).interpretsArgumentsAsHtml(this.asExpr())
133+
any(DomMethodCallNode call).interpretsArgumentsAsHtml(this)
134134
or
135135
// Assignment to a dangerous DOM property
136-
exists(DomPropWriteNode pw |
136+
exists(DomPropertyWrite pw |
137137
pw.interpretsValueAsHtml() and
138-
this = DataFlow::valueNode(pw.getRhs())
138+
this = pw.getRhs()
139139
)
140140
or
141141
// `html` or `source.html` properties of React Native `WebView`
@@ -159,7 +159,7 @@ module DomBasedXss {
159159
)
160160
or
161161
exists(DataFlow::MethodCallNode ccf |
162-
isDomValue(ccf.getReceiver().asExpr()) and
162+
isDomNode(ccf.getReceiver()) and
163163
ccf.getMethodName() = "createContextualFragment" and
164164
this = ccf.getArgument(0)
165165
)

0 commit comments

Comments
 (0)