Skip to content

Commit e98f794

Browse files
committed
implement precise data-flow steps for Promise.all
1 parent 5c9fb23 commit e98f794

File tree

2 files changed

+49
-5
lines changed

2 files changed

+49
-5
lines changed

javascript/ql/src/semmle/javascript/Arrays.qll

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -251,13 +251,17 @@ private module ArrayDataFlow {
251251
/**
252252
* A step for creating an array and storing the elements in the array.
253253
*/
254-
private class ArrayCreationStep extends DataFlow::AdditionalFlowStep, DataFlow::Node {
255-
ArrayCreationStep() { this instanceof DataFlow::ArrayCreationNode }
256-
254+
private class ArrayCreationStep extends DataFlow::AdditionalFlowStep, DataFlow::ArrayCreationNode {
257255
override predicate storeStep(DataFlow::Node element, DataFlow::SourceNode obj, string prop) {
258256
prop = arrayElement() and
259-
element = this.(DataFlow::ArrayCreationNode).getAnElement() and
257+
element = this.getAnElement() and
260258
obj = this
259+
or
260+
exists(int i |
261+
element = this.getElement(i) and
262+
obj = this and
263+
prop = i.toString()
264+
)
261265
}
262266
}
263267

javascript/ql/src/semmle/javascript/Promises.qll

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ private class ES2015PromiseDefinition extends PromiseDefinition, DataFlow::NewNo
8585
*/
8686
abstract class PromiseCreationCall extends DataFlow::CallNode {
8787
/**
88-
* Gets the value this promise is resolved with.
88+
* Gets a value this promise is resolved with.
8989
*/
9090
abstract DataFlow::Node getValue();
9191
}
@@ -95,6 +95,16 @@ abstract class PromiseCreationCall extends DataFlow::CallNode {
9595
*/
9696
abstract class ResolvedPromiseDefinition extends PromiseCreationCall { }
9797

98+
/**
99+
* A promise that is created using a `Promise.all(array)` call.
100+
*/
101+
abstract class PromiseAllCreation extends PromiseCreationCall {
102+
/**
103+
* Gets a node for the array of values given to the `Promise.all(array)` call.
104+
*/
105+
abstract DataFlow::Node getArrayNode();
106+
}
107+
98108
/**
99109
* A resolved promise created by the standard ECMAScript 2015 `Promise.resolve` function.
100110
*/
@@ -121,6 +131,15 @@ class AggregateES2015PromiseDefinition extends PromiseCreationCall {
121131
}
122132
}
123133

134+
/**
135+
* An aggregated promise created using `Promise.all()`.
136+
*/
137+
class ES2015PromiseAllDefinition extends AggregateES2015PromiseDefinition, PromiseAllCreation {
138+
ES2015PromiseAllDefinition() { this.getCalleeName() = "all" }
139+
140+
override DataFlow::Node getArrayNode() { result = getArgument(0) }
141+
}
142+
124143
/**
125144
* Common predicates shared between type-tracking and data-flow for promises.
126145
*/
@@ -303,16 +322,28 @@ private module PromiseFlow {
303322
CreationStep() { this = promise }
304323

305324
override predicate store(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
325+
not promise instanceof PromiseAllCreation and
306326
prop = valueProp() and
307327
pred = promise.getValue() and
308328
succ = this
329+
or
330+
promise instanceof PromiseAllCreation and
331+
prop = valueProp() and
332+
pred = promise.(PromiseAllCreation).getArrayNode() and
333+
succ = this
309334
}
310335

311336
override predicate loadStore(DataFlow::Node pred, DataFlow::Node succ, string prop) {
312337
// Copy the value of a resolved promise to the value of this promise.
338+
not promise instanceof PromiseAllCreation and
313339
prop = valueProp() and
314340
pred = promise.getValue() and
315341
succ = this
342+
or
343+
promise instanceof PromiseAllCreation and
344+
prop = valueProp() and
345+
pred = promise.(PromiseAllCreation).getArrayNode() and
346+
succ = this
316347
}
317348
}
318349

@@ -533,6 +564,15 @@ module Bluebird {
533564
result = getArgument(0).getALocalSource().(DataFlow::ArrayCreationNode).getAnElement()
534565
}
535566
}
567+
568+
/**
569+
* A promise created using `Promise.all`:
570+
*/
571+
class BluebirdPromiseAllDefinition extends AggregateBluebirdPromiseDefinition, PromiseAllCreation {
572+
BluebirdPromiseAllDefinition() { this.getCalleeName() = "all" }
573+
574+
override DataFlow::Node getArrayNode() { result = getArgument(0) }
575+
}
536576
}
537577

538578
/**

0 commit comments

Comments
 (0)