Skip to content

Commit 16b63b3

Browse files
committed
move deferred model to the query where it is used
1 parent 7045cd2 commit 16b63b3

File tree

5 files changed

+57
-54
lines changed

5 files changed

+57
-54
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+
* E.g. 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/src/semmle/javascript/Promises.qll

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -32,48 +32,6 @@ module Bluebird {
3232
}
3333
}
3434

35-
/**
36-
* Provides classes for working with various Deferred implementations
37-
*/
38-
module Deferred {
39-
class DeferredInstance extends DataFlow::NewNode {
40-
// Describes both `new Deferred()`, `new $.Deferred` and other variants.
41-
DeferredInstance() { this.getCalleeName() = "Deferred" }
42-
43-
private DataFlow::SourceNode ref(DataFlow::TypeTracker t) {
44-
t.start() and
45-
result = this
46-
or
47-
exists(DataFlow::TypeTracker t2 | result = ref(t2).track(t2, t))
48-
}
49-
50-
DataFlow::SourceNode ref() { result = ref(DataFlow::TypeTracker::end()) }
51-
}
52-
53-
/**
54-
* A promise object created by a Deferred constructor
55-
*/
56-
private class DeferredPromiseDefinition extends PromiseDefinition, DeferredInstance {
57-
DeferredPromiseDefinition() {
58-
// hardening of the "Deferred" heuristic: a method call to `resolve`.
59-
exists(ref().getAMethodCall("resolve"))
60-
}
61-
62-
override DataFlow::FunctionNode getExecutor() { result = getCallback(0) }
63-
}
64-
65-
/**
66-
* A resolved promise created by a `new Deferred().resolve()` call.
67-
*/
68-
class ResolvedDeferredPromiseDefinition extends ResolvedPromiseDefinition {
69-
ResolvedDeferredPromiseDefinition() {
70-
this = any(DeferredPromiseDefinition def).ref().getAMethodCall("resolve")
71-
}
72-
73-
override DataFlow::Node getValue() { result = getArgument(0) }
74-
}
75-
}
76-
7735
/**
7836
* Provides classes for working with the `q` library (https://github.com/kriskowal/q).
7937
*/

javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ typeInferenceMismatch
6969
| promise.js:5:25:5:32 | source() | promise.js:5:8:5:33 | bluebir ... urce()) |
7070
| promise.js:10:24:10:31 | source() | promise.js:10:8:10:32 | Promise ... urce()) |
7171
| promise.js:12:20:12:27 | source() | promise.js:13:8:13:23 | resolver.promise |
72-
| promise.js:22:23:22:30 | source() | promise.js:22:7:22:31 | promise ... urce()) |
7372
| sanitizer-guards.js:2:11:2:18 | source() | sanitizer-guards.js:4:8:4:8 | x |
7473
| sanitizer-guards.js:13:14:13:21 | source() | sanitizer-guards.js:15:10:15:15 | this.x |
7574
| sanitizer-guards.js:13:14:13:21 | source() | sanitizer-guards.js:21:14:21:19 | this.x |

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

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,4 @@ function closure() {
1111
let resolver = Promise.withResolver();
1212
resolver.resolve(source());
1313
sink(resolver.promise); // NOT OK
14-
}
15-
16-
class Deferred {
17-
18-
}
19-
20-
function deferred() {
21-
var promise = new Deferred();
22-
sink(promise.resolve(source())); // NOT OK
23-
24-
new Deferred().reject("foo") // <- a reject has to exist.
2514
}

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)