Skip to content

Commit a2827e9

Browse files
authored
Merge pull request #2362 from erik-krogh/promiseAll
Approved by max-schaefer
2 parents 3e5e14a + 967ecba commit a2827e9

File tree

5 files changed

+45
-7
lines changed

5 files changed

+45
-7
lines changed

javascript/ql/src/Statements/UseOfReturnlessFunction.ql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ predicate benignContext(Expr e) {
6868
any(InvokeExpr invoke).getCallee() = e
6969
or
7070
// arguments to Promise.resolve (and promise library variants) are benign.
71-
e = any(ResolvedPromiseDefinition promise).getValue().asExpr()
71+
e = any(PromiseCreationCall promise).getValue().asExpr()
7272
}
7373

7474
predicate oneshotClosure(DataFlow::CallNode call) {
@@ -198,7 +198,7 @@ module Deferred {
198198
/**
199199
* A resolved promise created by a `new Deferred().resolve()` call.
200200
*/
201-
class ResolvedDeferredPromiseDefinition extends ResolvedPromiseDefinition {
201+
class ResolvedDeferredPromiseDefinition extends PromiseCreationCall {
202202
ResolvedDeferredPromiseDefinition() {
203203
this = any(DeferredPromiseDefinition def).ref().getAMethodCall("resolve")
204204
}

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,22 @@ module Bluebird {
3030

3131
override DataFlow::Node getValue() { result = getArgument(0) }
3232
}
33+
34+
/**
35+
* An aggregated promise produced either by `Promise.all`, `Promise.race` or `Promise.map`.
36+
*/
37+
class AggregateBluebirdPromiseDefinition extends PromiseCreationCall {
38+
AggregateBluebirdPromiseDefinition() {
39+
exists(string m | m = "all" or m = "race" or m = "map" |
40+
this = bluebird().getAMemberCall(m)
41+
)
42+
}
43+
44+
override DataFlow::Node getValue() {
45+
result = getArgument(0).getALocalSource().(DataFlow::ArrayCreationNode).getAnElement()
46+
}
47+
}
48+
3349
}
3450

3551
/**

javascript/ql/src/semmle/javascript/StandardLibrary.qll

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,15 +152,20 @@ private class ES2015PromiseDefinition extends PromiseDefinition, DataFlow::NewNo
152152
}
153153

154154
/**
155-
* A promise that is resolved with the given value.
155+
* A promise that is created and resolved with one or more value.
156156
*/
157-
abstract class ResolvedPromiseDefinition extends DataFlow::CallNode {
157+
abstract class PromiseCreationCall extends DataFlow::CallNode {
158158
/**
159159
* Gets the value this promise is resolved with.
160160
*/
161161
abstract DataFlow::Node getValue();
162162
}
163163

164+
/**
165+
* A promise that is created using a `.resolve()` call.
166+
*/
167+
abstract class ResolvedPromiseDefinition extends PromiseCreationCall {}
168+
164169
/**
165170
* A resolved promise created by the standard ECMAScript 2015 `Promise.resolve` function.
166171
*/
@@ -172,6 +177,21 @@ class ResolvedES2015PromiseDefinition extends ResolvedPromiseDefinition {
172177
override DataFlow::Node getValue() { result = getArgument(0) }
173178
}
174179

180+
/**
181+
* An aggregated promise produced either by `Promise.all` or `Promise.race`.
182+
*/
183+
class AggregateES2015PromiseDefinition extends PromiseCreationCall {
184+
AggregateES2015PromiseDefinition() {
185+
exists(string m | m = "all" or m = "race" |
186+
this = DataFlow::globalVarRef("Promise").getAMemberCall(m)
187+
)
188+
}
189+
190+
override DataFlow::Node getValue() {
191+
result = getArgument(0).getALocalSource().(DataFlow::ArrayCreationNode).getAnElement()
192+
}
193+
}
194+
175195
/**
176196
* A data flow edge from a promise reaction to the corresponding handler.
177197
*/
@@ -197,7 +217,7 @@ predicate promiseTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
197217
pred = succ.(PromiseDefinition).getResolveParameter().getACall().getArgument(0)
198218
or
199219
// from `x` to `Promise.resolve(x)`
200-
pred = succ.(ResolvedPromiseDefinition).getValue()
220+
pred = succ.(PromiseCreationCall).getValue()
201221
or
202222
exists(DataFlow::MethodCallNode thn, DataFlow::FunctionNode cb |
203223
thn.getMethodName() = "then" and cb = thn.getCallback(0)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import javascript
22

33
query predicate test_ResolvedPromiseDefinition(
4-
ResolvedPromiseDefinition resolved, DataFlow::Node res
4+
PromiseCreationCall resolved, DataFlow::Node res
55
) {
66
res = resolved.getValue()
77
}

javascript/ql/test/query-tests/Statements/UseOfReturnlessFunction/tst.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@
8888
}
8989

9090
new Deferred().resolve(onlySideEffects()); // OK
91+
92+
Promise.all([onlySideEffects(), onlySideEffects()])
9193
})();
9294

9395
+function() {
@@ -104,4 +106,4 @@ class Bar extends Foo {
104106
constructor() {
105107
console.log(super()); // OK.
106108
}
107-
}
109+
}

0 commit comments

Comments
 (0)