Skip to content

Commit 31009d9

Browse files
committed
add type tracking to detect instances
1 parent 0a6b343 commit 31009d9

2 files changed

Lines changed: 49 additions & 24 deletions

File tree

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

Lines changed: 47 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -36,39 +36,60 @@ module Bluebird {
3636
* Provides classes for working with various Deferred implementations
3737
*/
3838
module Deferred {
39-
private DataFlow::SourceNode deferred() {
40-
(
41-
exists(Variable var |
39+
class DeferredClass extends DataFlow::SourceNode {
40+
DeferredClass() {
41+
exists(Variable var |
4242
var.getName() = "Deferred" and
43-
(var.getADeclaration() instanceof LocalNamespaceDecl or var.getScope() instanceof GlobalScope) and
44-
result = DataFlow::valueNode(var.getADefinition())
45-
)
46-
or
47-
result.(DataFlow::ParameterNode).getName() = "Deferred"
48-
or
43+
(
44+
var.getADeclaration() instanceof LocalNamespaceDecl or
45+
var.getScope() instanceof GlobalScope
46+
) and
47+
this = DataFlow::valueNode(var.getADefinition())
48+
)
49+
or
50+
this.(DataFlow::ParameterNode).getName() = "Deferred"
51+
or
4952
exists(Function f |
50-
f.getName() = "Deferred" and
51-
result = DataFlow::valueNode(f)
53+
f.getName() = "Deferred" and
54+
this = DataFlow::valueNode(f)
5255
)
5356
or
54-
exists(ClassDefinition c |
55-
c.getName() = "Deferred" and
56-
result = DataFlow::valueNode(c)
57+
exists(ClassDefinition c |
58+
c.getName() = "Deferred" and
59+
this = DataFlow::valueNode(c)
5760
)
58-
)
59-
and
60-
// Sanity check that it is a Deferred implementation
61-
exists(DataFlow::NewNode instantiation |
62-
instantiation = result.getAnInstantiation() and
63-
exists(instantiation.getAMemberCall("resolve"))
64-
)
61+
}
62+
}
63+
64+
class DeferredInstance extends DataFlow::NewNode {
65+
DeferredClass deferredClass;
66+
67+
DeferredInstance() { this = deferredClass.getAnInstantiation() }
68+
69+
private DataFlow::SourceNode ref(DataFlow::TypeTracker t) {
70+
t.start() and
71+
result = this
72+
or
73+
exists(DataFlow::TypeTracker t2 | result = ref(t2).track(t2, t))
74+
}
75+
76+
DeferredClass getDeferredClass() { result = deferredClass }
77+
78+
DataFlow::CallNode getPromiseMemberCall(string methodName) {
79+
result = ref(DataFlow::TypeTracker::end()).getAMemberCall(methodName)
80+
}
6581
}
6682

6783
/**
6884
* A promise object created by a Deferred constructor
6985
*/
70-
private class DeferredPromiseDefinition extends PromiseDefinition, DataFlow::NewNode {
71-
DeferredPromiseDefinition() { this = deferred().getAnInstantiation() }
86+
private class DeferredPromiseDefinition extends PromiseDefinition, DeferredInstance {
87+
DeferredPromiseDefinition() {
88+
this = any(DeferredClass c |
89+
exists(any(DeferredInstance i | i.getDeferredClass() = c).getPromiseMemberCall("resolve")) and
90+
exists(any(DeferredInstance i | i.getDeferredClass() = c).getPromiseMemberCall("reject"))
91+
).getAnInstantiation()
92+
}
7293

7394
override DataFlow::FunctionNode getExecutor() { result = getCallback(0) }
7495
}
@@ -77,7 +98,9 @@ module Deferred {
7798
* A resolved promise created by a `new Deferred().resolve()` call.
7899
*/
79100
class ResolvedDeferredPromiseDefinition extends ResolvedPromiseDefinition {
80-
ResolvedDeferredPromiseDefinition() { this = any(DeferredPromiseDefinition def).getAMemberCall("resolve") }
101+
ResolvedDeferredPromiseDefinition() {
102+
this = any(DeferredPromiseDefinition def).getPromiseMemberCall("resolve")
103+
}
81104

82105
override DataFlow::Node getValue() { result = getArgument(0) }
83106
}

javascript/ql/test/library-tests/TaintTracking/promise.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,6 @@ class Deferred {
2020
function deferred() {
2121
var promise = new Deferred();
2222
sink(promise.resolve(source())); // NOT OK
23+
24+
new Deferred().reject("foo") // <- a reject has to exist.
2325
}

0 commit comments

Comments
 (0)