|
4 | 4 |
|
5 | 5 | private import javascript
|
6 | 6 | private import semmle.javascript.dataflow.FlowSummary
|
| 7 | +private import semmle.javascript.dataflow.TypeTracking |
7 | 8 | private import FlowSummaryUtil
|
8 | 9 |
|
9 | 10 | DataFlow::SourceNode promiseConstructorRef() {
|
@@ -211,12 +212,57 @@ private class PromiseReject extends SummarizedCallable {
|
211 | 212 | }
|
212 | 213 | }
|
213 | 214 |
|
| 215 | +/** |
| 216 | + * A call to `Promise.all()`. |
| 217 | + */ |
| 218 | +class PromiseAllCall extends DataFlow::CallNode { |
| 219 | + PromiseAllCall() { this = promiseConstructorRef().getAMemberCall("all") } |
| 220 | + |
| 221 | + /** Gets the source of the input array */ |
| 222 | + DataFlow::ArrayCreationNode getInputArray() { result = this.getArgument(0).getALocalSource() } |
| 223 | + |
| 224 | + /** Gets the `n`th element of the input array */ |
| 225 | + DataFlow::Node getNthInput(int n) { result = this.getInputArray().getElement(n) } |
| 226 | + |
| 227 | + /** Gets a reference to the output array. */ |
| 228 | + DataFlow::SourceNode getOutputArray() { |
| 229 | + exists(AwaitExpr await | |
| 230 | + this.flowsToExpr(await.getOperand()) and |
| 231 | + result = await.flow() |
| 232 | + ) |
| 233 | + or |
| 234 | + result = this.getAMethodCall("then").getCallback(0).getParameter(0) |
| 235 | + } |
| 236 | + |
| 237 | + /** Gets the `n`th output */ |
| 238 | + DataFlow::SourceNode getNthOutput(int n) { |
| 239 | + exists(string prop | |
| 240 | + result = this.getOutputArray().getAPropertyRead(prop) and |
| 241 | + n = prop.toInt() |
| 242 | + ) |
| 243 | + } |
| 244 | +} |
| 245 | + |
| 246 | +/** |
| 247 | + * Helps type-tracking simple uses of `Promise.all()` such as `const [a, b] = await Promise.all([x, y])`. |
| 248 | + * |
| 249 | + * Due to limited access path depth, type tracking can't track things that are in a promise and an array |
| 250 | + * at once. This generates a step directly from the input array to the output array. |
| 251 | + */ |
| 252 | +private class PromiseAllStep extends SharedTypeTrackingStep { |
| 253 | + override predicate loadStep(DataFlow::Node node1, DataFlow::Node node2, string prop) { |
| 254 | + exists(PromiseAllCall call, int n | |
| 255 | + node1 = call.getNthInput(n) and |
| 256 | + node2 = call.getNthOutput(n) and |
| 257 | + prop = Promises::valueProp() |
| 258 | + ) |
| 259 | + } |
| 260 | +} |
| 261 | + |
214 | 262 | private class PromiseAll extends SummarizedCallable {
|
215 | 263 | PromiseAll() { this = "Promise.all()" }
|
216 | 264 |
|
217 |
| - override DataFlow::InvokeNode getACallSimple() { |
218 |
| - result = promiseConstructorRef().getAMemberCall("all") |
219 |
| - } |
| 265 | + override DataFlow::InvokeNode getACallSimple() { result instanceof PromiseAllCall } |
220 | 266 |
|
221 | 267 | override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
222 | 268 | preservesValue = true and
|
|
0 commit comments