Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions javascript/ql/src/semmle/javascript/Promises.qll
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)) |
Original file line number Diff line number Diff line change
@@ -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 |
10 changes: 10 additions & 0 deletions javascript/ql/test/library-tests/Promises/promises.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 });
})();
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down
8 changes: 8 additions & 0 deletions javascript/ql/test/library-tests/TaintTracking/promise.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
}