From 0ccc0057f95314fa179a80216558bb6704eb1a94 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 8 Oct 2019 15:04:11 +0200 Subject: [PATCH 01/10] add Deferred model to Promises.qll --- change-notes/1.23/analysis-javascript.md | 1 + .../ql/src/semmle/javascript/Promises.qll | 33 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/change-notes/1.23/analysis-javascript.md b/change-notes/1.23/analysis-javascript.md index 04b5e6cb5418..87b12c192303 100644 --- a/change-notes/1.23/analysis-javascript.md +++ b/change-notes/1.23/analysis-javascript.md @@ -38,3 +38,4 @@ ## Changes to QL libraries * `Expr.getDocumentation()` now handles chain assignments. +* Added `Deferred` as a promise library in Promises.qll diff --git a/javascript/ql/src/semmle/javascript/Promises.qll b/javascript/ql/src/semmle/javascript/Promises.qll index 858064e85a0d..794a866aee29 100644 --- a/javascript/ql/src/semmle/javascript/Promises.qll +++ b/javascript/ql/src/semmle/javascript/Promises.qll @@ -32,6 +32,39 @@ module Bluebird { } } +/** + * Provides classes for working with various Deferred implementations + */ +module Deferred { + private DataFlow::SourceNode deferred() { + exists(VarAccess var, DataFlow::NewNode instantiation | + var.getName() = "Deferred" and + result = DataFlow::exprNode(var).getALocalSource() and + // Sanity check that result really is a Deferred implementation + instantiation = result.getAnInstantiation() and + exists(instantiation.getAMemberCall("resolve")) + ) + } + + /** + * A promise object created by a Deferred constructor + */ + private class DeferredPromiseDefinition extends PromiseDefinition, DataFlow::NewNode { + DeferredPromiseDefinition() { this = deferred().getAnInstantiation() } + + override DataFlow::FunctionNode getExecutor() { result = getCallback(0) } + } + + /** + * A resolved promise created by a `new Deferred().resolve()` call. + */ + class ResolvedDeferredPromiseDefinition extends ResolvedPromiseDefinition { + ResolvedDeferredPromiseDefinition() { this = any(DeferredPromiseDefinition def).getAMemberCall("resolve") } + + override DataFlow::Node getValue() { result = getArgument(0) } + } +} + /** * Provides classes for working with the `q` library (https://github.com/kriskowal/q). */ From 411ed702fb1eabc9f308a34abe6105b688d12c36 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 9 Oct 2019 13:50:12 +0200 Subject: [PATCH 02/10] change change-notes --- change-notes/1.23/analysis-javascript.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/change-notes/1.23/analysis-javascript.md b/change-notes/1.23/analysis-javascript.md index 87b12c192303..70d903cb9448 100644 --- a/change-notes/1.23/analysis-javascript.md +++ b/change-notes/1.23/analysis-javascript.md @@ -9,6 +9,7 @@ - [rate-limiter-flexible](https://www.npmjs.com/package/rate-limiter-flexible) * The call graph has been improved to resolve method calls in more cases. This may produce more security alerts. +* Promises derived from a Deferred object are now recognized. ## New queries @@ -38,4 +39,3 @@ ## Changes to QL libraries * `Expr.getDocumentation()` now handles chain assignments. -* Added `Deferred` as a promise library in Promises.qll From c7eb0f17a9e35a0d9e0be951373df7bcad9c4f87 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 9 Oct 2019 13:59:00 +0200 Subject: [PATCH 03/10] add TaintTracking test for new Deferred model --- .../TaintTracking/BasicTaintTracking.expected | 1 + .../ql/test/library-tests/TaintTracking/promise.js | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected index 2722f67d6fdf..77592a2e855c 100644 --- a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected +++ b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected @@ -69,6 +69,7 @@ typeInferenceMismatch | 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 | +| promise.js:22:23:22:30 | source() | promise.js:22:7:22:31 | promise ... urce()) | | 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 | diff --git a/javascript/ql/test/library-tests/TaintTracking/promise.js b/javascript/ql/test/library-tests/TaintTracking/promise.js index cd5351720ba2..87e89451d99c 100644 --- a/javascript/ql/test/library-tests/TaintTracking/promise.js +++ b/javascript/ql/test/library-tests/TaintTracking/promise.js @@ -12,3 +12,12 @@ function closure() { resolver.resolve(source()); sink(resolver.promise); // NOT OK } + +class Deferred { + +} + +function deferred() { + var promise = new Deferred(); + sink(promise.resolve(source())); // NOT OK +} \ No newline at end of file From 4ec825b5b6f3dbb1ba8c5e20bf70963772c9bda0 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 9 Oct 2019 16:18:04 +0200 Subject: [PATCH 04/10] made model of Deferred more precise --- .../ql/src/semmle/javascript/Promises.qll | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/Promises.qll b/javascript/ql/src/semmle/javascript/Promises.qll index 794a866aee29..362bb2f5063a 100644 --- a/javascript/ql/src/semmle/javascript/Promises.qll +++ b/javascript/ql/src/semmle/javascript/Promises.qll @@ -37,10 +37,23 @@ module Bluebird { */ module Deferred { private DataFlow::SourceNode deferred() { - exists(VarAccess var, DataFlow::NewNode instantiation | - var.getName() = "Deferred" and - result = DataFlow::exprNode(var).getALocalSource() and - // Sanity check that result really is a Deferred implementation + ( + exists(Variable var | + var.getName() = "Deferred" and + (var.getADeclaration() instanceof LocalNamespaceDecl or var.getScope() instanceof GlobalScope) and + result = DataFlow::valueNode(var.getADefinition()) + ) + or + result.(DataFlow::ParameterNode).getName() = "Deferred" + or + exists(Function f | + f.getName() = "Deferred" and + result = DataFlow::valueNode(f) + ) + ) + and + // Sanity check that it is a Deferred implementation + exists(DataFlow::NewNode instantiation | instantiation = result.getAnInstantiation() and exists(instantiation.getAMemberCall("resolve")) ) From 0a6b343820e43fd47efa61cf8acec0deab53c458 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 10 Oct 2019 11:50:34 +0200 Subject: [PATCH 05/10] add "class Deferred{...}" as potential Deferred implementation to fix the tests --- javascript/ql/src/semmle/javascript/Promises.qll | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/javascript/ql/src/semmle/javascript/Promises.qll b/javascript/ql/src/semmle/javascript/Promises.qll index 362bb2f5063a..488adebab6e2 100644 --- a/javascript/ql/src/semmle/javascript/Promises.qll +++ b/javascript/ql/src/semmle/javascript/Promises.qll @@ -46,10 +46,15 @@ module Deferred { or result.(DataFlow::ParameterNode).getName() = "Deferred" or - exists(Function f | + exists(Function f | f.getName() = "Deferred" and result = DataFlow::valueNode(f) ) + or + exists(ClassDefinition c | + c.getName() = "Deferred" and + result = DataFlow::valueNode(c) + ) ) and // Sanity check that it is a Deferred implementation From 31009d979d323eb496530ae99c9f719ebed67272 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Fri, 11 Oct 2019 12:04:34 +0200 Subject: [PATCH 06/10] add type tracking to detect instances --- .../ql/src/semmle/javascript/Promises.qll | 71 ++++++++++++------- .../library-tests/TaintTracking/promise.js | 2 + 2 files changed, 49 insertions(+), 24 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/Promises.qll b/javascript/ql/src/semmle/javascript/Promises.qll index 488adebab6e2..13eb1e8114e9 100644 --- a/javascript/ql/src/semmle/javascript/Promises.qll +++ b/javascript/ql/src/semmle/javascript/Promises.qll @@ -36,39 +36,60 @@ module Bluebird { * Provides classes for working with various Deferred implementations */ module Deferred { - private DataFlow::SourceNode deferred() { - ( - exists(Variable var | + class DeferredClass extends DataFlow::SourceNode { + DeferredClass() { + exists(Variable var | var.getName() = "Deferred" and - (var.getADeclaration() instanceof LocalNamespaceDecl or var.getScope() instanceof GlobalScope) and - result = DataFlow::valueNode(var.getADefinition()) - ) - or - result.(DataFlow::ParameterNode).getName() = "Deferred" - or + ( + var.getADeclaration() instanceof LocalNamespaceDecl or + var.getScope() instanceof GlobalScope + ) and + this = DataFlow::valueNode(var.getADefinition()) + ) + or + this.(DataFlow::ParameterNode).getName() = "Deferred" + or exists(Function f | - f.getName() = "Deferred" and - result = DataFlow::valueNode(f) + f.getName() = "Deferred" and + this = DataFlow::valueNode(f) ) or - exists(ClassDefinition c | - c.getName() = "Deferred" and - result = DataFlow::valueNode(c) + exists(ClassDefinition c | + c.getName() = "Deferred" and + this = DataFlow::valueNode(c) ) - ) - and - // Sanity check that it is a Deferred implementation - exists(DataFlow::NewNode instantiation | - instantiation = result.getAnInstantiation() and - exists(instantiation.getAMemberCall("resolve")) - ) + } + } + + class DeferredInstance extends DataFlow::NewNode { + DeferredClass deferredClass; + + DeferredInstance() { this = deferredClass.getAnInstantiation() } + + private DataFlow::SourceNode ref(DataFlow::TypeTracker t) { + t.start() and + result = this + or + exists(DataFlow::TypeTracker t2 | result = ref(t2).track(t2, t)) + } + + DeferredClass getDeferredClass() { result = deferredClass } + + DataFlow::CallNode getPromiseMemberCall(string methodName) { + result = ref(DataFlow::TypeTracker::end()).getAMemberCall(methodName) + } } /** * A promise object created by a Deferred constructor */ - private class DeferredPromiseDefinition extends PromiseDefinition, DataFlow::NewNode { - DeferredPromiseDefinition() { this = deferred().getAnInstantiation() } + private class DeferredPromiseDefinition extends PromiseDefinition, DeferredInstance { + DeferredPromiseDefinition() { + this = any(DeferredClass c | + exists(any(DeferredInstance i | i.getDeferredClass() = c).getPromiseMemberCall("resolve")) and + exists(any(DeferredInstance i | i.getDeferredClass() = c).getPromiseMemberCall("reject")) + ).getAnInstantiation() + } override DataFlow::FunctionNode getExecutor() { result = getCallback(0) } } @@ -77,7 +98,9 @@ module Deferred { * A resolved promise created by a `new Deferred().resolve()` call. */ class ResolvedDeferredPromiseDefinition extends ResolvedPromiseDefinition { - ResolvedDeferredPromiseDefinition() { this = any(DeferredPromiseDefinition def).getAMemberCall("resolve") } + ResolvedDeferredPromiseDefinition() { + this = any(DeferredPromiseDefinition def).getPromiseMemberCall("resolve") + } override DataFlow::Node getValue() { result = getArgument(0) } } diff --git a/javascript/ql/test/library-tests/TaintTracking/promise.js b/javascript/ql/test/library-tests/TaintTracking/promise.js index 87e89451d99c..d16e57241e8d 100644 --- a/javascript/ql/test/library-tests/TaintTracking/promise.js +++ b/javascript/ql/test/library-tests/TaintTracking/promise.js @@ -20,4 +20,6 @@ class Deferred { function deferred() { var promise = new Deferred(); sink(promise.resolve(source())); // NOT OK + + new Deferred().reject("foo") // <- a reject has to exist. } \ No newline at end of file From 5c07750286a2bfa2c4bdffc35f4267e488d1f7f6 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 24 Oct 2019 11:28:42 +0200 Subject: [PATCH 07/10] simplify the heuristic for Deferred promises --- .../ql/src/semmle/javascript/Promises.qll | 48 ++++--------------- 1 file changed, 8 insertions(+), 40 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/Promises.qll b/javascript/ql/src/semmle/javascript/Promises.qll index 13eb1e8114e9..ae8554c1a0a3 100644 --- a/javascript/ql/src/semmle/javascript/Promises.qll +++ b/javascript/ql/src/semmle/javascript/Promises.qll @@ -36,35 +36,9 @@ module Bluebird { * Provides classes for working with various Deferred implementations */ module Deferred { - class DeferredClass extends DataFlow::SourceNode { - DeferredClass() { - exists(Variable var | - var.getName() = "Deferred" and - ( - var.getADeclaration() instanceof LocalNamespaceDecl or - var.getScope() instanceof GlobalScope - ) and - this = DataFlow::valueNode(var.getADefinition()) - ) - or - this.(DataFlow::ParameterNode).getName() = "Deferred" - or - exists(Function f | - f.getName() = "Deferred" and - this = DataFlow::valueNode(f) - ) - or - exists(ClassDefinition c | - c.getName() = "Deferred" and - this = DataFlow::valueNode(c) - ) - } - } - class DeferredInstance extends DataFlow::NewNode { - DeferredClass deferredClass; - - DeferredInstance() { this = deferredClass.getAnInstantiation() } + // Describes both `new Deferred()`, `new $.Deferred` and other variants. + DeferredInstance() { this.getCalleeName() = "Deferred" } private DataFlow::SourceNode ref(DataFlow::TypeTracker t) { t.start() and @@ -72,23 +46,17 @@ module Deferred { or exists(DataFlow::TypeTracker t2 | result = ref(t2).track(t2, t)) } - - DeferredClass getDeferredClass() { result = deferredClass } - - DataFlow::CallNode getPromiseMemberCall(string methodName) { - result = ref(DataFlow::TypeTracker::end()).getAMemberCall(methodName) - } + + DataFlow::SourceNode ref() { result = ref(DataFlow::TypeTracker::end()) } } /** * A promise object created by a Deferred constructor */ private class DeferredPromiseDefinition extends PromiseDefinition, DeferredInstance { - DeferredPromiseDefinition() { - this = any(DeferredClass c | - exists(any(DeferredInstance i | i.getDeferredClass() = c).getPromiseMemberCall("resolve")) and - exists(any(DeferredInstance i | i.getDeferredClass() = c).getPromiseMemberCall("reject")) - ).getAnInstantiation() + DeferredPromiseDefinition() { + // hardening of the "Deferred" heuristic: a method call to `resolve`. + exists(ref().getAMethodCall("resolve")) } override DataFlow::FunctionNode getExecutor() { result = getCallback(0) } @@ -99,7 +67,7 @@ module Deferred { */ class ResolvedDeferredPromiseDefinition extends ResolvedPromiseDefinition { ResolvedDeferredPromiseDefinition() { - this = any(DeferredPromiseDefinition def).getPromiseMemberCall("resolve") + this = any(DeferredPromiseDefinition def).ref().getAMethodCall("resolve") } override DataFlow::Node getValue() { result = getArgument(0) } From 16b63b3d01a68b8065f463002f43254aa9cdb369 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Tue, 5 Nov 2019 15:45:17 +0100 Subject: [PATCH 08/10] move deferred model to the query where it is used --- .../src/Statements/UseOfReturnlessFunction.ql | 51 +++++++++++++++++++ .../ql/src/semmle/javascript/Promises.qll | 42 --------------- .../TaintTracking/BasicTaintTracking.expected | 1 - .../library-tests/TaintTracking/promise.js | 11 ---- .../Statements/UseOfReturnlessFunction/tst.js | 6 +++ 5 files changed, 57 insertions(+), 54 deletions(-) diff --git a/javascript/ql/src/Statements/UseOfReturnlessFunction.ql b/javascript/ql/src/Statements/UseOfReturnlessFunction.ql index f8f459cc84d0..48af33b8e436 100644 --- a/javascript/ql/src/Statements/UseOfReturnlessFunction.ql +++ b/javascript/ql/src/Statements/UseOfReturnlessFunction.ql @@ -149,6 +149,57 @@ predicate voidArrayCallback(DataFlow::CallNode call, Function func) { ) } + +/** + * Provides classes for working with various Deferred implementations. + * It is a heuristic. The heuristic assume that a class is a promise defintion + * if the class is called "Deferred" and the method `resolve` is called on an instance. + * + * Removes some false positives in the js/use-of-returnless-function query. + */ +module Deferred { + /** + * An instance of a `Deferred` class. + * E.g. the result from `new Deferred()` or `new $.Deferred()`. + */ + class DeferredInstance extends DataFlow::NewNode { + // Describes both `new Deferred()`, `new $.Deferred` and other variants. + DeferredInstance() { this.getCalleeName() = "Deferred" } + + private DataFlow::SourceNode ref(DataFlow::TypeTracker t) { + t.start() and + result = this + or + exists(DataFlow::TypeTracker t2 | result = ref(t2).track(t2, t)) + } + + DataFlow::SourceNode ref() { result = ref(DataFlow::TypeTracker::end()) } + } + + /** + * A promise object created by a Deferred constructor + */ + private class DeferredPromiseDefinition extends PromiseDefinition, DeferredInstance { + DeferredPromiseDefinition() { + // hardening of the "Deferred" heuristic: a method call to `resolve`. + exists(ref().getAMethodCall("resolve")) + } + + override DataFlow::FunctionNode getExecutor() { result = getCallback(0) } + } + + /** + * A resolved promise created by a `new Deferred().resolve()` call. + */ + class ResolvedDeferredPromiseDefinition extends ResolvedPromiseDefinition { + ResolvedDeferredPromiseDefinition() { + this = any(DeferredPromiseDefinition def).ref().getAMethodCall("resolve") + } + + override DataFlow::Node getValue() { result = getArgument(0) } + } +} + from DataFlow::CallNode call, Function func, string name, string msg where ( diff --git a/javascript/ql/src/semmle/javascript/Promises.qll b/javascript/ql/src/semmle/javascript/Promises.qll index ae8554c1a0a3..858064e85a0d 100644 --- a/javascript/ql/src/semmle/javascript/Promises.qll +++ b/javascript/ql/src/semmle/javascript/Promises.qll @@ -32,48 +32,6 @@ module Bluebird { } } -/** - * Provides classes for working with various Deferred implementations - */ -module Deferred { - class DeferredInstance extends DataFlow::NewNode { - // Describes both `new Deferred()`, `new $.Deferred` and other variants. - DeferredInstance() { this.getCalleeName() = "Deferred" } - - private DataFlow::SourceNode ref(DataFlow::TypeTracker t) { - t.start() and - result = this - or - exists(DataFlow::TypeTracker t2 | result = ref(t2).track(t2, t)) - } - - DataFlow::SourceNode ref() { result = ref(DataFlow::TypeTracker::end()) } - } - - /** - * A promise object created by a Deferred constructor - */ - private class DeferredPromiseDefinition extends PromiseDefinition, DeferredInstance { - DeferredPromiseDefinition() { - // hardening of the "Deferred" heuristic: a method call to `resolve`. - exists(ref().getAMethodCall("resolve")) - } - - override DataFlow::FunctionNode getExecutor() { result = getCallback(0) } - } - - /** - * A resolved promise created by a `new Deferred().resolve()` call. - */ - class ResolvedDeferredPromiseDefinition extends ResolvedPromiseDefinition { - ResolvedDeferredPromiseDefinition() { - this = any(DeferredPromiseDefinition def).ref().getAMethodCall("resolve") - } - - override DataFlow::Node getValue() { result = getArgument(0) } - } -} - /** * Provides classes for working with the `q` library (https://github.com/kriskowal/q). */ diff --git a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected index 77592a2e855c..2722f67d6fdf 100644 --- a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected +++ b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected @@ -69,7 +69,6 @@ typeInferenceMismatch | 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 | -| promise.js:22:23:22:30 | source() | promise.js:22:7:22:31 | promise ... urce()) | | 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 | diff --git a/javascript/ql/test/library-tests/TaintTracking/promise.js b/javascript/ql/test/library-tests/TaintTracking/promise.js index d16e57241e8d..9714d258df5d 100644 --- a/javascript/ql/test/library-tests/TaintTracking/promise.js +++ b/javascript/ql/test/library-tests/TaintTracking/promise.js @@ -11,15 +11,4 @@ function closure() { let resolver = Promise.withResolver(); resolver.resolve(source()); sink(resolver.promise); // NOT OK -} - -class Deferred { - -} - -function deferred() { - var promise = new Deferred(); - sink(promise.resolve(source())); // NOT OK - - new Deferred().reject("foo") // <- a reject has to exist. } \ No newline at end of file diff --git a/javascript/ql/test/query-tests/Statements/UseOfReturnlessFunction/tst.js b/javascript/ql/test/query-tests/Statements/UseOfReturnlessFunction/tst.js index 8a6a44707edd..90a52153dead 100644 --- a/javascript/ql/test/query-tests/Statements/UseOfReturnlessFunction/tst.js +++ b/javascript/ql/test/query-tests/Statements/UseOfReturnlessFunction/tst.js @@ -82,4 +82,10 @@ var baz = [1,2,3].filter(n => {n === 3}) // OK console.log(baz); + + class Deferred { + + } + + new Deferred().resolve(onlySideEffects()); // OK })(); \ No newline at end of file From dc923ef694ca8cec80f8b25c7285ee13c6231753 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 6 Nov 2019 13:28:46 +0100 Subject: [PATCH 09/10] remove change note Co-Authored-By: Esben Sparre Andreasen --- change-notes/1.23/analysis-javascript.md | 1 - 1 file changed, 1 deletion(-) diff --git a/change-notes/1.23/analysis-javascript.md b/change-notes/1.23/analysis-javascript.md index 8b89369fcff7..bb1357589043 100644 --- a/change-notes/1.23/analysis-javascript.md +++ b/change-notes/1.23/analysis-javascript.md @@ -11,7 +11,6 @@ - [rate-limiter-flexible](https://www.npmjs.com/package/rate-limiter-flexible) * The call graph has been improved to resolve method calls in more cases. This may produce more security alerts. -* Promises derived from a Deferred object are now recognized. * TypeScript 3.6 and 3.7 features are now supported. From 19554ff6e7a8584e63b76bbde90ff88379e26781 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Wed, 6 Nov 2019 13:37:54 +0100 Subject: [PATCH 10/10] change "e.g." to "for example" in qldoc --- javascript/ql/src/Statements/UseOfReturnlessFunction.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/Statements/UseOfReturnlessFunction.ql b/javascript/ql/src/Statements/UseOfReturnlessFunction.ql index 48af33b8e436..123515a7cbd3 100644 --- a/javascript/ql/src/Statements/UseOfReturnlessFunction.ql +++ b/javascript/ql/src/Statements/UseOfReturnlessFunction.ql @@ -160,7 +160,7 @@ predicate voidArrayCallback(DataFlow::CallNode call, Function func) { module Deferred { /** * An instance of a `Deferred` class. - * E.g. the result from `new Deferred()` or `new $.Deferred()`. + * For example the result from `new Deferred()` or `new $.Deferred()`. */ class DeferredInstance extends DataFlow::NewNode { // Describes both `new Deferred()`, `new $.Deferred` and other variants.