Skip to content

Commit d9beb54

Browse files
authored
Merge pull request #2102 from erik-krogh/deferredModel
JS: add Deferred model in js/use-of-returnless-function
2 parents f73caac + 19554ff commit d9beb54

File tree

3 files changed

+58
-1
lines changed

3 files changed

+58
-1
lines changed

javascript/ql/src/Statements/UseOfReturnlessFunction.ql

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,57 @@ predicate voidArrayCallback(DataFlow::CallNode call, Function func) {
149149
)
150150
}
151151

152+
153+
/**
154+
* Provides classes for working with various Deferred implementations.
155+
* It is a heuristic. The heuristic assume that a class is a promise defintion
156+
* if the class is called "Deferred" and the method `resolve` is called on an instance.
157+
*
158+
* Removes some false positives in the js/use-of-returnless-function query.
159+
*/
160+
module Deferred {
161+
/**
162+
* An instance of a `Deferred` class.
163+
* For example the result from `new Deferred()` or `new $.Deferred()`.
164+
*/
165+
class DeferredInstance extends DataFlow::NewNode {
166+
// Describes both `new Deferred()`, `new $.Deferred` and other variants.
167+
DeferredInstance() { this.getCalleeName() = "Deferred" }
168+
169+
private DataFlow::SourceNode ref(DataFlow::TypeTracker t) {
170+
t.start() and
171+
result = this
172+
or
173+
exists(DataFlow::TypeTracker t2 | result = ref(t2).track(t2, t))
174+
}
175+
176+
DataFlow::SourceNode ref() { result = ref(DataFlow::TypeTracker::end()) }
177+
}
178+
179+
/**
180+
* A promise object created by a Deferred constructor
181+
*/
182+
private class DeferredPromiseDefinition extends PromiseDefinition, DeferredInstance {
183+
DeferredPromiseDefinition() {
184+
// hardening of the "Deferred" heuristic: a method call to `resolve`.
185+
exists(ref().getAMethodCall("resolve"))
186+
}
187+
188+
override DataFlow::FunctionNode getExecutor() { result = getCallback(0) }
189+
}
190+
191+
/**
192+
* A resolved promise created by a `new Deferred().resolve()` call.
193+
*/
194+
class ResolvedDeferredPromiseDefinition extends ResolvedPromiseDefinition {
195+
ResolvedDeferredPromiseDefinition() {
196+
this = any(DeferredPromiseDefinition def).ref().getAMethodCall("resolve")
197+
}
198+
199+
override DataFlow::Node getValue() { result = getArgument(0) }
200+
}
201+
}
202+
152203
from DataFlow::CallNode call, Function func, string name, string msg
153204
where
154205
(

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ function closure() {
1111
let resolver = Promise.withResolver();
1212
resolver.resolve(source());
1313
sink(resolver.promise); // NOT OK
14-
}
14+
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,4 +82,10 @@
8282

8383
var baz = [1,2,3].filter(n => {n === 3}) // OK
8484
console.log(baz);
85+
86+
class Deferred {
87+
88+
}
89+
90+
new Deferred().resolve(onlySideEffects()); // OK
8591
})();

0 commit comments

Comments
 (0)