Merge pull request #2618 from erik-krogh/ExceptionalPromise

Approved by asgerf
This commit is contained in:
semmle-qlci
2020-01-31 07:59:09 +00:00
committed by GitHub
18 changed files with 1084 additions and 276 deletions

View File

@@ -1,9 +1,385 @@
/**
* 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 of a Promise.
* The data-flow is based on pseudo-properties rather than tainting the Promise object (which is what `PromiseTaintStep` does).
*/
private module PromiseFlow {
/**
* Gets the pseudo-field used to describe resolved values in a promise.
*/
string resolveField() {
result = "$PromiseResolveField$"
}
/**
* Gets the pseudo-field used to describe rejected values in a promise.
*/
string rejectField() {
result = "$PromiseRejectField$"
}
/**
* A flow step describing a promise definition.
*
* The resolved/rejected value is written to a pseudo-field on the promise.
*/
class PromiseDefitionStep extends DataFlow::AdditionalFlowStep {
PromiseDefinition promise;
PromiseDefitionStep() {
this = promise
}
override predicate storeStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = resolveField() and
pred = promise.getResolveParameter().getACall().getArgument(0) and
succ = this
or
prop = rejectField() and
(
pred = promise.getRejectParameter().getACall().getArgument(0) or
pred = promise.getExecutor().getExceptionalReturn()
) and
succ = this
}
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
// Copy the value of a resolved promise to the value of this promise.
prop = resolveField() and
pred = promise.getResolveParameter().getACall().getArgument(0) and
succ = this
}
}
/**
* A flow step describing the a Promise.resolve (and similar) call.
*/
class CreationStep extends DataFlow::AdditionalFlowStep {
PromiseCreationCall promise;
CreationStep() {
this = promise
}
override predicate storeStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = resolveField() and
pred = promise.getValue() and
succ = this
}
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
// Copy the value of a resolved promise to the value of this promise.
prop = resolveField() and
pred = promise.getValue() and
succ = this
}
}
/**
* A load step loading the pseudo-field describing that the promise is rejected.
* The rejected value is thrown as a exception.
*/
class AwaitStep extends DataFlow::AdditionalFlowStep {
DataFlow::Node operand;
AwaitExpr await;
AwaitStep() {
this.getEnclosingExpr() = await and
operand.getEnclosingExpr() = await.getOperand()
}
override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = resolveField() and
succ = this and
pred = operand
or
prop = rejectField() and
succ = await.getExceptionTarget() and
pred = operand
}
}
/**
* A flow step describing the data-flow related to the `.then` method of a promise.
*/
class ThenStep extends DataFlow::AdditionalFlowStep, DataFlow::MethodCallNode {
ThenStep() {
this.getMethodName() = "then"
}
override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = resolveField() and
pred = getReceiver() and
succ = getCallback(0).getParameter(0)
or
prop = rejectField() and
pred = getReceiver() and
succ = getCallback(1).getParameter(0)
}
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
not exists(this.getArgument(1)) and
prop = rejectField() and
pred = getReceiver() and
succ = this
or
// read the value of a resolved/rejected promise that is returned
(prop = rejectField() or prop = resolveField()) and
pred = getCallback([0..1]).getAReturn() and
succ = this
}
override predicate storeStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = resolveField() and
pred = getCallback([0..1]).getAReturn() and
succ = this
or
prop = rejectField() and
pred = getCallback([0..1]).getExceptionalReturn() and
succ = this
}
}
/**
* A flow step describing the data-flow related to the `.catch` method of a promise.
*/
class CatchStep extends DataFlow::AdditionalFlowStep, DataFlow::MethodCallNode {
CatchStep() {
this.getMethodName() = "catch"
}
override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = rejectField() and
pred = getReceiver() and
succ = getCallback(0).getParameter(0)
}
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = resolveField() and
pred = getReceiver().getALocalSource() and
succ = this
or
// read the value of a resolved/rejected promise that is returned
(prop = rejectField() or prop = resolveField()) and
pred = getCallback(0).getAReturn() and
succ = this
}
override predicate storeStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = rejectField() and
pred = getCallback(0).getExceptionalReturn() and
succ = this
or
prop = resolveField() and
pred = getCallback(0).getAReturn() and
succ = this
}
}
/**
* A flow step describing the data-flow related to the `.finally` method of a promise.
*/
class FinallyStep extends DataFlow::AdditionalFlowStep, DataFlow::MethodCallNode {
FinallyStep() {
this.getMethodName() = "finally"
}
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
(prop = resolveField() or prop = rejectField()) and
pred = getReceiver() and
succ = this
or
// read the value of a rejected promise that is returned
prop = rejectField() and
pred = getCallback(0).getAReturn() and
succ = this
}
override predicate storeStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = rejectField() and
pred = getCallback(0).getExceptionalReturn() and
succ = this
}
}
}
/**
* 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 |
thn.getMethodName() = "then"
|
// from `p` to `x` in `p.then(x => ...)`
pred = thn.getReceiver() and
succ = thn.getCallback(0).getParameter(0)
or
// from `v` to `p.then(x => return v)`
pred = thn.getCallback([0..1]).getAReturn() and
succ = thn
)
or
exists(DataFlow::MethodCallNode catch | catch.getMethodName() = "catch" |
// from `p` to `p.catch(..)`
pred = catch.getReceiver() and
succ = catch
or
// from `v` to `p.catch(x => return v)`
pred = catch.getCallback(0).getAReturn() and
succ = catch
)
or
// from `p` to `p.finally(..)`
exists(DataFlow::MethodCallNode finally | finally.getMethodName() = "finally" |
pred = finally.getReceiver() and
succ = finally
)
or
// from `x` to `await x`
exists(AwaitExpr await |
pred.getEnclosingExpr() = await.getOperand() and
succ.getEnclosingExpr() = await
)
}
/**
* 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).
*/

View File

@@ -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`.

View File

@@ -223,6 +223,29 @@ abstract class Configuration extends string {
predicate hasFlowPath(SourcePathNode source, SinkPathNode sink) {
flowsTo(source, _, sink, _, this)
}
/**
* EXPERIMENTAL. This API may change in the future.
*
* Holds if `pred` should be stored in the object `succ` under the property `prop`.
*/
predicate isAdditionalStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
/**
* EXPERIMENTAL. This API may change in the future.
*
* Holds if the property `prop` of the object `pred` should be loaded into `succ`.
*/
predicate isAdditionalLoadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
/**
* EXPERIMENTAL. This API may change in the future.
*
* Holds if the property `prop` should be copied from the object `pred` to the object `succ`.
*/
predicate isAdditionalLoadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
none()
}
}
/**
@@ -307,11 +330,8 @@ abstract class BarrierGuardNode extends DataFlow::Node {
// 1) `nd` is a use of a refinement node that blocks its input variable
exists(SsaRefinementNode ref, boolean outcome |
nd = DataFlow::ssaDefinitionNode(ref) and
forex(SsaVariable input | input = ref.getAnInput() |
getEnclosingExpr() = ref.getGuard().getTest() and
outcome = ref.getGuard().(ConditionGuardNode).getOutcome() and
barrierGuardBlocksExpr(this, outcome, input.getAUse(), label)
)
outcome = ref.getGuard().(ConditionGuardNode).getOutcome() and
ssaRefinementBlocks(outcome, ref, label)
)
or
// 2) `nd` is an instance of an access path `p`, and dominated by a barrier for `p`
@@ -324,6 +344,18 @@ abstract class BarrierGuardNode extends DataFlow::Node {
)
}
/**
* Holds if there exists an input variable of `ref` that blocks the label `label`.
*
* This predicate is outlined to give the optimizer a hint about the join ordering.
*/
private predicate ssaRefinementBlocks(boolean outcome, SsaRefinementNode ref, string label) {
getEnclosingExpr() = ref.getGuard().getTest() and
forex(SsaVariable input | input = ref.getAnInput() |
barrierGuardBlocksExpr(this, outcome, input.getAUse(), label)
)
}
/**
* Holds if this node blocks expression `e` provided it evaluates to `outcome`.
*
@@ -338,11 +370,13 @@ abstract class BarrierGuardNode extends DataFlow::Node {
}
/**
* Holds if data flow node `nd` acts as a barrier for data flow.
*
* `label` is bound to the blocked label, or the empty string if all labels should be blocked.
*/
private predicate barrierGuardBlocksExpr(BarrierGuardNode guard, boolean outcome, Expr test, string label) {
* Holds if data flow node `nd` acts as a barrier for data flow.
*
* `label` is bound to the blocked label, or the empty string if all labels should be blocked.
*/
private predicate barrierGuardBlocksExpr(
BarrierGuardNode guard, boolean outcome, Expr test, string label
) {
guard.blocks(outcome, test) and label = ""
or
guard.blocks(outcome, test, label)
@@ -353,23 +387,29 @@ private predicate barrierGuardBlocksExpr(BarrierGuardNode guard, boolean outcome
}
/**
* Holds if data flow node `nd` acts as a barrier for data flow due to aliasing through
* an access path.
*
* `label` is bound to the blocked label, or the empty string if all labels should be blocked.
*/
* Holds if data flow node `nd` acts as a barrier for data flow due to aliasing through
* an access path.
*
* `label` is bound to the blocked label, or the empty string if all labels should be blocked.
*/
pragma[noinline]
private predicate barrierGuardBlocksAccessPath(BarrierGuardNode guard, boolean outcome, AccessPath ap, string label) {
private predicate barrierGuardBlocksAccessPath(
BarrierGuardNode guard, boolean outcome, AccessPath ap, string label
) {
barrierGuardBlocksExpr(guard, outcome, ap.getAnInstance(), label)
}
/**
* Holds if `guard` should block flow along the edge `pred -> succ`.
*
* `label` is bound to the blocked label, or the empty string if all labels should be blocked.
*/
private predicate barrierGuardBlocksEdge(BarrierGuardNode guard, DataFlow::Node pred, DataFlow::Node succ, string label) {
exists(SsaVariable input, SsaPhiNode phi, BasicBlock bb, ConditionGuardNode cond, boolean outcome |
* Holds if `guard` should block flow along the edge `pred -> succ`.
*
* `label` is bound to the blocked label, or the empty string if all labels should be blocked.
*/
private predicate barrierGuardBlocksEdge(
BarrierGuardNode guard, DataFlow::Node pred, DataFlow::Node succ, string label
) {
exists(
SsaVariable input, SsaPhiNode phi, BasicBlock bb, ConditionGuardNode cond, boolean outcome
|
pred = DataFlow::ssaDefinitionNode(input) and
succ = DataFlow::ssaDefinitionNode(phi) and
input = phi.getInputFromBlock(bb) and
@@ -399,7 +439,9 @@ private predicate isBarrierEdge(Configuration cfg, DataFlow::Node pred, DataFlow
* Holds if there is a labeled barrier edge `pred -> succ` in `cfg` either through an explicit barrier edge
* or one implied by a barrier guard.
*/
private predicate isLabeledBarrierEdge(Configuration cfg, DataFlow::Node pred, DataFlow::Node succ, DataFlow::FlowLabel label) {
private predicate isLabeledBarrierEdge(
Configuration cfg, DataFlow::Node pred, DataFlow::Node succ, DataFlow::FlowLabel label
) {
cfg.isBarrierEdge(pred, succ, label)
or
exists(DataFlow::BarrierGuardNode guard |
@@ -449,6 +491,30 @@ abstract class AdditionalFlowStep extends DataFlow::Node {
) {
none()
}
/**
* EXPERIMENTAL. This API may change in the future.
*
* Holds if `pred` should be stored in the object `succ` under the property `prop`.
*/
cached
predicate storeStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
/**
* EXPERIMENTAL. This API may change in the future.
*
* Holds if the property `prop` of the object `pred` should be loaded into `succ`.
*/
cached
predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
/**
* EXPERIMENTAL. This API may change in the future.
*
* Holds if the property `prop` should be copied from the object `pred` to the object `succ`.
*/
cached
predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
}
/**
@@ -551,6 +617,9 @@ private predicate exploratoryFlowStep(
basicFlowStep(pred, succ, _, cfg) or
basicStoreStep(pred, succ, _) or
basicLoadStep(pred, succ, _) or
isAdditionalStoreStep(pred, succ, _, cfg) or
isAdditionalLoadStep(pred, succ, _, cfg) or
isAdditionalLoadStoreStep(pred, succ, _, cfg) or
// the following two disjuncts taken together over-approximate flow through
// higher-order calls
callback(pred, succ) or
@@ -712,6 +781,9 @@ private predicate storeStep(
basicStoreStep(pred, succ, prop) and
summary = PathSummary::level()
or
isAdditionalStoreStep(pred, succ, prop, cfg) and
summary = PathSummary::level()
or
exists(Function f, DataFlow::Node mid |
// `f` stores its parameter `pred` in property `prop` of a value that flows back to the caller,
// and `succ` is an invocation of `f`
@@ -719,6 +791,10 @@ private predicate storeStep(
(
returnedPropWrite(f, _, prop, mid)
or
exists(DataFlow::SourceNode base | base.flowsToExpr(f.getAReturnedExpr()) |
isAdditionalStoreStep(mid, base, prop, cfg)
)
or
succ instanceof DataFlow::NewNode and
receiverPropWrite(f, prop, mid)
)
@@ -729,12 +805,16 @@ private predicate storeStep(
* Holds if `f` may `read` property `prop` of parameter `parm`.
*/
private predicate parameterPropRead(
Function f, DataFlow::Node invk, DataFlow::Node arg, string prop, DataFlow::PropRead read,
Function f, DataFlow::Node invk, DataFlow::Node arg, string prop, DataFlow::Node read,
DataFlow::Configuration cfg
) {
exists(DataFlow::SourceNode parm |
callInputStep(f, invk, arg, parm, cfg) and
read = parm.getAPropertyRead(prop)
(
read = parm.getAPropertyRead(prop)
or
exists(DataFlow::Node use | parm.flowsTo(use) | isAdditionalLoadStep(use, read, prop, cfg))
)
)
}
@@ -756,6 +836,39 @@ private predicate reachesReturn(
)
}
/**
* Holds if the property `prop` of the object `pred` should be loaded into `succ`.
*/
private predicate isAdditionalLoadStep(
DataFlow::Node pred, DataFlow::Node succ, string prop, DataFlow::Configuration cfg
) {
any(AdditionalFlowStep s).loadStep(pred, succ, prop)
or
cfg.isAdditionalLoadStep(pred, succ, prop)
}
/**
* Holds if `pred` should be stored in the object `succ` under the property `prop`.
*/
private predicate isAdditionalStoreStep(
DataFlow::Node pred, DataFlow::Node succ, string prop, DataFlow::Configuration cfg
) {
any(AdditionalFlowStep s).storeStep(pred, succ, prop)
or
cfg.isAdditionalStoreStep(pred, succ, prop)
}
/**
* Holds if the property `prop` should be copied from the object `pred` to the object `succ`.
*/
private predicate isAdditionalLoadStoreStep(
DataFlow::Node pred, DataFlow::Node succ, string prop, DataFlow::Configuration cfg
) {
any(AdditionalFlowStep s).loadStoreStep(pred, succ, prop)
or
cfg.isAdditionalLoadStoreStep(pred, succ, prop)
}
/**
* Holds if property `prop` of `pred` may flow into `succ` along a path summarized by
* `summary`.
@@ -767,7 +880,10 @@ private predicate loadStep(
basicLoadStep(pred, succ, prop) and
summary = PathSummary::level()
or
exists(Function f, DataFlow::PropRead read |
isAdditionalLoadStep(pred, succ, prop, cfg) and
summary = PathSummary::level()
or
exists(Function f, DataFlow::Node read |
parameterPropRead(f, succ, pred, prop, read, cfg) and
reachesReturn(f, read, cfg, summary)
)
@@ -788,7 +904,12 @@ private predicate reachableFromStoreBase(
or
exists(DataFlow::Node mid, PathSummary oldSummary, PathSummary newSummary |
reachableFromStoreBase(prop, rhs, mid, cfg, oldSummary) and
flowStep(mid, cfg, nd, newSummary) and
(
flowStep(mid, cfg, nd, newSummary)
or
isAdditionalLoadStoreStep(mid, nd, prop, cfg) and
newSummary = PathSummary::level()
) and
summary = oldSummary.appendValuePreserving(newSummary)
)
}
@@ -996,19 +1117,19 @@ private predicate onPath(DataFlow::Node nd, DataFlow::Configuration cfg, PathSum
* Holds if there is a configuration that has at least one source and at least one sink.
*/
pragma[noinline]
private predicate isLive() { exists(DataFlow::Configuration cfg | isSource(_, cfg, _) and isSink(_, cfg, _)) }
private predicate isLive() {
exists(DataFlow::Configuration cfg | isSource(_, cfg, _) and isSink(_, cfg, _))
}
/**
* A data flow node on an inter-procedural path from a source.
*/
private newtype TPathNode =
MkSourceNode(DataFlow::Node nd, DataFlow::Configuration cfg) { isSourceNode(nd, cfg, _) }
or
MkSourceNode(DataFlow::Node nd, DataFlow::Configuration cfg) { isSourceNode(nd, cfg, _) } or
MkMidNode(DataFlow::Node nd, DataFlow::Configuration cfg, PathSummary summary) {
isLive() and
onPath(nd, cfg, summary)
}
or
} or
MkSinkNode(DataFlow::Node nd, DataFlow::Configuration cfg) { isSinkNode(nd, cfg, _) }
/**
@@ -1069,9 +1190,7 @@ class PathNode extends TPathNode {
}
/** Holds if this path node wraps data-flow node `nd` and configuration `c`. */
predicate wraps(DataFlow::Node n, DataFlow::Configuration c) {
nd = n and cfg = c
}
predicate wraps(DataFlow::Node n, DataFlow::Configuration c) { nd = n and cfg = c }
/** Gets the underlying configuration of this path node. */
DataFlow::Configuration getConfiguration() { result = cfg }
@@ -1080,9 +1199,7 @@ class PathNode extends TPathNode {
DataFlow::Node getNode() { result = nd }
/** Gets a successor node of this path node. */
final PathNode getASuccessor() {
result = getASuccessor(this)
}
final PathNode getASuccessor() { result = getASuccessor(this) }
/** Gets a textual representation of this path node. */
string toString() { result = nd.toString() }
@@ -1126,7 +1243,10 @@ private MidPathNode finalMidNode(SinkPathNode snk) {
* This helper predicate exists to clarify the intended join order in `getASuccessor` below.
*/
pragma[noinline]
private predicate midNodeStep(PathNode nd, DataFlow::Node predNd, Configuration cfg, PathSummary summary, DataFlow::Node succNd, PathSummary newSummary) {
private predicate midNodeStep(
PathNode nd, DataFlow::Node predNd, Configuration cfg, PathSummary summary, DataFlow::Node succNd,
PathSummary newSummary
) {
nd = MkMidNode(predNd, cfg, summary) and
flowStep(predNd, id(cfg), succNd, newSummary)
}
@@ -1139,7 +1259,10 @@ private PathNode getASuccessor(PathNode nd) {
result = initialMidNode(nd)
or
// mid node to mid node
exists(Configuration cfg, DataFlow::Node predNd, PathSummary summary, DataFlow::Node succNd, PathSummary newSummary |
exists(
Configuration cfg, DataFlow::Node predNd, PathSummary summary, DataFlow::Node succNd,
PathSummary newSummary
|
midNodeStep(nd, predNd, cfg, summary, succNd, newSummary) and
result = MkMidNode(succNd, id(cfg), summary.append(newSummary))
)
@@ -1210,9 +1333,7 @@ class SinkPathNode extends PathNode, MkSinkNode {
*/
module PathGraph {
/** Holds if `nd` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode nd) {
not nd.(MidPathNode).isHidden()
}
query predicate nodes(PathNode nd) { not nd.(MidPathNode).isHidden() }
/**
* Gets a node to which data from `nd` may flow in one step, skipping over hidden nodes.
@@ -1220,7 +1341,8 @@ module PathGraph {
private PathNode succ0(PathNode nd) {
result = getASuccessorIfHidden*(nd.getASuccessor()) and
// skip hidden nodes
nodes(nd) and nodes(result)
nodes(nd) and
nodes(result)
}
/**
@@ -1260,27 +1382,21 @@ module PathGraph {
}
}
/**
* Gets an operand of the given `&&` operator.
*
* We use this to construct the transitive closure over a relation
* that does not include all of `BinaryExpr.getAnOperand`.
*/
private Expr getALogicalAndOperand(LogAndExpr e) { result = e.getAnOperand() }
/**
* Gets an operand of the given `&&` operator.
*
* We use this to construct the transitive closure over a relation
* that does not include all of `BinaryExpr.getAnOperand`.
*/
private Expr getALogicalAndOperand(LogAndExpr e) {
result = e.getAnOperand()
}
/**
* Gets an operand of the given `||` operator.
*
* We use this to construct the transitive closure over a relation
* that does not include all of `BinaryExpr.getAnOperand`.
*/
private Expr getALogicalOrOperand(LogOrExpr e) {
result = e.getAnOperand()
}
* Gets an operand of the given `||` operator.
*
* We use this to construct the transitive closure over a relation
* that does not include all of `BinaryExpr.getAnOperand`.
*/
private Expr getALogicalOrOperand(LogOrExpr e) { result = e.getAnOperand() }
/**
* A `BarrierGuardNode` that controls which data flow
@@ -1295,8 +1411,8 @@ abstract class AdditionalBarrierGuardNode extends BarrierGuardNode {
}
/**
* A function that returns the result of a barrier guard.
*/
* A function that returns the result of a barrier guard.
*/
private class BarrierGuardFunction extends Function {
DataFlow::ParameterNode sanitizedParameter;
BarrierGuardNode guard;
@@ -1329,8 +1445,8 @@ private class BarrierGuardFunction extends Function {
}
/**
* Holds if this function sanitizes argument `e` of call `call`, provided the call evaluates to `outcome`.
*/
* Holds if this function sanitizes argument `e` of call `call`, provided the call evaluates to `outcome`.
*/
predicate isBarrierCall(DataFlow::CallNode call, Expr e, boolean outcome, string lbl) {
exists(DataFlow::Node arg |
arg.asExpr() = e and
@@ -1343,22 +1459,20 @@ private class BarrierGuardFunction extends Function {
}
/**
* Holds if this function applies to the flow in `cfg`.
*/
* Holds if this function applies to the flow in `cfg`.
*/
predicate appliesTo(Configuration cfg) { cfg.isBarrierGuard(guard) }
}
/**
* A call that sanitizes an argument.
*/
* A call that sanitizes an argument.
*/
private class AdditionalBarrierGuardCall extends AdditionalBarrierGuardNode, DataFlow::CallNode {
BarrierGuardFunction f;
AdditionalBarrierGuardCall() { f.isBarrierCall(this, _, _, _) }
override predicate blocks(boolean outcome, Expr e) {
f.isBarrierCall(this, e, outcome, "")
}
override predicate blocks(boolean outcome, Expr e) { f.isBarrierCall(this, e, outcome, "") }
predicate internalBlocksLabel(boolean outcome, Expr e, DataFlow::FlowLabel label) {
f.isBarrierCall(this, e, outcome, label)

View File

@@ -0,0 +1 @@
| tst.js:4:15:4:22 | "source" | tst.js:9:7:9:24 | readTaint(tainted) |

View File

@@ -0,0 +1,26 @@
import javascript
class Configuration extends TaintTracking::Configuration {
Configuration() { this = "PromiseFlowTestingConfig" }
override predicate isSource(DataFlow::Node source) {
source.getEnclosingExpr().getStringValue() = "source"
}
override predicate isSink(DataFlow::Node sink) {
any(DataFlow::InvokeNode call | call.getCalleeName() = "sink").getAnArgument() = sink
}
// When the source code states that "foo" is being read, "bar" is additionally being read.
override predicate isAdditionalLoadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
exists(DataFlow::PropRead read | read = succ |
read.getBase() = pred and
read.getPropertyName() = "foo"
) and
prop = "bar"
}
}
from DataFlow::Node pred, DataFlow::Node succ, Configuration cfg
where cfg.hasFlow(pred, succ)
select pred, succ

View File

@@ -0,0 +1,10 @@
// When the source code states that "foo" is being read, "bar" is additionally being read.
(function () {
var source = "source";
var tainted = { bar: source };
function readTaint(x) {
return x.foo;
}
sink(readTaint(tainted));
})();

View File

@@ -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 |

View File

@@ -24,11 +24,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 |

View File

@@ -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) |

View File

@@ -0,0 +1,32 @@
| missing | promises.js:1:2:1:2 | source | promises.js:6:26:6:28 | val |
| missing | promises.js:1:2:1:2 | source | promises.js:7:16:7:18 | val |
| missing | promises.js:1:2:1:2 | source | promises.js:37:11:37:11 | v |
| missing | promises.js:1:2:1:2 | source | promises.js:38:32:38:32 | v |
| missing | promises.js:2:16:2:24 | "tainted" | promises.js:6:26:6:28 | val |
| missing | promises.js:2:16:2:24 | "tainted" | promises.js:7:16:7:18 | val |
| missing | promises.js:2:16:2:24 | "tainted" | promises.js:37:11:37:11 | v |
| missing | promises.js:2:16:2:24 | "tainted" | promises.js:38:32:38:32 | v |
| missing | promises.js:10:30:17:3 | exceptional return of anonymous function | promises.js:20:7:20:7 | v |
| missing | promises.js:10:30:17:3 | exceptional return of anonymous function | promises.js:21:20:21:20 | v |
| missing | promises.js:10:30:17:3 | exceptional return of anonymous function | promises.js:23:19:23:19 | v |
| missing | promises.js:10:30:17:3 | exceptional return of anonymous function | promises.js:24:20:24:20 | v |
| missing | promises.js:11:22:11:31 | "resolved" | promises.js:18:18:18:18 | v |
| missing | promises.js:11:22:11:31 | "resolved" | promises.js:19:20:19:20 | v |
| missing | promises.js:12:22:12:31 | "rejected" | promises.js:20:7:20:7 | v |
| missing | promises.js:12:22:12:31 | "rejected" | promises.js:21:20:21:20 | v |
| missing | promises.js:12:22:12:31 | "rejected" | promises.js:23:19:23:19 | v |
| missing | promises.js:12:22:12:31 | "rejected" | promises.js:24:20:24:20 | v |
| missing | promises.js:13:9:13:21 | exceptional return of Math.random() | promises.js:20:7:20:7 | v |
| missing | promises.js:13:9:13:21 | exceptional return of Math.random() | promises.js:21:20:21:20 | v |
| missing | promises.js:13:9:13:21 | exceptional return of Math.random() | promises.js:23:19:23:19 | v |
| missing | promises.js:13:9:13:21 | exceptional return of Math.random() | promises.js:24:20:24:20 | v |
| missing | promises.js:14:7:14:21 | exceptional return of res(res_source) | promises.js:20:7:20:7 | v |
| missing | promises.js:14:7:14:21 | exceptional return of res(res_source) | promises.js:21:20:21:20 | v |
| missing | promises.js:14:7:14:21 | exceptional return of res(res_source) | promises.js:23:19:23:19 | v |
| missing | promises.js:14:7:14:21 | exceptional return of res(res_source) | promises.js:24:20:24:20 | v |
| missing | promises.js:16:7:16:21 | exceptional return of rej(rej_source) | promises.js:20:7:20:7 | v |
| missing | promises.js:16:7:16:21 | exceptional return of rej(rej_source) | promises.js:21:20:21:20 | v |
| missing | promises.js:16:7:16:21 | exceptional return of rej(rej_source) | promises.js:23:19:23:19 | v |
| missing | promises.js:16:7:16:21 | exceptional return of rej(rej_source) | promises.js:24:20:24:20 | v |
| missing | promises.js:32:24:32:37 | "also tainted" | promises.js:37:11:37:11 | v |
| missing | promises.js:32:24:32:37 | "also tainted" | promises.js:38:32:38:32 | v |

View File

@@ -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;
});

View File

@@ -1,4 +1,74 @@
| additional-promises.js:2:13:2:57 | new Pin ... ct) {}) |
| flow.js:7:11:7:59 | new Pro ... ource)) |
| flow.js:10:11:10:58 | new Pro ... ource)) |
| flow.js:13:11:13:58 | new Pro ... ource)) |
| flow.js:20:2:20:24 | Promise ... source) |
| flow.js:22:2:22:24 | Promise ... source) |
| flow.js:24:2:24:49 | new Pro ... ource)) |
| flow.js:26:2:26:49 | new Pro ... ource)) |
| flow.js:28:2:28:23 | Promise ... ("foo") |
| flow.js:28:2:28:41 | Promise ... source) |
| flow.js:30:2:30:24 | Promise ... source) |
| flow.js:30:2:30:41 | Promise ... "foo") |
| flow.js:32:2:32:49 | new Pro ... ource)) |
| flow.js:34:2:34:24 | Promise ... source) |
| flow.js:34:2:34:41 | Promise ... => { }) |
| flow.js:36:11:36:33 | Promise ... source) |
| flow.js:37:11:37:29 | p5.catch(() => { }) |
| flow.js:40:2:40:49 | new Pro ... ource)) |
| flow.js:40:2:40:65 | new Pro ... => { }) |
| flow.js:42:2:42:49 | new Pro ... ource)) |
| flow.js:42:2:42:76 | new Pro ... => { }) |
| flow.js:44:2:44:24 | Promise ... source) |
| flow.js:44:2:44:41 | Promise ... => { }) |
| flow.js:44:2:44:58 | Promise ... => { }) |
| flow.js:44:2:44:75 | Promise ... => { }) |
| flow.js:46:2:46:24 | Promise ... source) |
| flow.js:46:2:46:43 | Promise ... => { }) |
| flow.js:48:2:48:36 | new Pro ... urce }) |
| flow.js:53:2:53:22 | createP ... source) |
| flow.js:55:11:55:58 | new Pro ... ource)) |
| flow.js:56:11:56:27 | p8.then(() => {}) |
| flow.js:57:12:57:31 | p9.finally(() => {}) |
| flow.js:60:12:60:59 | new Pro ... ource)) |
| flow.js:61:12:61:29 | p11.then(() => {}) |
| flow.js:65:9:65:56 | new Pro ... ource)) |
| flow.js:74:10:74:57 | new Pro ... ource)) |
| flow.js:76:2:76:17 | chainedPromise() |
| flow.js:76:2:76:32 | chained ... => {}) |
| flow.js:86:23:86:70 | new Pro ... ource)) |
| flow.js:89:3:89:27 | ("foo", ... => {}) |
| flow.js:91:21:91:68 | new Pro ... ource)) |
| flow.js:100:28:100:75 | new Pro ... ource)) |
| flow.js:103:2:103:48 | new Pro ... "BLA")) |
| flow.js:103:2:103:76 | new Pro ... ource}) |
| flow.js:105:2:105:48 | new Pro ... "BLA")) |
| flow.js:105:2:105:77 | new Pro ... ource}) |
| flow.js:107:17:107:64 | new Pro ... ource)) |
| flow.js:109:2:109:48 | new Pro ... "BLA")) |
| flow.js:109:2:109:71 | new Pro ... jected) |
| flow.js:111:2:111:48 | new Pro ... "BLA")) |
| flow.js:111:2:111:69 | new Pro ... jected) |
| flow.js:113:2:113:48 | new Pro ... "BLA")) |
| flow.js:113:2:113:69 | new Pro ... jected) |
| flow.js:117:2:117:48 | new Pro ... "BLA")) |
| flow.js:117:2:117:69 | new Pro ... solved) |
| flow.js:119:2:119:48 | new Pro ... "BLA")) |
| flow.js:119:2:119:69 | new Pro ... solved) |
| flow.js:121:2:121:21 | Promise.resolve(123) |
| flow.js:121:2:121:41 | Promise ... solved) |
| flow.js:123:2:123:21 | Promise.resolve(123) |
| flow.js:123:2:123:41 | Promise ... solved) |
| flow.js:125:2:125:21 | Promise.resolve(123) |
| flow.js:125:2:125:41 | Promise ... jected) |
| flow.js:127:2:127:21 | Promise.resolve(123) |
| flow.js:127:2:127:41 | Promise ... jected) |
| flow.js:129:2:129:52 | new Pro ... olved)) |
| flow.js:131:2:131:26 | Promise ... solved) |
| interflow.js:6:3:6:25 | loadScr ... urce()) |
| interflow.js:6:3:7:26 | loadScr ... () { }) |
| interflow.js:6:3:8:26 | loadScr ... () { }) |
| interflow.js:11:12:15:6 | 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 }) |

View File

@@ -0,0 +1,132 @@
(async function () {
var source = "source";
var p1 = Promise.resolve(source);
sink(await p1); // NOT OK
var p2 = new Promise((resolve, reject) => resolve(source));
sink(await p2); // NOT OK
var p3 = new Promise((resolve, reject) => reject(source));
sink(await p3); // OK!
var p4 = new Promise((resolve, reject) => reject(source));
try {
var foo = await p4;
} catch (e) {
sink(e); // NOT OK!
}
Promise.resolve(source).then(x => sink(x)); // NOT OK!
Promise.resolve(source).then(x => foo(x), y => sink(y)); // OK!
new Promise((resolve, reject) => reject(source)).then(x => sink(x)); // OK!
new Promise((resolve, reject) => reject(source)).then(x => foo(x), y => sink(y)); // NOT OK!
Promise.resolve("foo").then(x => source).then(z => sink(z)); // NOT OK!
Promise.resolve(source).then(x => "foo").then(z => sink(z)); // OK!
new Promise((resolve, reject) => reject(source)).catch(x => sink(x)); // NOT OK!
Promise.resolve(source).catch(() => { }).then(a => sink(a)); // NOT OK!
var p5 = Promise.resolve(source);
var p6 = p5.catch(() => { });
var p7 = p6.then(a => sink(a)); // NOT OK!
new Promise((resolve, reject) => reject(source)).then(() => { }).catch(x => sink(x)); // NOT OK!
new Promise((resolve, reject) => reject(source)).then(() => { }, () => { }).catch(x => sink(x)); // OK!
Promise.resolve(source).catch(() => { }).catch(() => { }).catch(() => { }).then(a => sink(a)); // NOT OK!
Promise.resolve(source).finally(() => { }).then(a => sink(a)); // NOT OK!
new Promise(() => { throw source }).catch(x => sink(x)); // NOT OK!
function createPromise(src) {
return Promise.resolve(src);
}
createPromise(source).then(v => sink(v)); // NOT OK!
var p8 = new Promise((resolve, reject) => reject(source));
var p9 = p8.then(() => {});
var p10 = p9.finally(() => {});
p10.catch((x) => sink(x)); // NOT OK!
var p11 = new Promise((resolve, reject) => reject(source));
var p12 = p11.then(() => {});
p12.catch(x => sink(x)); // NOT OK!
async function throws() {
await new Promise((resolve, reject) => reject(source));
}
try {
throws();
} catch(e) {
sink(e); // NOT OK!
}
function chainedPromise() {
return new Promise((resolve, reject) => reject(source)).then(() => {});
}
chainedPromise().then(() => {}).catch(e => sink(e)); // NOT OK!
function leaksResolvedPromise(p) {
p.then(x => sink(x)); // NOT OK!
}
leaksResolvedPromise(Promise.resolve(source));
function leaksRejectedPromise(p) {
p.catch(e => sink(e)); // NOT OK!
}
leaksRejectedPromise(new Promise((resolve, reject) => reject(source)));
function leaksRejectedAgain(p) {
("foo", p).then(() => {}).catch(e => sink(e)); // NOT OK!
}
leaksRejectedAgain(new Promise((resolve, reject) => reject(source)).then(() => {}));
async function returnsRejected(p) {
try {
await p;
} catch(e) {
return e;
}
}
var foo = returnsRejected(new Promise((resolve, reject) => reject(source)));
sink(foo); // NOT OK!
new Promise((resolve, reject) => reject("BLA")).catch(x => {return source}).then(x => sink(x)); // NOT OK
new Promise((resolve, reject) => reject("BLA")).finally(x => {throw source}).catch(x => sink(x)); // NOT OK
var rejected = new Promise((resolve, reject) => reject(source));
new Promise((resolve, reject) => reject("BLA")).finally(x => rejected).catch(x => sink(x)); // NOT OK
new Promise((resolve, reject) => reject("BLA")).catch(x => rejected).then(x => sink(x)) // OK
new Promise((resolve, reject) => reject("BLA")).catch(x => rejected).catch(x => sink(x)) // NOT OK
var resolved = Promise.resolve(source);
new Promise((resolve, reject) => reject("BLA")).catch(x => resolved).catch(x => sink(x)) // OK
new Promise((resolve, reject) => reject("BLA")).catch(x => resolved).then(x => sink(x)) // NOT OK
Promise.resolve(123).then(x => resolved).catch(x => sink(x)) // OK
Promise.resolve(123).then(x => resolved).then(x => sink(x)) // NOT OK
Promise.resolve(123).then(x => rejected).catch(x => sink(x)) // NOT OK
Promise.resolve(123).then(x => rejected).then(x => sink(x)) // OK
new Promise((resolve, reject) => resolve(resolved)).then(x => sink(x)); // NOT OK
Promise.resolve(resolved).then(x => sink(x)); // NOT OK
})();

View File

@@ -0,0 +1,34 @@
import javascript
class Configuration extends DataFlow::Configuration {
Configuration() { this = "PromiseDataFlowFlowTestingConfig" }
override predicate isSource(DataFlow::Node source) {
source.getEnclosingExpr().getStringValue() = "source"
}
override predicate isSink(DataFlow::Node sink) {
any(DataFlow::InvokeNode call | call.getCalleeName() = "sink").getAnArgument() = sink
}
}
class TaintConfig extends TaintTracking::Configuration {
TaintConfig() { this = "PromiseTaintFlowTestingConfig" }
override predicate isSource(DataFlow::Node source) {
source.getEnclosingExpr().getStringValue() = "source"
}
override predicate isSink(DataFlow::Node sink) {
any(DataFlow::InvokeNode call | call.getCalleeName() = "sink").getAnArgument() = sink
}
}
query predicate flow(DataFlow::Node source, DataFlow::Node sink) {
any(Configuration c).hasFlow(source, sink)
}
query predicate exclusiveTaintFlow(DataFlow::Node source, DataFlow::Node sink) {
not any(Configuration c).hasFlow(source, sink) and
any(TaintConfig c).hasFlow(source, sink)
}

View File

@@ -0,0 +1,20 @@
(function () {
function getSource() {
var source = "source"; // step 1
return source; // step 2
}
loadScript(getSource()) // step 3
.then(function () { })
.then(function () { })
.catch(handleError);
function loadScript(src) { // step 4 (is summarized)
return new Promise(function (resolve, reject) {
setTimeout(function (error) {
reject(new Error('Blah: ' + src)); // step 5
}, 1000);
});
}
function handleError(error) { // step 6
sink(error); // step 7
}
})();

View File

@@ -0,0 +1 @@
semmle-extractor-options: --experimental

View File

@@ -1,38 +1,222 @@
test_ResolvedPromiseDefinition
| flow.js:4:11:4:33 | Promise ... source) | flow.js:4:27:4:32 | source |
| flow.js:20:2:20:24 | Promise ... source) | flow.js:20:18:20:23 | source |
| flow.js:22:2:22:24 | Promise ... source) | flow.js:22:18:22:23 | source |
| flow.js:28:2:28:23 | Promise ... ("foo") | flow.js:28:18:28:22 | "foo" |
| flow.js:30:2:30:24 | Promise ... source) | flow.js:30:18:30:23 | source |
| flow.js:34:2:34:24 | Promise ... source) | flow.js:34:18:34:23 | source |
| flow.js:36:11:36:33 | Promise ... source) | flow.js:36:27:36:32 | source |
| flow.js:44:2:44:24 | Promise ... source) | flow.js:44:18:44:23 | source |
| flow.js:46:2:46:24 | Promise ... source) | flow.js:46:18:46:23 | source |
| flow.js:51:10:51:29 | Promise.resolve(src) | flow.js:51:26:51:28 | src |
| flow.js:81:23:81:45 | Promise ... source) | flow.js:81:39:81:44 | source |
| flow.js:115:17:115:39 | Promise ... source) | flow.js:115:33:115:38 | source |
| flow.js:121:2:121:21 | Promise.resolve(123) | flow.js:121:18:121:20 | 123 |
| flow.js:123:2:123:21 | Promise.resolve(123) | flow.js:123:18:123:20 | 123 |
| flow.js:125:2:125:21 | Promise.resolve(123) | flow.js:125:18:125:20 | 123 |
| flow.js:127:2:127:21 | Promise.resolve(123) | flow.js:127:18:127:20 | 123 |
| flow.js:131:2:131:26 | Promise ... solved) | flow.js:131:18:131:25 | resolved |
| 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
| flow.js:26:2:26:49 | new Pro ... ource)) | flow.js:26:69:26:80 | y => sink(y) |
| flow.js:32:2:32:49 | new Pro ... ource)) | flow.js:32:57:32:68 | x => sink(x) |
| flow.js:42:2:42:49 | new Pro ... ource)) | flow.js:42:67:42:75 | () => { } |
| flow.js:48:2:48:36 | new Pro ... urce }) | flow.js:48:44:48:55 | x => sink(x) |
| flow.js:103:2:103:48 | new Pro ... "BLA")) | flow.js:103:56:103:75 | x => {return source} |
| flow.js:105:2:105:48 | new Pro ... "BLA")) | flow.js:105:58:105:76 | x => {throw source} |
| flow.js:109:2:109:48 | new Pro ... "BLA")) | flow.js:109:58:109:70 | x => rejected |
| flow.js:111:2:111:48 | new Pro ... "BLA")) | flow.js:111:56:111:68 | x => rejected |
| flow.js:113:2:113:48 | new Pro ... "BLA")) | flow.js:113:56:113:68 | x => rejected |
| flow.js:117:2:117:48 | new Pro ... "BLA")) | flow.js:117:56:117:68 | x => resolved |
| flow.js:119:2:119:48 | new Pro ... "BLA")) | flow.js:119:56:119:68 | x => resolved |
| 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
| flow.js:7:11:7:59 | new Pro ... ource)) | flow.js:7:23:7:58 | (resolv ... source) |
| flow.js:10:11:10:58 | new Pro ... ource)) | flow.js:10:23:10:57 | (resolv ... source) |
| flow.js:13:11:13:58 | new Pro ... ource)) | flow.js:13:23:13:57 | (resolv ... source) |
| flow.js:24:2:24:49 | new Pro ... ource)) | flow.js:24:14:24:48 | (resolv ... source) |
| flow.js:26:2:26:49 | new Pro ... ource)) | flow.js:26:14:26:48 | (resolv ... source) |
| flow.js:32:2:32:49 | new Pro ... ource)) | flow.js:32:14:32:48 | (resolv ... source) |
| flow.js:40:2:40:49 | new Pro ... ource)) | flow.js:40:14:40:48 | (resolv ... source) |
| flow.js:42:2:42:49 | new Pro ... ource)) | flow.js:42:14:42:48 | (resolv ... source) |
| flow.js:48:2:48:36 | new Pro ... urce }) | flow.js:48:14:48:35 | () => { ... ource } |
| flow.js:55:11:55:58 | new Pro ... ource)) | flow.js:55:23:55:57 | (resolv ... source) |
| flow.js:60:12:60:59 | new Pro ... ource)) | flow.js:60:24:60:58 | (resolv ... source) |
| flow.js:65:9:65:56 | new Pro ... ource)) | flow.js:65:21:65:55 | (resolv ... source) |
| flow.js:74:10:74:57 | new Pro ... ource)) | flow.js:74:22:74:56 | (resolv ... source) |
| flow.js:86:23:86:70 | new Pro ... ource)) | flow.js:86:35:86:69 | (resolv ... source) |
| flow.js:91:21:91:68 | new Pro ... ource)) | flow.js:91:33:91:67 | (resolv ... source) |
| flow.js:100:28:100:75 | new Pro ... ource)) | flow.js:100:40:100:74 | (resolv ... source) |
| flow.js:103:2:103:48 | new Pro ... "BLA")) | flow.js:103:14:103:47 | (resolv ... ("BLA") |
| flow.js:105:2:105:48 | new Pro ... "BLA")) | flow.js:105:14:105:47 | (resolv ... ("BLA") |
| flow.js:107:17:107:64 | new Pro ... ource)) | flow.js:107:29:107:63 | (resolv ... source) |
| flow.js:109:2:109:48 | new Pro ... "BLA")) | flow.js:109:14:109:47 | (resolv ... ("BLA") |
| flow.js:111:2:111:48 | new Pro ... "BLA")) | flow.js:111:14:111:47 | (resolv ... ("BLA") |
| flow.js:113:2:113:48 | new Pro ... "BLA")) | flow.js:113:14:113:47 | (resolv ... ("BLA") |
| flow.js:117:2:117:48 | new Pro ... "BLA")) | flow.js:117:14:117:47 | (resolv ... ("BLA") |
| flow.js:119:2:119:48 | new Pro ... "BLA")) | flow.js:119:14:119:47 | (resolv ... ("BLA") |
| flow.js:129:2:129:52 | new Pro ... olved)) | flow.js:129:14:129:51 | (resolv ... solved) |
| interflow.js:11:12:15:6 | new Pro ... \\n }) | interflow.js:11:24:15:5 | functio ... ;\\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 } |
| promises.js:43:19:45:6 | Q.Promi ... \\n }) | promises.js:43:29:45:5 | functio ... ;\\n } |
test_PromiseDefinition_getAFinallyHandler
| flow.js:105:2:105:48 | new Pro ... "BLA")) | flow.js:105:58:105:76 | x => {throw source} |
| flow.js:109:2:109:48 | new Pro ... "BLA")) | flow.js:109:58:109:70 | x => rejected |
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:26:20:28:3 | (v) => ... v;\\n } |
test_PromiseDefinition
| flow.js:7:11:7:59 | new Pro ... ource)) |
| flow.js:10:11:10:58 | new Pro ... ource)) |
| flow.js:13:11:13:58 | new Pro ... ource)) |
| flow.js:24:2:24:49 | new Pro ... ource)) |
| flow.js:26:2:26:49 | new Pro ... ource)) |
| flow.js:32:2:32:49 | new Pro ... ource)) |
| flow.js:40:2:40:49 | new Pro ... ource)) |
| flow.js:42:2:42:49 | new Pro ... ource)) |
| flow.js:48:2:48:36 | new Pro ... urce }) |
| flow.js:55:11:55:58 | new Pro ... ource)) |
| flow.js:60:12:60:59 | new Pro ... ource)) |
| flow.js:65:9:65:56 | new Pro ... ource)) |
| flow.js:74:10:74:57 | new Pro ... ource)) |
| flow.js:86:23:86:70 | new Pro ... ource)) |
| flow.js:91:21:91:68 | new Pro ... ource)) |
| flow.js:100:28:100:75 | new Pro ... ource)) |
| flow.js:103:2:103:48 | new Pro ... "BLA")) |
| flow.js:105:2:105:48 | new Pro ... "BLA")) |
| flow.js:107:17:107:64 | new Pro ... ource)) |
| flow.js:109:2:109:48 | new Pro ... "BLA")) |
| flow.js:111:2:111:48 | new Pro ... "BLA")) |
| flow.js:113:2:113:48 | new Pro ... "BLA")) |
| flow.js:117:2:117:48 | new Pro ... "BLA")) |
| flow.js:119:2:119:48 | new Pro ... "BLA")) |
| flow.js:129:2:129:52 | new Pro ... olved)) |
| interflow.js:11:12:15:6 | 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
| flow.js:24:2:24:49 | new Pro ... ource)) | flow.js:24:56:24:67 | x => sink(x) |
| flow.js:26:2:26:49 | new Pro ... ource)) | flow.js:26:56:26:66 | x => foo(x) |
| flow.js:40:2:40:49 | new Pro ... ource)) | flow.js:40:56:40:64 | () => { } |
| flow.js:42:2:42:49 | new Pro ... ource)) | flow.js:42:56:42:64 | () => { } |
| flow.js:55:11:55:58 | new Pro ... ource)) | flow.js:56:19:56:26 | () => {} |
| flow.js:60:12:60:59 | new Pro ... ource)) | flow.js:61:21:61:28 | () => {} |
| flow.js:74:10:74:57 | new Pro ... ource)) | flow.js:74:64:74:71 | () => {} |
| flow.js:91:21:91:68 | new Pro ... ource)) | flow.js:91:75:91:82 | () => {} |
| flow.js:105:2:105:48 | new Pro ... "BLA")) | flow.js:105:58:105:76 | x => {throw source} |
| flow.js:109:2:109:48 | new Pro ... "BLA")) | flow.js:109:58:109:70 | x => rejected |
| flow.js:129:2:129:52 | new Pro ... olved)) | flow.js:129:59:129:70 | x => sink(x) |
| 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
| flow.js:7:11:7:59 | new Pro ... ource)) | flow.js:7:33:7:38 | reject |
| flow.js:10:11:10:58 | new Pro ... ource)) | flow.js:10:33:10:38 | reject |
| flow.js:13:11:13:58 | new Pro ... ource)) | flow.js:13:33:13:38 | reject |
| flow.js:24:2:24:49 | new Pro ... ource)) | flow.js:24:24:24:29 | reject |
| flow.js:26:2:26:49 | new Pro ... ource)) | flow.js:26:24:26:29 | reject |
| flow.js:32:2:32:49 | new Pro ... ource)) | flow.js:32:24:32:29 | reject |
| flow.js:40:2:40:49 | new Pro ... ource)) | flow.js:40:24:40:29 | reject |
| flow.js:42:2:42:49 | new Pro ... ource)) | flow.js:42:24:42:29 | reject |
| flow.js:55:11:55:58 | new Pro ... ource)) | flow.js:55:33:55:38 | reject |
| flow.js:60:12:60:59 | new Pro ... ource)) | flow.js:60:34:60:39 | reject |
| flow.js:65:9:65:56 | new Pro ... ource)) | flow.js:65:31:65:36 | reject |
| flow.js:74:10:74:57 | new Pro ... ource)) | flow.js:74:32:74:37 | reject |
| flow.js:86:23:86:70 | new Pro ... ource)) | flow.js:86:45:86:50 | reject |
| flow.js:91:21:91:68 | new Pro ... ource)) | flow.js:91:43:91:48 | reject |
| flow.js:100:28:100:75 | new Pro ... ource)) | flow.js:100:50:100:55 | reject |
| flow.js:103:2:103:48 | new Pro ... "BLA")) | flow.js:103:24:103:29 | reject |
| flow.js:105:2:105:48 | new Pro ... "BLA")) | flow.js:105:24:105:29 | reject |
| flow.js:107:17:107:64 | new Pro ... ource)) | flow.js:107:39:107:44 | reject |
| flow.js:109:2:109:48 | new Pro ... "BLA")) | flow.js:109:24:109:29 | reject |
| flow.js:111:2:111:48 | new Pro ... "BLA")) | flow.js:111:24:111:29 | reject |
| flow.js:113:2:113:48 | new Pro ... "BLA")) | flow.js:113:24:113:29 | reject |
| flow.js:117:2:117:48 | new Pro ... "BLA")) | flow.js:117:24:117:29 | reject |
| flow.js:119:2:119:48 | new Pro ... "BLA")) | flow.js:119:24:119:29 | reject |
| flow.js:129:2:129:52 | new Pro ... olved)) | flow.js:129:24:129:29 | reject |
| interflow.js:11:12:15:6 | new Pro ... \\n }) | interflow.js:11:43:11:48 | 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
| flow.js:7:11:7:59 | new Pro ... ource)) | flow.js:7:24:7:30 | resolve |
| flow.js:10:11:10:58 | new Pro ... ource)) | flow.js:10:24:10:30 | resolve |
| flow.js:13:11:13:58 | new Pro ... ource)) | flow.js:13:24:13:30 | resolve |
| flow.js:24:2:24:49 | new Pro ... ource)) | flow.js:24:15:24:21 | resolve |
| flow.js:26:2:26:49 | new Pro ... ource)) | flow.js:26:15:26:21 | resolve |
| flow.js:32:2:32:49 | new Pro ... ource)) | flow.js:32:15:32:21 | resolve |
| flow.js:40:2:40:49 | new Pro ... ource)) | flow.js:40:15:40:21 | resolve |
| flow.js:42:2:42:49 | new Pro ... ource)) | flow.js:42:15:42:21 | resolve |
| flow.js:55:11:55:58 | new Pro ... ource)) | flow.js:55:24:55:30 | resolve |
| flow.js:60:12:60:59 | new Pro ... ource)) | flow.js:60:25:60:31 | resolve |
| flow.js:65:9:65:56 | new Pro ... ource)) | flow.js:65:22:65:28 | resolve |
| flow.js:74:10:74:57 | new Pro ... ource)) | flow.js:74:23:74:29 | resolve |
| flow.js:86:23:86:70 | new Pro ... ource)) | flow.js:86:36:86:42 | resolve |
| flow.js:91:21:91:68 | new Pro ... ource)) | flow.js:91:34:91:40 | resolve |
| flow.js:100:28:100:75 | new Pro ... ource)) | flow.js:100:41:100:47 | resolve |
| flow.js:103:2:103:48 | new Pro ... "BLA")) | flow.js:103:15:103:21 | resolve |
| flow.js:105:2:105:48 | new Pro ... "BLA")) | flow.js:105:15:105:21 | resolve |
| flow.js:107:17:107:64 | new Pro ... ource)) | flow.js:107:30:107:36 | resolve |
| flow.js:109:2:109:48 | new Pro ... "BLA")) | flow.js:109:15:109:21 | resolve |
| flow.js:111:2:111:48 | new Pro ... "BLA")) | flow.js:111:15:111:21 | resolve |
| flow.js:113:2:113:48 | new Pro ... "BLA")) | flow.js:113:15:113:21 | resolve |
| flow.js:117:2:117:48 | new Pro ... "BLA")) | flow.js:117:15:117:21 | resolve |
| flow.js:119:2:119:48 | new Pro ... "BLA")) | flow.js:119:15:119:21 | resolve |
| flow.js:129:2:129:52 | new Pro ... olved)) | flow.js:129:15:129:21 | resolve |
| interflow.js:11:12:15:6 | new Pro ... \\n }) | interflow.js:11:34:11:40 | 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
| flow.js:32:2:32:49 | new Pro ... ource)) | flow.js:32:57:32:68 | x => sink(x) |
| flow.js:48:2:48:36 | new Pro ... urce }) | flow.js:48:44:48:55 | x => sink(x) |
| flow.js:103:2:103:48 | new Pro ... "BLA")) | flow.js:103:56:103:75 | x => {return source} |
| flow.js:111:2:111:48 | new Pro ... "BLA")) | flow.js:111:56:111:68 | x => rejected |
| flow.js:113:2:113:48 | new Pro ... "BLA")) | flow.js:113:56:113:68 | x => rejected |
| flow.js:117:2:117:48 | new Pro ... "BLA")) | flow.js:117:56:117:68 | x => resolved |
| flow.js:119:2:119:48 | new Pro ... "BLA")) | flow.js:119:56:119:68 | x => resolved |
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:23:18:25:3 | (v) => ... v;\\n } |
flow
| flow.js:2:15:2:22 | "source" | flow.js:5:7:5:14 | await p1 |
| flow.js:2:15:2:22 | "source" | flow.js:8:7:8:14 | await p2 |
| flow.js:2:15:2:22 | "source" | flow.js:17:8:17:8 | e |
| flow.js:2:15:2:22 | "source" | flow.js:20:41:20:41 | x |
| flow.js:2:15:2:22 | "source" | flow.js:26:79:26:79 | y |
| flow.js:2:15:2:22 | "source" | flow.js:28:58:28:58 | z |
| flow.js:2:15:2:22 | "source" | flow.js:32:67:32:67 | x |
| flow.js:2:15:2:22 | "source" | flow.js:34:58:34:58 | a |
| flow.js:2:15:2:22 | "source" | flow.js:38:29:38:29 | a |
| flow.js:2:15:2:22 | "source" | flow.js:40:83:40:83 | x |
| flow.js:2:15:2:22 | "source" | flow.js:44:92:44:92 | a |
| flow.js:2:15:2:22 | "source" | flow.js:46:60:46:60 | a |
| flow.js:2:15:2:22 | "source" | flow.js:48:54:48:54 | x |
| flow.js:2:15:2:22 | "source" | flow.js:53:39:53:39 | v |
| flow.js:2:15:2:22 | "source" | flow.js:58:24:58:24 | x |
| flow.js:2:15:2:22 | "source" | flow.js:62:22:62:22 | x |
| flow.js:2:15:2:22 | "source" | flow.js:70:8:70:8 | e |
| flow.js:2:15:2:22 | "source" | flow.js:76:50:76:50 | e |
| flow.js:2:15:2:22 | "source" | flow.js:79:20:79:20 | x |
| flow.js:2:15:2:22 | "source" | flow.js:84:21:84:21 | e |
| flow.js:2:15:2:22 | "source" | flow.js:89:45:89:45 | e |
| flow.js:2:15:2:22 | "source" | flow.js:101:7:101:9 | foo |
| flow.js:2:15:2:22 | "source" | flow.js:103:93:103:93 | x |
| flow.js:2:15:2:22 | "source" | flow.js:105:95:105:95 | x |
| flow.js:2:15:2:22 | "source" | flow.js:109:89:109:89 | x |
| flow.js:2:15:2:22 | "source" | flow.js:113:87:113:87 | x |
| flow.js:2:15:2:22 | "source" | flow.js:119:86:119:86 | x |
| flow.js:2:15:2:22 | "source" | flow.js:123:58:123:58 | x |
| flow.js:2:15:2:22 | "source" | flow.js:125:59:125:59 | x |
| flow.js:2:15:2:22 | "source" | flow.js:129:69:129:69 | x |
| flow.js:2:15:2:22 | "source" | flow.js:131:43:131:43 | x |
exclusiveTaintFlow
| interflow.js:3:18:3:25 | "source" | interflow.js:18:10:18:14 | error |

View File

@@ -7,3 +7,4 @@ import PromiseDefinition_getAResolveHandler
import PromiseDefinition_getRejectParameter
import PromiseDefinition_getResolveParameter
import PromiseDefinition_getACatchHandler
import flow