33 */
44
55import javascript
6+ private import dataflow.internal.StepSummary
67
78/**
89 * A definition of a `Promise` object.
@@ -121,46 +122,166 @@ class AggregateES2015PromiseDefinition extends PromiseCreationCall {
121122}
122123
123124/**
124- * This module defines how data-flow propagates into and out of a Promise.
125- * The data-flow is based on pseudo-properties rather than tainting the Promise object (which is what `PromiseTaintStep` does).
125+ * Common predicates shared between type-tracking and data-flow for promises.
126126 */
127- private module PromiseFlow {
127+ module Promises {
128128 /**
129129 * Gets the pseudo-field used to describe resolved values in a promise.
130130 */
131- string resolveField ( ) { result = "$PromiseResolveField$" }
131+ string valueProp ( ) { result = "$PromiseResolveField$" }
132132
133133 /**
134134 * Gets the pseudo-field used to describe rejected values in a promise.
135135 */
136- string rejectField ( ) { result = "$PromiseRejectField$" }
136+ string errorProp ( ) { result = "$PromiseRejectField$" }
137+ }
138+
139+ /**
140+ * A module for supporting promises in type-tracking predicates.
141+ * The `PromiseTypeTracking::promiseStep` predicate is used for type tracking in and out of promises,
142+ * and is included in the standard type-tracking steps (`SourceNode::track`).
143+ * The `TypeTracker::startInPromise()` predicate can be used to initiate a type-tracker
144+ * where the tracked value is a promise.
145+ *
146+ * The below is an example of a type-tracking predicate where the initial value is a promise:
147+ * ```
148+ * DataFlow::SourceNode myType(DataFlow::TypeTracker t) {
149+ * t.startInPromise() and
150+ * result = <the promise value> and
151+ * or
152+ * exists(DataFlow::TypeTracker t2 | result = myType(t2).track(t2, t))
153+ * }
154+ * ```
155+ *
156+ * The type-tracking predicate above will only end (`t = DataFlow::TypeTracker::end()`) after the tracked value has been
157+ * extracted from the promise.
158+ *
159+ * The `PromiseTypeTracking::promiseStep` predicate can be used instead of `SourceNode::track`
160+ * to get type-tracking only for promise steps.
161+ *
162+ * Replace `t.startInPromise()` in the above example with `t.start()` to create a type-tracking predicate
163+ * where the value is not initially inside a promise.
164+ */
165+ module PromiseTypeTracking {
166+ /**
167+ * Gets the result from a single step through a promise, from `pred` to `result` summarized by `summary`.
168+ * This can be loading a resolved value from a promise, storing a value in a promise, or copying a resolved value from one promise to another.
169+ */
170+ DataFlow:: SourceNode promiseStep ( DataFlow:: SourceNode pred , StepSummary summary ) {
171+ exists ( PromiseFlowStep step , string field | field = Promises:: valueProp ( ) |
172+ summary = LoadStep ( field ) and
173+ step .load ( pred , result , field )
174+ or
175+ summary = StoreStep ( field ) and
176+ step .store ( pred , result , field )
177+ or
178+ summary = LevelStep ( ) and
179+ step .loadStore ( pred , result , field )
180+ )
181+ }
182+
183+ /**
184+ * Gets the result from a single step through a promise, from `pred` with tracker `t2` to `result` with tracker `t`.
185+ * This can be loading a resolved value from a promise, storing a value in a promise, or copying a resolved value from one promise to another.
186+ */
187+ DataFlow:: SourceNode promiseStep (
188+ DataFlow:: SourceNode pred , DataFlow:: TypeTracker t , DataFlow:: TypeTracker t2
189+ ) {
190+ exists ( StepSummary summary |
191+ result = PromiseTypeTracking:: promiseStep ( pred , summary ) and
192+ t = t2 .append ( summary )
193+ )
194+ }
195+
196+ /**
197+ * A class enabling the use of the `resolveField` as a pseudo-property in type-tracking predicates.
198+ */
199+ private class ResolveFieldAsTypeTrackingProperty extends TypeTrackingPseudoProperty {
200+ ResolveFieldAsTypeTrackingProperty ( ) { this = Promises:: valueProp ( ) }
201+ }
202+ }
203+
204+ /**
205+ * An `AdditionalFlowStep` used to model a data-flow step related to promises.
206+ *
207+ * The `loadStep`/`storeStep`/`loadStoreStep` methods are overloaded such that the new predicates
208+ * `load`/`store`/`loadStore` can be used in the `PromiseTypeTracking` module.
209+ * (Thereby avoiding conflicts with a "cousin" `AdditionalFlowStep` implementation.)
210+ *
211+ * The class is private and is only intended to be used inside the `PromiseTypeTracking` and `PromiseFlow` modules.
212+ */
213+ abstract private class PromiseFlowStep extends DataFlow:: AdditionalFlowStep {
214+ final override predicate step ( DataFlow:: Node pred , DataFlow:: Node succ ) { none ( ) }
215+
216+ final override predicate step (
217+ DataFlow:: Node p , DataFlow:: Node s , DataFlow:: FlowLabel pl , DataFlow:: FlowLabel sl
218+ ) {
219+ none ( )
220+ }
221+
222+ /**
223+ * Holds if the property `prop` of the object `pred` should be loaded into `succ`.
224+ */
225+ predicate load ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) { none ( ) }
226+
227+ final override predicate loadStep ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
228+ this .load ( pred , succ , prop )
229+ }
230+
231+ /**
232+ * Holds if `pred` should be stored in the object `succ` under the property `prop`.
233+ */
234+ predicate store ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) { none ( ) }
235+
236+ final override predicate storeStep ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
237+ this .store ( pred , succ , prop )
238+ }
239+
240+ /**
241+ * Holds if the property `prop` should be copied from the object `pred` to the object `succ`.
242+ */
243+ predicate loadStore ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) { none ( ) }
244+
245+ final override predicate loadStoreStep ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
246+ this .loadStore ( pred , succ , prop )
247+ }
248+ }
249+
250+ /**
251+ * This module defines how data-flow propagates into and out of a Promise.
252+ * The data-flow is based on pseudo-properties rather than tainting the Promise object (which is what `PromiseTaintStep` does).
253+ */
254+ private module PromiseFlow {
255+ private predicate valueProp = Promises:: valueProp / 0 ;
256+
257+ private predicate errorProp = Promises:: errorProp / 0 ;
137258
138259 /**
139260 * A flow step describing a promise definition.
140261 *
141262 * The resolved/rejected value is written to a pseudo-field on the promise.
142263 */
143- class PromiseDefitionStep extends DataFlow :: AdditionalFlowStep {
264+ class PromiseDefitionStep extends PromiseFlowStep {
144265 PromiseDefinition promise ;
145266
146267 PromiseDefitionStep ( ) { this = promise }
147268
148- override predicate storeStep ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
149- prop = resolveField ( ) and
269+ override predicate store ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
270+ prop = valueProp ( ) and
150271 pred = promise .getResolveParameter ( ) .getACall ( ) .getArgument ( 0 ) and
151272 succ = this
152273 or
153- prop = rejectField ( ) and
274+ prop = errorProp ( ) and
154275 (
155276 pred = promise .getRejectParameter ( ) .getACall ( ) .getArgument ( 0 ) or
156277 pred = promise .getExecutor ( ) .getExceptionalReturn ( )
157278 ) and
158279 succ = this
159280 }
160281
161- override predicate loadStoreStep ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
282+ override predicate loadStore ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
162283 // Copy the value of a resolved promise to the value of this promise.
163- prop = resolveField ( ) and
284+ prop = valueProp ( ) and
164285 pred = promise .getResolveParameter ( ) .getACall ( ) .getArgument ( 0 ) and
165286 succ = this
166287 }
@@ -169,20 +290,20 @@ private module PromiseFlow {
169290 /**
170291 * A flow step describing the a Promise.resolve (and similar) call.
171292 */
172- class CreationStep extends DataFlow :: AdditionalFlowStep {
293+ class CreationStep extends PromiseFlowStep {
173294 PromiseCreationCall promise ;
174295
175296 CreationStep ( ) { this = promise }
176297
177- override predicate storeStep ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
178- prop = resolveField ( ) and
298+ override predicate store ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
299+ prop = valueProp ( ) and
179300 pred = promise .getValue ( ) and
180301 succ = this
181302 }
182303
183- override predicate loadStoreStep ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
304+ override predicate loadStore ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
184305 // Copy the value of a resolved promise to the value of this promise.
185- prop = resolveField ( ) and
306+ prop = valueProp ( ) and
186307 pred = promise .getValue ( ) and
187308 succ = this
188309 }
@@ -192,7 +313,7 @@ private module PromiseFlow {
192313 * A load step loading the pseudo-field describing that the promise is rejected.
193314 * The rejected value is thrown as a exception.
194315 */
195- class AwaitStep extends DataFlow :: AdditionalFlowStep {
316+ class AwaitStep extends PromiseFlowStep {
196317 DataFlow:: Node operand ;
197318 AwaitExpr await ;
198319
@@ -201,12 +322,12 @@ private module PromiseFlow {
201322 operand .getEnclosingExpr ( ) = await .getOperand ( )
202323 }
203324
204- override predicate loadStep ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
205- prop = resolveField ( ) and
325+ override predicate load ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
326+ prop = valueProp ( ) and
206327 succ = this and
207328 pred = operand
208329 or
209- prop = rejectField ( ) and
330+ prop = errorProp ( ) and
210331 succ = await .getExceptionTarget ( ) and
211332 pred = operand
212333 }
@@ -215,37 +336,37 @@ private module PromiseFlow {
215336 /**
216337 * A flow step describing the data-flow related to the `.then` method of a promise.
217338 */
218- class ThenStep extends DataFlow :: AdditionalFlowStep , DataFlow:: MethodCallNode {
339+ class ThenStep extends PromiseFlowStep , DataFlow:: MethodCallNode {
219340 ThenStep ( ) { this .getMethodName ( ) = "then" }
220341
221- override predicate loadStep ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
222- prop = resolveField ( ) and
342+ override predicate load ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
343+ prop = valueProp ( ) and
223344 pred = getReceiver ( ) and
224345 succ = getCallback ( 0 ) .getParameter ( 0 )
225346 or
226- prop = rejectField ( ) and
347+ prop = errorProp ( ) and
227348 pred = getReceiver ( ) and
228349 succ = getCallback ( 1 ) .getParameter ( 0 )
229350 }
230351
231- override predicate loadStoreStep ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
352+ override predicate loadStore ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
232353 not exists ( this .getArgument ( 1 ) ) and
233- prop = rejectField ( ) and
354+ prop = errorProp ( ) and
234355 pred = getReceiver ( ) and
235356 succ = this
236357 or
237358 // read the value of a resolved/rejected promise that is returned
238- ( prop = rejectField ( ) or prop = resolveField ( ) ) and
359+ ( prop = errorProp ( ) or prop = valueProp ( ) ) and
239360 pred = getCallback ( [ 0 .. 1 ] ) .getAReturn ( ) and
240361 succ = this
241362 }
242363
243- override predicate storeStep ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
244- prop = resolveField ( ) and
364+ override predicate store ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
365+ prop = valueProp ( ) and
245366 pred = getCallback ( [ 0 .. 1 ] ) .getAReturn ( ) and
246367 succ = this
247368 or
248- prop = rejectField ( ) and
369+ prop = errorProp ( ) and
249370 pred = getCallback ( [ 0 .. 1 ] ) .getExceptionalReturn ( ) and
250371 succ = this
251372 }
@@ -254,32 +375,32 @@ private module PromiseFlow {
254375 /**
255376 * A flow step describing the data-flow related to the `.catch` method of a promise.
256377 */
257- class CatchStep extends DataFlow :: AdditionalFlowStep , DataFlow:: MethodCallNode {
378+ class CatchStep extends PromiseFlowStep , DataFlow:: MethodCallNode {
258379 CatchStep ( ) { this .getMethodName ( ) = "catch" }
259380
260- override predicate loadStep ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
261- prop = rejectField ( ) and
381+ override predicate load ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
382+ prop = errorProp ( ) and
262383 pred = getReceiver ( ) and
263384 succ = getCallback ( 0 ) .getParameter ( 0 )
264385 }
265386
266- override predicate loadStoreStep ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
267- prop = resolveField ( ) and
387+ override predicate loadStore ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
388+ prop = valueProp ( ) and
268389 pred = getReceiver ( ) .getALocalSource ( ) and
269390 succ = this
270391 or
271392 // read the value of a resolved/rejected promise that is returned
272- ( prop = rejectField ( ) or prop = resolveField ( ) ) and
393+ ( prop = errorProp ( ) or prop = valueProp ( ) ) and
273394 pred = getCallback ( 0 ) .getAReturn ( ) and
274395 succ = this
275396 }
276397
277- override predicate storeStep ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
278- prop = rejectField ( ) and
398+ override predicate store ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
399+ prop = errorProp ( ) and
279400 pred = getCallback ( 0 ) .getExceptionalReturn ( ) and
280401 succ = this
281402 or
282- prop = resolveField ( ) and
403+ prop = valueProp ( ) and
283404 pred = getCallback ( 0 ) .getAReturn ( ) and
284405 succ = this
285406 }
@@ -288,22 +409,22 @@ private module PromiseFlow {
288409 /**
289410 * A flow step describing the data-flow related to the `.finally` method of a promise.
290411 */
291- class FinallyStep extends DataFlow :: AdditionalFlowStep , DataFlow:: MethodCallNode {
412+ class FinallyStep extends PromiseFlowStep , DataFlow:: MethodCallNode {
292413 FinallyStep ( ) { this .getMethodName ( ) = "finally" }
293414
294- override predicate loadStoreStep ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
295- ( prop = resolveField ( ) or prop = rejectField ( ) ) and
415+ override predicate loadStore ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
416+ ( prop = valueProp ( ) or prop = errorProp ( ) ) and
296417 pred = getReceiver ( ) and
297418 succ = this
298419 or
299420 // read the value of a rejected promise that is returned
300- prop = rejectField ( ) and
421+ prop = errorProp ( ) and
301422 pred = getCallback ( 0 ) .getAReturn ( ) and
302423 succ = this
303424 }
304425
305- override predicate storeStep ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
306- prop = rejectField ( ) and
426+ override predicate store ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
427+ prop = errorProp ( ) and
307428 pred = getCallback ( 0 ) .getExceptionalReturn ( ) and
308429 succ = this
309430 }
0 commit comments