Skip to content

Commit 084159d

Browse files
author
Max Schaefer
committed
JavaScript: Teach type trackers to track flow through one level of properties.
1 parent 9fbc0eb commit 084159d

File tree

13 files changed

+128
-92
lines changed

13 files changed

+128
-92
lines changed

javascript/ql/src/semmle/javascript/dataflow/TypeTracking.qll

Lines changed: 87 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,19 @@
99
import javascript
1010
private import internal.FlowSteps
1111

12+
private class PropertyName extends string {
13+
PropertyName() { this = any(DataFlow::PropRef pr).getPropertyName() }
14+
}
15+
1216
/**
1317
* A description of a step on an inter-procedural data flow path.
1418
*/
1519
private newtype TStepSummary =
1620
LevelStep() or
1721
CallStep() or
18-
ReturnStep()
22+
ReturnStep() or
23+
StoreStep(PropertyName prop) or
24+
LoadStep(PropertyName prop)
1925

2026
/**
2127
* INTERNAL: Use `TypeTracker` or `TypeBackTracker` instead.
@@ -64,6 +70,14 @@ module StepSummary {
6470
// Flow through an instance field between members of the same class
6571
DataFlow::localFieldStep(predNode, succ) and
6672
summary = LevelStep()
73+
or
74+
exists(string prop |
75+
basicStoreStep(predNode, succ, prop) and
76+
summary = StoreStep(prop)
77+
or
78+
loadStep(predNode, succ, prop) and
79+
summary = LoadStep(prop)
80+
)
6781
)
6882
}
6983

@@ -73,8 +87,22 @@ module StepSummary {
7387
* Appends a step summary onto a type-tracking summary.
7488
*/
7589
TypeTracker append(TypeTracker type, StepSummary summary) {
76-
not (type.hasCall() = true and summary.hasReturn() = true) and
77-
result.hasCall() = type.hasCall().booleanOr(summary.hasCall())
90+
exists(boolean hadCall, boolean hasCall, string oldProp, string newProp |
91+
hadCall = type.hasCall() and
92+
oldProp = type.getProp()
93+
|
94+
not (hadCall = true and summary.hasReturn() = true) and
95+
hasCall = hadCall.booleanOr(summary.hasCall()) and
96+
(
97+
if summary instanceof StoreStep
98+
then oldProp = "" and summary = StoreStep(newProp)
99+
else
100+
if summary instanceof LoadStep
101+
then summary = LoadStep(oldProp) and newProp = ""
102+
else newProp = oldProp
103+
) and
104+
result = MkTypeTracker(hasCall, newProp)
105+
)
78106
}
79107

80108
/**
@@ -83,12 +111,27 @@ module StepSummary {
83111
* Prepends a step summary before a backwards type-tracking summary.
84112
*/
85113
TypeBackTracker prepend(StepSummary summary, TypeBackTracker type) {
86-
not (type.hasReturn() = true and summary.hasCall() = true) and
87-
result.hasReturn() = type.hasReturn().booleanOr(summary.hasReturn())
114+
exists(boolean hadReturn, boolean hasReturn, string oldProp, string newProp |
115+
hadReturn = type.hasReturn() and
116+
oldProp = type.getProp()
117+
|
118+
not (hadReturn = true and summary.hasCall() = true) and
119+
hasReturn = hadReturn.booleanOr(summary.hasReturn()) and
120+
(
121+
if summary instanceof StoreStep
122+
then summary = StoreStep(oldProp) and newProp = ""
123+
else
124+
if summary instanceof LoadStep
125+
then oldProp = "" and summary = LoadStep(newProp)
126+
else newProp = oldProp
127+
) and
128+
result = MkTypeBackTracker(hasReturn, newProp)
129+
)
88130
}
89131
}
90132

91-
private newtype TTypeTracker = MkTypeTracker(Boolean hasCall)
133+
private newtype TTypeTracker =
134+
MkTypeTracker(Boolean hasCall, string prop) { prop = "" or prop instanceof PropertyName }
92135

93136
/**
94137
* EXPERIMENTAL.
@@ -112,7 +155,7 @@ private newtype TTypeTracker = MkTypeTracker(Boolean hasCall)
112155
* )
113156
* }
114157
*
115-
* DataFlow::SourceNode myType() { result = myType(_) }
158+
* DataFlow::SourceNode myType() { result = myType(DataFlow::TypeTracker::end()) }
116159
* ```
117160
*
118161
* To track values backwards, which can be useful for tracking
@@ -121,28 +164,41 @@ private newtype TTypeTracker = MkTypeTracker(Boolean hasCall)
121164
class TypeTracker extends TTypeTracker {
122165
Boolean hasCall;
123166

124-
TypeTracker() { this = MkTypeTracker(hasCall) }
167+
string prop;
168+
169+
TypeTracker() { this = MkTypeTracker(hasCall, prop) }
125170

126171
string toString() {
127-
hasCall = true and result = "type tracker with call steps"
128-
or
129-
hasCall = false and result = "type tracker without call steps"
172+
exists(string withCall, string withProp |
173+
(if hasCall = true then withCall = "with" else withCall = "without") and
174+
(if prop != "" then withProp = " with property " + prop else withProp = "") and
175+
result = "type tracker " + withCall + " call steps" + withProp
176+
)
130177
}
131178

132179
/**
133180
* Holds if this is the starting point of type tracking.
134181
*/
135-
predicate start() { hasCall = false }
182+
predicate start() { hasCall = false and prop = "" }
183+
184+
predicate end() { prop = "" }
136185

137186
/**
138187
* INTERNAL. DO NOT USE.
139188
*
140189
* Holds if this type has been tracked into a call.
141190
*/
142191
boolean hasCall() { result = hasCall }
192+
193+
string getProp() { result = prop }
143194
}
144195

145-
private newtype TTypeBackTracker = MkTypeBackTracker(Boolean hasReturn)
196+
module TypeTracker {
197+
TypeTracker end() { result.end() }
198+
}
199+
200+
private newtype TTypeBackTracker =
201+
MkTypeBackTracker(Boolean hasReturn, string prop) { prop = "" or prop instanceof PropertyName }
146202

147203
/**
148204
* EXPERIMENTAL.
@@ -168,29 +224,41 @@ private newtype TTypeBackTracker = MkTypeBackTracker(Boolean hasReturn)
168224
* )
169225
* }
170226
*
171-
* DataFlow::SourceNode myCallback() { result = myCallback(_) }
227+
* DataFlow::SourceNode myCallback() { result = myCallback(DataFlow::TypeBackTracker::end()) }
172228
* ```
173229
*/
174230
class TypeBackTracker extends TTypeBackTracker {
175231
Boolean hasReturn;
176232

177-
TypeBackTracker() { this = MkTypeBackTracker(hasReturn) }
233+
string prop;
234+
235+
TypeBackTracker() { this = MkTypeBackTracker(hasReturn, prop) }
178236

179237
string toString() {
180-
hasReturn = true and result = "type back-tracker with return steps"
181-
or
182-
hasReturn = false and result = "type back-tracker without return steps"
238+
exists(string withReturn, string withProp |
239+
(if hasReturn = true then withReturn = "with" else withReturn = "without") and
240+
(if prop != "" then withProp = " with property " + prop else withProp = "") and
241+
result = "type back-tracker " + withReturn + " return steps" + withProp
242+
)
183243
}
184244

185245
/**
186246
* Holds if this is the starting point of type tracking.
187247
*/
188-
predicate start() { hasReturn = false }
248+
predicate start() { hasReturn = false and prop = "" }
249+
250+
predicate end() { prop = "" }
189251

190252
/**
191253
* INTERNAL. DO NOT USE.
192254
*
193255
* Holds if this type has been back-tracked into a call through return edge.
194256
*/
195257
boolean hasReturn() { result = hasReturn }
258+
259+
string getProp() { result = prop }
260+
}
261+
262+
module TypeBackTracker {
263+
TypeBackTracker end() { result.end() }
196264
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ module Connect {
118118
}
119119

120120
override DataFlow::SourceNode getARouteHandler() {
121-
result = getARouteHandler(_)
121+
result = getARouteHandler(DataFlow::TypeBackTracker::end())
122122
}
123123

124124
private DataFlow::SourceNode getARouteHandler(DataFlow::TypeBackTracker t) {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ module Electron {
6969
* A data flow node whose value may originate from a browser object instantiation.
7070
*/
7171
private class BrowserObjectByFlow extends BrowserObject {
72-
BrowserObjectByFlow() { browserObject(_).flowsTo(this) }
72+
BrowserObjectByFlow() { browserObject(DataFlow::TypeTracker::end()).flowsTo(this) }
7373
}
7474

7575
/**

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ module Express {
111111
Expr getLastRouteHandlerExpr() { result = max(int i | | getRouteHandlerExpr(i) order by i) }
112112

113113
override DataFlow::SourceNode getARouteHandler() {
114-
result = getARouteHandler(_)
114+
result = getARouteHandler(DataFlow::TypeBackTracker::end())
115115
}
116116

117117
private DataFlow::SourceNode getARouteHandler(DataFlow::TypeBackTracker t) {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ module HTTP {
282282
*/
283283
abstract RouteHandler getRouteHandler();
284284

285-
predicate flowsTo(DataFlow::Node nd) { flowsToSourceNode(_).flowsTo(nd) }
285+
predicate flowsTo(DataFlow::Node nd) { flowsToSourceNode(DataFlow::TypeTracker::end()).flowsTo(nd) }
286286

287287
private DataFlow::SourceNode flowsToSourceNode(DataFlow::TypeTracker t) {
288288
t.start() and
@@ -303,7 +303,7 @@ module HTTP {
303303
*/
304304
abstract RouteHandler getRouteHandler();
305305

306-
predicate flowsTo(DataFlow::Node nd) { flowsToSourceNode(_).flowsTo(nd) }
306+
predicate flowsTo(DataFlow::Node nd) { flowsToSourceNode(DataFlow::TypeTracker::end()).flowsTo(nd) }
307307

308308
private DataFlow::SourceNode flowsToSourceNode(DataFlow::TypeTracker t) {
309309
t.start() and

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ module Hapi {
193193
}
194194

195195
override DataFlow::SourceNode getARouteHandler() {
196-
result = getARouteHandler(_)
196+
result = getARouteHandler(DataFlow::TypeBackTracker::end())
197197
}
198198

199199
private DataFlow::SourceNode getARouteHandler(DataFlow::TypeBackTracker t) {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ module Koa {
7979
RouteHandler getRouteHandler() { result = rh }
8080

8181
predicate flowsTo(DataFlow::Node nd) {
82-
flowsToSourceNode(_).flowsTo(nd)
82+
flowsToSourceNode(DataFlow::TypeTracker::end()).flowsTo(nd)
8383
}
8484

8585
private DataFlow::SourceNode flowsToSourceNode(DataFlow::TypeTracker t) {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ module NodeJSLib {
189189
}
190190

191191
override DataFlow::SourceNode getARouteHandler() {
192-
result = getARouteHandler(_)
192+
result = getARouteHandler(DataFlow::TypeBackTracker::end())
193193
}
194194

195195
private DataFlow::SourceNode getARouteHandler(DataFlow::TypeBackTracker t) {

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ module SocketIO {
6161
class ServerNode extends DataFlow::SourceNode {
6262
ServerObject srv;
6363

64-
ServerNode() { this = server(srv, _) }
64+
ServerNode() { this = server(srv, DataFlow::TypeTracker::end()) }
6565

6666
/** Gets the server to which this node refers. */
6767
ServerObject getServer() { result = srv }
@@ -130,7 +130,7 @@ module SocketIO {
130130
class NamespaceNode extends DataFlow::SourceNode {
131131
NamespaceObject ns;
132132

133-
NamespaceNode() { this = namespace(ns, _) }
133+
NamespaceNode() { this = namespace(ns, DataFlow::TypeTracker::end()) }
134134

135135
/** Gets the namespace to which this node refers. */
136136
NamespaceObject getNamespace() { result = ns }
@@ -194,7 +194,7 @@ module SocketIO {
194194
class SocketNode extends DataFlow::SourceNode {
195195
NamespaceObject ns;
196196

197-
SocketNode() { this = socket(ns, _) }
197+
SocketNode() { this = socket(ns, DataFlow::TypeTracker::end()) }
198198

199199
/** Gets the namespace to which this socket belongs. */
200200
NamespaceObject getNamespace() { result = ns }
@@ -417,7 +417,7 @@ module SocketIOClient {
417417
class SocketNode extends DataFlow::SourceNode {
418418
DataFlow::InvokeNode invk;
419419

420-
SocketNode() { this = socket(invk, _) }
420+
SocketNode() { this = socket(invk, DataFlow::TypeTracker::end()) }
421421

422422
/** Gets the path of the namespace this socket belongs to, if it can be determined. */
423423
string getNamespacePath() {

0 commit comments

Comments
 (0)