JS: basic model of closure Promises

This commit is contained in:
Asger F
2019-02-27 11:09:42 +00:00
parent a9f8a53dac
commit 3d400cc57f
6 changed files with 77 additions and 0 deletions

View File

@@ -45,3 +45,57 @@ module Q {
override DataFlow::FunctionNode getExecutor() { result = getCallback(0) }
}
}
private module ClosurePromise {
/**
* A promise created by a call `new goog.Promise(executor)`.
*/
private class ClosurePromiseDefinition extends PromiseDefinition, DataFlow::NewNode {
ClosurePromiseDefinition() { this = Closure::moduleImport("goog.Promise").getACall() }
override DataFlow::FunctionNode getExecutor() { result = getCallback(0) }
}
/**
* A promise created by a call `goog.Promise.resolve(value)`.
*/
private class ResolvedClosurePromiseDefinition extends ResolvedPromiseDefinition {
ResolvedClosurePromiseDefinition() {
this = Closure::moduleImport("goog.Promise.resolve").getACall()
}
override DataFlow::Node getValue() { result = getArgument(0) }
}
/**
* Taint steps through closure promise methods.
*/
private class ClosurePromiseTaintStep extends TaintTracking::AdditionalTaintStep {
DataFlow::Node pred;
ClosurePromiseTaintStep() {
// static methods in goog.Promise
exists (DataFlow::CallNode call, string name |
call = Closure::moduleImport("goog.Promise." + name).getACall() and
this = call and
pred = call.getAnArgument()
|
name = "all" or
name = "allSettled" or
name = "firstFulfilled" or
name = "race"
)
or
// promise created through goog.promise.withResolver()
exists (DataFlow::CallNode resolver |
resolver = Closure::moduleImport("goog.Promise.withResolver").getACall() and
this = resolver.getAPropertyRead("promise") and
pred = resolver.getAMethodCall("resolve").getArgument(0)
)
}
override predicate step(DataFlow::Node src, DataFlow::Node dst) {
src = pred and dst = this
}
}
}

View File

@@ -5,3 +5,5 @@
| promises.js:43:19:45:6 | Q.Promi ... \\n }) |
| promises.js:53:19:53:41 | Promise ... source) |
| promises.js:62:19:62:41 | Promise ... source) |
| promises.js:71:5:71:27 | Promise ... source) |
| promises.js:72:5:72:41 | new Pro ... ource)) |

View File

@@ -1,2 +1,3 @@
| promises.js:53:19:53:41 | Promise ... source) | promises.js:53:35:53:40 | source |
| promises.js:62:19:62:41 | Promise ... source) | promises.js:62:35:62:40 | source |
| promises.js:71:5:71:27 | Promise ... source) | promises.js:71:21:71:26 | source |

View File

@@ -64,3 +64,13 @@
var sink = val;
});
})();
(function() {
var Promise = goog.require('goog.Promise');
var source = "tainted";
Promise.resolve(source).then(val => { var sink = val; });
new Promise((res,rej) => res(source)).then(val => { var sink = val });
let resolver = Promise.withResolver();
resolver.resolve(source);
resolver.promise.then(val => { var sink = val });
})();

View File

@@ -23,6 +23,8 @@
| partialCalls.js:4:17:4:24 | source() | partialCalls.js:51:14:51:14 | x |
| promise.js:4:24:4:31 | source() | promise.js:4:8:4:32 | Promise ... urce()) |
| promise.js:5:25:5:32 | source() | promise.js:5:8:5:33 | bluebir ... urce()) |
| promise.js:10:24:10:31 | source() | promise.js:10:8:10:32 | Promise ... urce()) |
| promise.js:12:20:12:27 | source() | promise.js:13:8:13:23 | resolver.promise |
| sanitizer-guards.js:2:11:2:18 | source() | sanitizer-guards.js:4:8:4:8 | x |
| sanitizer-guards.js:13:14:13:21 | source() | sanitizer-guards.js:15:10:15:15 | this.x |
| sanitizer-guards.js:13:14:13:21 | source() | sanitizer-guards.js:21:14:21:19 | this.x |

View File

@@ -4,3 +4,11 @@ function test() {
sink(Promise.resolve(source())); // NOT OK
sink(bluebird.resolve(source())); // NOT OK
}
function closure() {
let Promise = goog.require('goog.Promise');
sink(Promise.resolve(source())); // NOT OK
let resolver = Promise.withResolver();
resolver.resolve(source());
sink(resolver.promise); // NOT OK
}