mirror of
https://github.com/github/codeql.git
synced 2026-04-26 17:25:19 +02:00
add additional data-flow edges to data-flow related to promises
This commit is contained in:
@@ -1,9 +1,380 @@
|
||||
/**
|
||||
* Provides classes for modelling promise libraries.
|
||||
* Provides classes for modelling promises and their data-flow.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
/**
|
||||
* A definition of a `Promise` object.
|
||||
*/
|
||||
abstract class PromiseDefinition extends DataFlow::SourceNode {
|
||||
/** Gets the executor function of this promise object. */
|
||||
abstract DataFlow::FunctionNode getExecutor();
|
||||
|
||||
/** Gets the `resolve` parameter of the executor function. */
|
||||
DataFlow::ParameterNode getResolveParameter() { result = getExecutor().getParameter(0) }
|
||||
|
||||
/** Gets the `reject` parameter of the executor function. */
|
||||
DataFlow::ParameterNode getRejectParameter() { result = getExecutor().getParameter(1) }
|
||||
|
||||
/** Gets the `i`th callback handler installed by method `m`. */
|
||||
private DataFlow::FunctionNode getAHandler(string m, int i) {
|
||||
result = getAMethodCall(m).getCallback(i)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a function that handles promise resolution, including both
|
||||
* `then` handlers and `finally` handlers.
|
||||
*/
|
||||
DataFlow::FunctionNode getAResolveHandler() {
|
||||
result = getAHandler("then", 0) or
|
||||
result = getAFinallyHandler()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a function that handles promise rejection, including
|
||||
* `then` handlers, `catch` handlers and `finally` handlers.
|
||||
*/
|
||||
DataFlow::FunctionNode getARejectHandler() {
|
||||
result = getAHandler("then", 1) or
|
||||
result = getACatchHandler() or
|
||||
result = getAFinallyHandler()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a `catch` handler of this promise.
|
||||
*/
|
||||
DataFlow::FunctionNode getACatchHandler() { result = getAHandler("catch", 0) }
|
||||
|
||||
/**
|
||||
* Gets a `finally` handler of this promise.
|
||||
*/
|
||||
DataFlow::FunctionNode getAFinallyHandler() { result = getAHandler("finally", 0) }
|
||||
}
|
||||
|
||||
/** Holds if the `i`th callback handler is installed by method `m`. */
|
||||
private predicate hasHandler(DataFlow::InvokeNode promise, string m, int i) {
|
||||
exists(promise.getAMethodCall(m).getCallback(i))
|
||||
}
|
||||
|
||||
/**
|
||||
* A call that looks like a Promise.
|
||||
*
|
||||
* For example, this could be the call `promise(f).then(function(v){...})`
|
||||
*/
|
||||
class PromiseCandidate extends DataFlow::InvokeNode {
|
||||
PromiseCandidate() {
|
||||
hasHandler(this, "then", [0 .. 1]) or
|
||||
hasHandler(this, "catch", 0) or
|
||||
hasHandler(this, "finally", 0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A promise object created by the standard ECMAScript 2015 `Promise` constructor.
|
||||
*/
|
||||
private class ES2015PromiseDefinition extends PromiseDefinition, DataFlow::NewNode {
|
||||
ES2015PromiseDefinition() { this = DataFlow::globalVarRef("Promise").getAnInstantiation() }
|
||||
|
||||
override DataFlow::FunctionNode getExecutor() { result = getCallback(0) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A promise that is created and resolved with one or more value.
|
||||
*/
|
||||
abstract class PromiseCreationCall extends DataFlow::CallNode {
|
||||
/**
|
||||
* Gets the value this promise is resolved with.
|
||||
*/
|
||||
abstract DataFlow::Node getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* A promise that is created using a `.resolve()` call.
|
||||
*/
|
||||
abstract class ResolvedPromiseDefinition extends PromiseCreationCall { }
|
||||
|
||||
/**
|
||||
* A resolved promise created by the standard ECMAScript 2015 `Promise.resolve` function.
|
||||
*/
|
||||
class ResolvedES2015PromiseDefinition extends ResolvedPromiseDefinition {
|
||||
ResolvedES2015PromiseDefinition() {
|
||||
this = DataFlow::globalVarRef("Promise").getAMemberCall("resolve")
|
||||
}
|
||||
|
||||
override DataFlow::Node getValue() { result = getArgument(0) }
|
||||
}
|
||||
|
||||
/**
|
||||
* An aggregated promise produced either by `Promise.all` or `Promise.race`.
|
||||
*/
|
||||
class AggregateES2015PromiseDefinition extends PromiseCreationCall {
|
||||
AggregateES2015PromiseDefinition() {
|
||||
exists(string m | m = "all" or m = "race" |
|
||||
this = DataFlow::globalVarRef("Promise").getAMemberCall(m)
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getValue() {
|
||||
result = getArgument(0).getALocalSource().(DataFlow::ArrayCreationNode).getAnElement()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This module defines how data-flow propagates into and out a Promise.
|
||||
*/
|
||||
private module PromiseFlow {
|
||||
/**
|
||||
* A promise from which data-flow can flow into or out of.
|
||||
*
|
||||
* This promise can both be a promise created by e.g. `new Promise(..)` or `Promise.resolve(..)`,
|
||||
* or the result from calling a method on a promise e.g. `promise.then(..)`.
|
||||
*
|
||||
* The 4 methods in this class describe that ordinary and exceptional flow can flow into and out of this promise.
|
||||
*/
|
||||
private abstract class PromiseNode extends DataFlow::SourceNode {
|
||||
|
||||
/**
|
||||
* Get a DataFlow::Node for a value that this promise is resolved with.
|
||||
* The value is sent either to a chained promise, or to an `await` expression.
|
||||
*
|
||||
* The value is e.g. an argument to `resolve(..)`, or a return value from a `.then(..)` handler.
|
||||
*/
|
||||
DataFlow::Node getASentResolveValue() { none() }
|
||||
|
||||
/**
|
||||
* Get the DataFlow::Node that receives the value that this promise has been resolved with.
|
||||
*
|
||||
* E.g. the `x` in `promise.then((x) => ..)`.
|
||||
*/
|
||||
DataFlow::Node getReceivedResolveValue() { none() }
|
||||
|
||||
/**
|
||||
* Get a DataFlow::Node for a value that this promise is rejected with.
|
||||
* The value is sent either to a chained promise, or thrown by an `await` expression.
|
||||
*
|
||||
* The value is e.g. an argument to `reject(..)`, or an exception thrown by the promise executor.
|
||||
*/
|
||||
DataFlow::Node getASentRejectValue() { none() }
|
||||
|
||||
/**
|
||||
* Get the DataFlow::Node that receives the value that this promise has been rejected with.
|
||||
*
|
||||
* E.g. the `x` in `promise.catch((x) => ..)`.
|
||||
*/
|
||||
DataFlow::Node getReceivedRejectValue() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A PromiseNode for a PromiseDefinition.
|
||||
* E.g. `new Promise(..)`.
|
||||
*/
|
||||
private class PromiseDefinitionNode extends PromiseNode {
|
||||
PromiseDefinition promise;
|
||||
|
||||
PromiseDefinitionNode() { this = promise }
|
||||
|
||||
override DataFlow::Node getASentResolveValue() {
|
||||
result = promise.getResolveParameter().getACall().getArgument(0)
|
||||
}
|
||||
|
||||
override DataFlow::Node getASentRejectValue() {
|
||||
result = promise.getRejectParameter().getACall().getArgument(0)
|
||||
or
|
||||
result = promise.getExecutor().getExceptionalReturn()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A PromiseNode for a call that creates a promise.
|
||||
* E.g. `Promise.resolve(..)` or `Promise.all(..)`.
|
||||
*/
|
||||
private class PromiseCreationNode extends PromiseNode {
|
||||
PromiseCreationCall promise;
|
||||
|
||||
PromiseCreationNode() { this = promise }
|
||||
|
||||
override DataFlow::Node getASentResolveValue() {
|
||||
exists(DataFlow::Node value | value = promise.getValue() |
|
||||
not value instanceof PromiseNode and
|
||||
result = value
|
||||
or
|
||||
result = value.(PromiseNode).getASentResolveValue()
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getASentRejectValue() {
|
||||
result = promise.getValue().(PromiseNode).getASentRejectValue()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A node referring to a PromiseNode through type-tracking.
|
||||
*/
|
||||
private class TrackedPromiseNode extends PromiseNode {
|
||||
PromiseNode base;
|
||||
TrackedPromiseNode() {
|
||||
this = trackPromise(DataFlow::TypeTracker::end(), base) and
|
||||
not this instanceof PromiseDefinitionNode and
|
||||
not this instanceof PromiseCreationNode
|
||||
}
|
||||
|
||||
override DataFlow::Node getASentResolveValue() { result = base.getASentResolveValue() }
|
||||
override DataFlow::Node getReceivedResolveValue() { result = base.getReceivedResolveValue() }
|
||||
override DataFlow::Node getASentRejectValue() { result = base.getASentRejectValue() }
|
||||
override DataFlow::Node getReceivedRejectValue() { result = base.getReceivedRejectValue() }
|
||||
}
|
||||
|
||||
private DataFlow::SourceNode trackPromise(DataFlow::TypeTracker t, PromiseNode promise) {
|
||||
t.start() and result = promise
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = trackPromise(t2, promise).track(t2, t))
|
||||
}
|
||||
|
||||
/**
|
||||
* A PromiseNode that is a method call on an existing PromiseNode.
|
||||
* E.g. `promise.then(..)`.
|
||||
*/
|
||||
private abstract class ChainedPromiseNode extends PromiseNode, DataFlow::MethodCallNode {
|
||||
PromiseNode base;
|
||||
|
||||
ChainedPromiseNode() { this = base.getAMethodCall(_) }
|
||||
|
||||
PromiseNode getBase() { result = base }
|
||||
}
|
||||
|
||||
/**
|
||||
* A PromiseNode for the `.then(..)` method on an existing promise.
|
||||
*/
|
||||
private class PromiseThenNode extends ChainedPromiseNode {
|
||||
PromiseThenNode() { this = base.getAMethodCall("then") }
|
||||
|
||||
override DataFlow::Node getASentResolveValue() {
|
||||
exists(DataFlow::Node ret | ret = this.getCallback(0).getAReturn() |
|
||||
if ret instanceof PromiseNode
|
||||
then result = ret.(PromiseNode).getReceivedResolveValue()
|
||||
else result = ret
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getASentRejectValue() {
|
||||
not exists(this.getCallback(1)) and result = base.getASentRejectValue()
|
||||
or
|
||||
result = this.getCallback([0..1]).getExceptionalReturn()
|
||||
}
|
||||
|
||||
override DataFlow::Node getReceivedResolveValue() { result = this.getCallback(0).getParameter(0) }
|
||||
|
||||
override DataFlow::Node getReceivedRejectValue() { result = this.getCallback(1).getParameter(0) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A PromiseNode for the `.finally(..)` method on an existing promise.
|
||||
*/
|
||||
private class PromiseFinallyNode extends ChainedPromiseNode {
|
||||
PromiseFinallyNode() { this = base.getAMethodCall("finally") }
|
||||
|
||||
override DataFlow::Node getASentResolveValue() { result = base.getASentResolveValue() }
|
||||
|
||||
override DataFlow::Node getASentRejectValue() {
|
||||
result = base.getASentRejectValue()
|
||||
or
|
||||
result = this.getCallback(0).getExceptionalReturn()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A PromiseNode for the `.catch(..)` method on an existing promise.
|
||||
*/
|
||||
private class PromiseCatchNode extends ChainedPromiseNode {
|
||||
PromiseCatchNode() { this = base.getAMethodCall("catch") }
|
||||
|
||||
override DataFlow::Node getASentResolveValue() {
|
||||
exists(DataFlow::Node ret | ret = this.getCallback(0).getAReturn() |
|
||||
if ret instanceof PromiseNode
|
||||
then result = ret.(PromiseNode).getReceivedResolveValue()
|
||||
else result = ret
|
||||
)
|
||||
or
|
||||
result = base.getASentResolveValue()
|
||||
}
|
||||
|
||||
override DataFlow::Node getASentRejectValue() { result = this.getCallback(0).getExceptionalReturn() }
|
||||
|
||||
override DataFlow::Node getReceivedResolveValue() { none() }
|
||||
|
||||
override DataFlow::Node getReceivedRejectValue() { result = this.getCallback(0).getParameter(0) }
|
||||
}
|
||||
|
||||
|
||||
private ChainedPromiseNode getAChainedPromise(PromiseNode p) { result.getBase() = p}
|
||||
|
||||
/**
|
||||
* A data flow edge from a promise resolve/reject to the corresponding handler (or `await` expression).
|
||||
*/
|
||||
private class PromiseFlowStep extends DataFlow::AdditionalFlowStep {
|
||||
PromiseNode promise;
|
||||
|
||||
PromiseFlowStep() { this = promise }
|
||||
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
pred = promise.getASentResolveValue() and
|
||||
succ = getAChainedPromise(promise).getReceivedResolveValue()
|
||||
or
|
||||
pred = promise.getASentRejectValue() and
|
||||
succ = getAChainedPromise(promise).getReceivedRejectValue()
|
||||
or
|
||||
pred = promise.getASentResolveValue() and
|
||||
exists(DataFlow::SourceNode awaitNode |
|
||||
awaitNode.asExpr().(AwaitExpr).getOperand() = promise.asExpr() and
|
||||
succ = awaitNode
|
||||
)
|
||||
or
|
||||
pred = promise.getASentRejectValue() and
|
||||
exists(DataFlow::SourceNode awaitNode |
|
||||
awaitNode.asExpr().(AwaitExpr).getOperand() = promise.asExpr() and
|
||||
succ = awaitNode.asExpr().getExceptionTarget()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint propagates from `pred` to `succ` through promises.
|
||||
*/
|
||||
predicate promiseTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
// from `x` to `new Promise((res, rej) => res(x))`
|
||||
pred = succ.(PromiseDefinition).getResolveParameter().getACall().getArgument(0)
|
||||
or
|
||||
// from `x` to `Promise.resolve(x)`
|
||||
pred = succ.(PromiseCreationCall).getValue()
|
||||
or
|
||||
exists(DataFlow::MethodCallNode thn, DataFlow::FunctionNode cb |
|
||||
thn.getMethodName() = "then" and cb = thn.getCallback(0)
|
||||
|
|
||||
// from `p` to `x` in `p.then(x => ...)`
|
||||
pred = thn.getReceiver() and
|
||||
succ = cb.getParameter(0)
|
||||
or
|
||||
// from `v` to `p.then(x => return v)`
|
||||
pred = cb.getAReturn() and
|
||||
succ = thn
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* An additional taint step that involves promises.
|
||||
*/
|
||||
private class PromiseTaintStep extends TaintTracking::AdditionalTaintStep {
|
||||
DataFlow::Node source;
|
||||
|
||||
PromiseTaintStep() { promiseTaintStep(source, this) }
|
||||
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
pred = source and succ = this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides classes for working with the `bluebird` library (http://bluebirdjs.com).
|
||||
*/
|
||||
|
||||
@@ -76,191 +76,6 @@ private class AnalyzedThisInArrayIterationFunction extends AnalyzedNode, DataFlo
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A definition of a `Promise` object.
|
||||
*/
|
||||
abstract class PromiseDefinition extends DataFlow::SourceNode {
|
||||
/** Gets the executor function of this promise object. */
|
||||
abstract DataFlow::FunctionNode getExecutor();
|
||||
|
||||
/** Gets the `resolve` parameter of the executor function. */
|
||||
DataFlow::ParameterNode getResolveParameter() { result = getExecutor().getParameter(0) }
|
||||
|
||||
/** Gets the `reject` parameter of the executor function. */
|
||||
DataFlow::ParameterNode getRejectParameter() { result = getExecutor().getParameter(1) }
|
||||
|
||||
/** Gets the `i`th callback handler installed by method `m`. */
|
||||
private DataFlow::FunctionNode getAHandler(string m, int i) {
|
||||
result = getAMethodCall(m).getCallback(i)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a function that handles promise resolution, including both
|
||||
* `then` handlers and `finally` handlers.
|
||||
*/
|
||||
DataFlow::FunctionNode getAResolveHandler() {
|
||||
result = getAHandler("then", 0) or
|
||||
result = getAFinallyHandler()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a function that handles promise rejection, including
|
||||
* `then` handlers, `catch` handlers and `finally` handlers.
|
||||
*/
|
||||
DataFlow::FunctionNode getARejectHandler() {
|
||||
result = getAHandler("then", 1) or
|
||||
result = getACatchHandler() or
|
||||
result = getAFinallyHandler()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a `catch` handler of this promise.
|
||||
*/
|
||||
DataFlow::FunctionNode getACatchHandler() { result = getAHandler("catch", 0) }
|
||||
|
||||
/**
|
||||
* Gets a `finally` handler of this promise.
|
||||
*/
|
||||
DataFlow::FunctionNode getAFinallyHandler() { result = getAHandler("finally", 0) }
|
||||
}
|
||||
|
||||
/** Holds if the `i`th callback handler is installed by method `m`. */
|
||||
private predicate hasHandler(DataFlow::InvokeNode promise, string m, int i) {
|
||||
exists(promise.getAMethodCall(m).getCallback(i))
|
||||
}
|
||||
|
||||
/**
|
||||
* A call that looks like a Promise.
|
||||
*
|
||||
* For example, this could be the call `promise(f).then(function(v){...})`
|
||||
*/
|
||||
class PromiseCandidate extends DataFlow::InvokeNode {
|
||||
PromiseCandidate() {
|
||||
hasHandler(this, "then", [0 .. 1]) or
|
||||
hasHandler(this, "catch", 0) or
|
||||
hasHandler(this, "finally", 0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A promise object created by the standard ECMAScript 2015 `Promise` constructor.
|
||||
*/
|
||||
private class ES2015PromiseDefinition extends PromiseDefinition, DataFlow::NewNode {
|
||||
ES2015PromiseDefinition() { this = DataFlow::globalVarRef("Promise").getAnInstantiation() }
|
||||
|
||||
override DataFlow::FunctionNode getExecutor() { result = getCallback(0) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A promise that is created and resolved with one or more value.
|
||||
*/
|
||||
abstract class PromiseCreationCall extends DataFlow::CallNode {
|
||||
/**
|
||||
* Gets the value this promise is resolved with.
|
||||
*/
|
||||
abstract DataFlow::Node getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* A promise that is created using a `.resolve()` call.
|
||||
*/
|
||||
abstract class ResolvedPromiseDefinition extends PromiseCreationCall {}
|
||||
|
||||
/**
|
||||
* A resolved promise created by the standard ECMAScript 2015 `Promise.resolve` function.
|
||||
*/
|
||||
class ResolvedES2015PromiseDefinition extends ResolvedPromiseDefinition {
|
||||
ResolvedES2015PromiseDefinition() {
|
||||
this = DataFlow::globalVarRef("Promise").getAMemberCall("resolve")
|
||||
}
|
||||
|
||||
override DataFlow::Node getValue() { result = getArgument(0) }
|
||||
}
|
||||
|
||||
/**
|
||||
* An aggregated promise produced either by `Promise.all` or `Promise.race`.
|
||||
*/
|
||||
class AggregateES2015PromiseDefinition extends PromiseCreationCall {
|
||||
AggregateES2015PromiseDefinition() {
|
||||
exists(string m | m = "all" or m = "race" |
|
||||
this = DataFlow::globalVarRef("Promise").getAMemberCall(m)
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getValue() {
|
||||
result = getArgument(0).getALocalSource().(DataFlow::ArrayCreationNode).getAnElement()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow edge from a promise reaction to the corresponding handler.
|
||||
*/
|
||||
private class PromiseFlowStep extends DataFlow::AdditionalFlowStep {
|
||||
PromiseDefinition p;
|
||||
|
||||
PromiseFlowStep() { this = p }
|
||||
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
pred = p.getResolveParameter().getACall().getArgument(0) and
|
||||
succ = p.getAResolveHandler().getParameter(0)
|
||||
or
|
||||
pred = p.getRejectParameter().getACall().getArgument(0) and
|
||||
succ = p.getARejectHandler().getParameter(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow edge from the exceptional return of the promise executor to the promise catch handler.
|
||||
* This only adds an edge from the exceptional return of the promise executor to a `.catch()` handler.
|
||||
*/
|
||||
private class PromiseExceptionalStep extends DataFlow::AdditionalFlowStep {
|
||||
PromiseDefinition promise;
|
||||
PromiseExceptionalStep() {
|
||||
promise = this
|
||||
}
|
||||
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
pred = promise.getExecutor().getExceptionalReturn() and
|
||||
succ = promise.getACatchHandler().getParameter(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint propagates from `pred` to `succ` through promises.
|
||||
*/
|
||||
predicate promiseTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
// from `x` to `new Promise((res, rej) => res(x))`
|
||||
pred = succ.(PromiseDefinition).getResolveParameter().getACall().getArgument(0)
|
||||
or
|
||||
// from `x` to `Promise.resolve(x)`
|
||||
pred = succ.(PromiseCreationCall).getValue()
|
||||
or
|
||||
exists(DataFlow::MethodCallNode thn, DataFlow::FunctionNode cb |
|
||||
thn.getMethodName() = "then" and cb = thn.getCallback(0)
|
||||
|
|
||||
// from `p` to `x` in `p.then(x => ...)`
|
||||
pred = thn.getReceiver() and
|
||||
succ = cb.getParameter(0)
|
||||
or
|
||||
// from `v` to `p.then(x => return v)`
|
||||
pred = cb.getAReturn() and
|
||||
succ = thn
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* An additional taint step that involves promises.
|
||||
*/
|
||||
private class PromiseTaintStep extends TaintTracking::AdditionalTaintStep {
|
||||
DataFlow::Node source;
|
||||
|
||||
PromiseTaintStep() { promiseTaintStep(source, this) }
|
||||
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
pred = source and succ = this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow step propagating the exception thrown from a callback to a method whose name coincides
|
||||
* a built-in Array iteration method, such as `forEach` or `map`.
|
||||
@@ -298,9 +113,7 @@ class StringReplaceCall extends DataFlow::MethodCallNode {
|
||||
}
|
||||
|
||||
/** Gets the regular expression passed as the first argument to `replace`, if any. */
|
||||
DataFlow::RegExpLiteralNode getRegExp() {
|
||||
result.flowsTo(getArgument(0))
|
||||
}
|
||||
DataFlow::RegExpLiteralNode getRegExp() { result.flowsTo(getArgument(0)) }
|
||||
|
||||
/** Gets a string that is being replaced by this call. */
|
||||
string getAReplacedString() {
|
||||
@@ -312,17 +125,13 @@ class StringReplaceCall extends DataFlow::MethodCallNode {
|
||||
* Gets the second argument of this call to `replace`, which is either a string
|
||||
* or a callback.
|
||||
*/
|
||||
DataFlow::Node getRawReplacement() {
|
||||
result = getArgument(1)
|
||||
}
|
||||
DataFlow::Node getRawReplacement() { result = getArgument(1) }
|
||||
|
||||
/**
|
||||
* Holds if this is a global replacement, that is, the first argument is a regular expression
|
||||
* with the `g` flag.
|
||||
*/
|
||||
predicate isGlobal() {
|
||||
getRegExp().isGlobal()
|
||||
}
|
||||
predicate isGlobal() { getRegExp().isGlobal() }
|
||||
|
||||
/**
|
||||
* Holds if this call to `replace` replaces `old` with `new`.
|
||||
|
||||
@@ -23,11 +23,11 @@
|
||||
| partial.js:5:15:5:24 | "tainted1" | partial.js:21:15:21:15 | x |
|
||||
| partial.js:5:15:5:24 | "tainted1" | partial.js:27:15:27:15 | x |
|
||||
| promises.js:2:16:2:24 | "tainted" | promises.js:7:16:7:18 | val |
|
||||
| promises.js:2:16:2:24 | "tainted" | promises.js:38:32:38:32 | v |
|
||||
| promises.js:11:22:11:31 | "resolved" | promises.js:19:20:19:20 | v |
|
||||
| promises.js:11:22:11:31 | "resolved" | promises.js:27:16:27:16 | v |
|
||||
| promises.js:12:22:12:31 | "rejected" | promises.js:21:20:21:20 | v |
|
||||
| promises.js:12:22:12:31 | "rejected" | promises.js:24:20:24:20 | v |
|
||||
| promises.js:12:22:12:31 | "rejected" | promises.js:27:16:27:16 | v |
|
||||
| promises.js:32:24:32:37 | "also tainted" | promises.js:38:32:38:32 | v |
|
||||
| properties2.js:7:14:7:21 | "source" | properties2.js:8:12:8:24 | foo(source).p |
|
||||
| properties2.js:7:14:7:21 | "source" | properties2.js:33:13:33:20 | getP(o3) |
|
||||
| properties.js:2:16:2:24 | "tainted" | properties.js:5:14:5:23 | a.someProp |
|
||||
|
||||
@@ -30,10 +30,8 @@
|
||||
| promises.js:2:16:2:24 | "tainted" | promises.js:7:16:7:18 | val |
|
||||
| promises.js:2:16:2:24 | "tainted" | promises.js:38:32:38:32 | v |
|
||||
| promises.js:11:22:11:31 | "resolved" | promises.js:19:20:19:20 | v |
|
||||
| promises.js:11:22:11:31 | "resolved" | promises.js:27:16:27:16 | v |
|
||||
| promises.js:12:22:12:31 | "rejected" | promises.js:21:20:21:20 | v |
|
||||
| promises.js:12:22:12:31 | "rejected" | promises.js:24:20:24:20 | v |
|
||||
| promises.js:12:22:12:31 | "rejected" | promises.js:27:16:27:16 | v |
|
||||
| promises.js:32:24:32:37 | "also tainted" | promises.js:38:32:38:32 | v |
|
||||
| properties2.js:7:14:7:21 | "source" | properties2.js:8:12:8:24 | foo(source).p |
|
||||
| properties2.js:7:14:7:21 | "source" | properties2.js:33:13:33:20 | getP(o3) |
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
promise2.catch((v) => {
|
||||
var rej_sink = v;
|
||||
});
|
||||
promise2.finally((v) => {
|
||||
promise2.finally((v) => { // no promise implementation sends an argument to the finally handler. So there is no data-flow here.
|
||||
var sink = v;
|
||||
});
|
||||
|
||||
|
||||
5
javascript/ql/test/library-tests/Promises/Flowsteps.qll
Normal file
5
javascript/ql/test/library-tests/Promises/Flowsteps.qll
Normal file
@@ -0,0 +1,5 @@
|
||||
import javascript
|
||||
|
||||
query predicate flowSteps(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
any(DataFlow::AdditionalFlowStep step).step(pred, succ)
|
||||
}
|
||||
56
javascript/ql/test/library-tests/Promises/flowsteps.js
Normal file
56
javascript/ql/test/library-tests/Promises/flowsteps.js
Normal file
@@ -0,0 +1,56 @@
|
||||
(async function () {
|
||||
function throws(resolve, reject) {
|
||||
throw new Error()
|
||||
}
|
||||
new Promise(throws)
|
||||
.catch((e) => console.log(e));
|
||||
|
||||
new Promise(throws)
|
||||
.then((val) => console.log(val), (error) => console.log(error));
|
||||
|
||||
try {
|
||||
await new Promise(throws);
|
||||
} catch (e2) {
|
||||
console.log(e2);
|
||||
}
|
||||
|
||||
new Promise((resolve, reject) => reject(3))
|
||||
.catch((e3) => console.log(e3));
|
||||
|
||||
try {
|
||||
await new Promise((resolve, reject) => reject(4));
|
||||
} catch(e4) {
|
||||
console.log(e4);
|
||||
}
|
||||
|
||||
new Promise(throws)
|
||||
.then(() => {})
|
||||
.catch((e5) => console.log(e5));
|
||||
|
||||
|
||||
new Promise(throws)
|
||||
.then(() => {})
|
||||
.catch((e6) => console.log(e6))
|
||||
.catch((e7) => console.log(e7));
|
||||
|
||||
var foo = await new Promise((resolve, reject) => resolve(8))
|
||||
|
||||
var bar = await new Promise((resolve, reject) => resolve(9)).then((x) => x + 2);
|
||||
|
||||
var p = Promise.resolve(3);
|
||||
var baz = await p.then((val) => val * 2);
|
||||
|
||||
var p2 = new Promise((resolve, reject) => {
|
||||
if (Math.random() > 0.5) {
|
||||
resolve(13);
|
||||
} else {
|
||||
reject(14);
|
||||
}
|
||||
});
|
||||
var quz = await p2.then(val => val * 4).catch(e => e * 3);
|
||||
|
||||
function returnsPromise() {
|
||||
return Promise.resolve(3);
|
||||
}
|
||||
var a = await returnsPromise();
|
||||
})();
|
||||
@@ -1,12 +1,27 @@
|
||||
test_ResolvedPromiseDefinition
|
||||
| flowsteps.js:40:11:40:28 | Promise.resolve(3) | flowsteps.js:40:27:40:27 | 3 |
|
||||
| flowsteps.js:53:12:53:29 | Promise.resolve(3) | flowsteps.js:53:28:53:28 | 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 |
|
||||
test_PromiseDefinition_getARejectHandler
|
||||
| flowsteps.js:5:3:5:21 | new Promise(throws) | flowsteps.js:6:12:6:32 | (e) => ... .log(e) |
|
||||
| flowsteps.js:8:3:8:21 | new Promise(throws) | flowsteps.js:9:38:9:66 | (error) ... (error) |
|
||||
| flowsteps.js:17:3:17:45 | new Pro ... ect(3)) | flowsteps.js:18:12:18:34 | (e3) => ... log(e3) |
|
||||
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:20:6:22:3 | (v) => ... v;\\n } |
|
||||
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:23:18:25:3 | (v) => ... v;\\n } |
|
||||
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:26:20:28:3 | (v) => ... v;\\n } |
|
||||
test_PromiseDefinition_getExecutor
|
||||
| flowsteps.js:5:3:5:21 | new Promise(throws) | flowsteps.js:2:3:4:3 | functio ... r()\\n } |
|
||||
| flowsteps.js:8:3:8:21 | new Promise(throws) | flowsteps.js:2:3:4:3 | functio ... r()\\n } |
|
||||
| flowsteps.js:12:11:12:29 | new Promise(throws) | flowsteps.js:2:3:4:3 | functio ... r()\\n } |
|
||||
| flowsteps.js:17:3:17:45 | new Pro ... ect(3)) | flowsteps.js:17:15:17:44 | (resolv ... ject(3) |
|
||||
| flowsteps.js:21:11:21:53 | new Pro ... ect(4)) | flowsteps.js:21:23:21:52 | (resolv ... ject(4) |
|
||||
| flowsteps.js:26:3:26:21 | new Promise(throws) | flowsteps.js:2:3:4:3 | functio ... r()\\n } |
|
||||
| flowsteps.js:31:3:31:21 | new Promise(throws) | flowsteps.js:2:3:4:3 | functio ... r()\\n } |
|
||||
| flowsteps.js:36:19:36:62 | new Pro ... lve(8)) | flowsteps.js:36:31:36:61 | (resolv ... olve(8) |
|
||||
| flowsteps.js:38:19:38:62 | new Pro ... lve(9)) | flowsteps.js:38:31:38:61 | (resolv ... olve(9) |
|
||||
| flowsteps.js:43:12:49:4 | new Pro ... }\\n }) | flowsteps.js:43:24:49:3 | (resolv ... }\\n } |
|
||||
| promises.js:3:17:5:4 | new Pro ... );\\n }) | promises.js:3:29:5:3 | functio ... e);\\n } |
|
||||
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:10:30:17:3 | (res, r ... e);\\n } |
|
||||
| promises.js:33:19:35:6 | new Pro ... \\n }) | promises.js:33:31:35:5 | functio ... ;\\n } |
|
||||
@@ -14,25 +29,103 @@ test_PromiseDefinition_getExecutor
|
||||
test_PromiseDefinition_getAFinallyHandler
|
||||
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:26:20:28:3 | (v) => ... v;\\n } |
|
||||
test_PromiseDefinition
|
||||
| flowsteps.js:5:3:5:21 | new Promise(throws) |
|
||||
| flowsteps.js:8:3:8:21 | new Promise(throws) |
|
||||
| flowsteps.js:12:11:12:29 | new Promise(throws) |
|
||||
| flowsteps.js:17:3:17:45 | new Pro ... ect(3)) |
|
||||
| flowsteps.js:21:11:21:53 | new Pro ... ect(4)) |
|
||||
| flowsteps.js:26:3:26:21 | new Promise(throws) |
|
||||
| flowsteps.js:31:3:31:21 | new Promise(throws) |
|
||||
| flowsteps.js:36:19:36:62 | new Pro ... lve(8)) |
|
||||
| flowsteps.js:38:19:38:62 | new Pro ... lve(9)) |
|
||||
| flowsteps.js:43:12:49:4 | new Pro ... }\\n }) |
|
||||
| promises.js:3:17:5:4 | new Pro ... );\\n }) |
|
||||
| promises.js:10:18:17:4 | new Pro ... );\\n }) |
|
||||
| promises.js:33:19:35:6 | new Pro ... \\n }) |
|
||||
| promises.js:43:19:45:6 | Q.Promi ... \\n }) |
|
||||
test_PromiseDefinition_getAResolveHandler
|
||||
| flowsteps.js:8:3:8:21 | new Promise(throws) | flowsteps.js:9:11:9:35 | (val) = ... og(val) |
|
||||
| flowsteps.js:26:3:26:21 | new Promise(throws) | flowsteps.js:27:11:27:18 | () => {} |
|
||||
| flowsteps.js:31:3:31:21 | new Promise(throws) | flowsteps.js:32:11:32:18 | () => {} |
|
||||
| flowsteps.js:38:19:38:62 | new Pro ... lve(9)) | flowsteps.js:38:69:38:80 | (x) => x + 2 |
|
||||
| flowsteps.js:43:12:49:4 | new Pro ... }\\n }) | flowsteps.js:50:27:50:40 | val => val * 4 |
|
||||
| promises.js:3:17:5:4 | new Pro ... );\\n }) | promises.js:6:16:8:3 | functio ... al;\\n } |
|
||||
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:18:17:20:3 | (v) => ... v;\\n } |
|
||||
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:26:20:28:3 | (v) => ... v;\\n } |
|
||||
| promises.js:33:19:35:6 | new Pro ... \\n }) | promises.js:36:18:38:5 | functio ... ;\\n } |
|
||||
| promises.js:43:19:45:6 | Q.Promi ... \\n }) | promises.js:46:18:48:5 | functio ... ;\\n } |
|
||||
test_PromiseDefinition_getRejectParameter
|
||||
| flowsteps.js:5:3:5:21 | new Promise(throws) | flowsteps.js:2:28:2:33 | reject |
|
||||
| flowsteps.js:8:3:8:21 | new Promise(throws) | flowsteps.js:2:28:2:33 | reject |
|
||||
| flowsteps.js:12:11:12:29 | new Promise(throws) | flowsteps.js:2:28:2:33 | reject |
|
||||
| flowsteps.js:17:3:17:45 | new Pro ... ect(3)) | flowsteps.js:17:25:17:30 | reject |
|
||||
| flowsteps.js:21:11:21:53 | new Pro ... ect(4)) | flowsteps.js:21:33:21:38 | reject |
|
||||
| flowsteps.js:26:3:26:21 | new Promise(throws) | flowsteps.js:2:28:2:33 | reject |
|
||||
| flowsteps.js:31:3:31:21 | new Promise(throws) | flowsteps.js:2:28:2:33 | reject |
|
||||
| flowsteps.js:36:19:36:62 | new Pro ... lve(8)) | flowsteps.js:36:41:36:46 | reject |
|
||||
| flowsteps.js:38:19:38:62 | new Pro ... lve(9)) | flowsteps.js:38:41:38:46 | reject |
|
||||
| flowsteps.js:43:12:49:4 | new Pro ... }\\n }) | flowsteps.js:43:34:43:39 | reject |
|
||||
| promises.js:3:17:5:4 | new Pro ... );\\n }) | promises.js:3:48:3:53 | reject |
|
||||
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:10:36:10:38 | rej |
|
||||
| promises.js:33:19:35:6 | new Pro ... \\n }) | promises.js:33:50:33:55 | reject |
|
||||
| promises.js:43:19:45:6 | Q.Promi ... \\n }) | promises.js:43:48:43:53 | reject |
|
||||
test_PromiseDefinition_getResolveParameter
|
||||
| flowsteps.js:5:3:5:21 | new Promise(throws) | flowsteps.js:2:19:2:25 | resolve |
|
||||
| flowsteps.js:8:3:8:21 | new Promise(throws) | flowsteps.js:2:19:2:25 | resolve |
|
||||
| flowsteps.js:12:11:12:29 | new Promise(throws) | flowsteps.js:2:19:2:25 | resolve |
|
||||
| flowsteps.js:17:3:17:45 | new Pro ... ect(3)) | flowsteps.js:17:16:17:22 | resolve |
|
||||
| flowsteps.js:21:11:21:53 | new Pro ... ect(4)) | flowsteps.js:21:24:21:30 | resolve |
|
||||
| flowsteps.js:26:3:26:21 | new Promise(throws) | flowsteps.js:2:19:2:25 | resolve |
|
||||
| flowsteps.js:31:3:31:21 | new Promise(throws) | flowsteps.js:2:19:2:25 | resolve |
|
||||
| flowsteps.js:36:19:36:62 | new Pro ... lve(8)) | flowsteps.js:36:32:36:38 | resolve |
|
||||
| flowsteps.js:38:19:38:62 | new Pro ... lve(9)) | flowsteps.js:38:32:38:38 | resolve |
|
||||
| flowsteps.js:43:12:49:4 | new Pro ... }\\n }) | flowsteps.js:43:25:43:31 | resolve |
|
||||
| promises.js:3:17:5:4 | new Pro ... );\\n }) | promises.js:3:39:3:45 | resolve |
|
||||
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:10:31:10:33 | res |
|
||||
| promises.js:33:19:35:6 | new Pro ... \\n }) | promises.js:33:41:33:47 | resolve |
|
||||
| promises.js:43:19:45:6 | Q.Promi ... \\n }) | promises.js:43:39:43:45 | resolve |
|
||||
test_PromiseDefinition_getACatchHandler
|
||||
| flowsteps.js:5:3:5:21 | new Promise(throws) | flowsteps.js:6:12:6:32 | (e) => ... .log(e) |
|
||||
| flowsteps.js:17:3:17:45 | new Pro ... ect(3)) | flowsteps.js:18:12:18:34 | (e3) => ... log(e3) |
|
||||
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:23:18:25:3 | (v) => ... v;\\n } |
|
||||
flowSteps
|
||||
| flowsteps.js:2:3:4:3 | exceptional return of function throws | flowsteps.js:6:13:6:13 | e |
|
||||
| flowsteps.js:2:3:4:3 | exceptional return of function throws | flowsteps.js:9:39:9:43 | error |
|
||||
| flowsteps.js:2:3:4:3 | exceptional return of function throws | flowsteps.js:13:12:13:13 | e2 |
|
||||
| flowsteps.js:2:3:4:3 | exceptional return of function throws | flowsteps.js:28:13:28:14 | e5 |
|
||||
| flowsteps.js:2:3:4:3 | exceptional return of function throws | flowsteps.js:33:13:33:14 | e6 |
|
||||
| flowsteps.js:17:15:17:44 | exceptional return of anonymous function | flowsteps.js:18:13:18:14 | e3 |
|
||||
| flowsteps.js:17:43:17:43 | 3 | flowsteps.js:18:13:18:14 | e3 |
|
||||
| flowsteps.js:21:23:21:52 | exceptional return of anonymous function | flowsteps.js:22:11:22:12 | e4 |
|
||||
| flowsteps.js:21:51:21:51 | 4 | flowsteps.js:22:11:22:12 | e4 |
|
||||
| flowsteps.js:27:11:27:18 | exceptional return of anonymous function | flowsteps.js:28:13:28:14 | e5 |
|
||||
| flowsteps.js:32:11:32:18 | exceptional return of anonymous function | flowsteps.js:33:13:33:14 | e6 |
|
||||
| flowsteps.js:33:12:33:34 | exceptional return of anonymous function | flowsteps.js:34:13:34:14 | e7 |
|
||||
| flowsteps.js:36:31:36:61 | exceptional return of anonymous function | flowsteps.js:1:2:56:1 | exceptional return of anonymous function |
|
||||
| flowsteps.js:36:60:36:60 | 8 | flowsteps.js:36:13:36:62 | await n ... lve(8)) |
|
||||
| flowsteps.js:38:31:38:61 | exceptional return of anonymous function | flowsteps.js:1:2:56:1 | exceptional return of anonymous function |
|
||||
| flowsteps.js:38:60:38:60 | 9 | flowsteps.js:38:70:38:70 | x |
|
||||
| flowsteps.js:38:69:38:80 | exceptional return of anonymous function | flowsteps.js:1:2:56:1 | exceptional return of anonymous function |
|
||||
| flowsteps.js:38:76:38:80 | x + 2 | flowsteps.js:38:13:38:81 | await n ... x + 2) |
|
||||
| flowsteps.js:40:27:40:27 | 3 | flowsteps.js:41:27:41:29 | val |
|
||||
| flowsteps.js:41:26:41:41 | exceptional return of anonymous function | flowsteps.js:1:2:56:1 | exceptional return of anonymous function |
|
||||
| flowsteps.js:41:35:41:41 | val * 2 | flowsteps.js:41:13:41:42 | await p ... al * 2) |
|
||||
| flowsteps.js:43:24:49:3 | exceptional return of anonymous function | flowsteps.js:50:49:50:49 | e |
|
||||
| flowsteps.js:45:12:45:13 | 13 | flowsteps.js:50:27:50:29 | val |
|
||||
| flowsteps.js:47:11:47:12 | 14 | flowsteps.js:50:49:50:49 | e |
|
||||
| flowsteps.js:50:27:50:40 | exceptional return of anonymous function | flowsteps.js:50:49:50:49 | e |
|
||||
| flowsteps.js:50:34:50:40 | val * 4 | flowsteps.js:50:13:50:59 | await p ... e * 3) |
|
||||
| flowsteps.js:50:49:50:58 | exceptional return of anonymous function | flowsteps.js:1:2:56:1 | exceptional return of anonymous function |
|
||||
| flowsteps.js:50:54:50:58 | e * 3 | flowsteps.js:50:13:50:59 | await p ... e * 3) |
|
||||
| flowsteps.js:53:28:53:28 | 3 | flowsteps.js:55:11:55:32 | await r ... omise() |
|
||||
| promises.js:4:13:4:18 | source | promises.js:6:26:6:28 | val |
|
||||
| promises.js:10:30:17:3 | exceptional return of anonymous function | promises.js:20:7:20:7 | v |
|
||||
| promises.js:10:30:17:3 | exceptional return of anonymous function | promises.js:23:19:23:19 | v |
|
||||
| promises.js:14:11:14:20 | res_source | promises.js:18:18:18:18 | v |
|
||||
| promises.js:16:11:16:20 | rej_source | promises.js:20:7:20:7 | v |
|
||||
| promises.js:16:11:16:20 | rej_source | promises.js:23:19:23:19 | v |
|
||||
| promises.js:34:17:34:22 | source | promises.js:36:28:36:30 | val |
|
||||
| promises.js:44:17:44:22 | source | promises.js:46:28:46:30 | val |
|
||||
| promises.js:53:35:53:40 | source | promises.js:54:28:54:30 | val |
|
||||
| promises.js:62:35:62:40 | source | promises.js:63:28:63:30 | val |
|
||||
| promises.js:71:21:71:26 | source | promises.js:71:34:71:36 | val |
|
||||
|
||||
@@ -7,3 +7,4 @@ import PromiseDefinition_getAResolveHandler
|
||||
import PromiseDefinition_getRejectParameter
|
||||
import PromiseDefinition_getResolveParameter
|
||||
import PromiseDefinition_getACatchHandler
|
||||
import Flowsteps
|
||||
Reference in New Issue
Block a user