Merge pull request #3036 from erik-krogh/CustomTrack

Approved by asgerf
This commit is contained in:
semmle-qlci
2020-03-17 13:44:51 +00:00
committed by GitHub
10 changed files with 426 additions and 251 deletions

View File

@@ -3,6 +3,7 @@
*/
import javascript
private import dataflow.internal.StepSummary
/**
* A definition of a `Promise` object.
@@ -121,36 +122,156 @@ class AggregateES2015PromiseDefinition extends PromiseCreationCall {
}
/**
* 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).
* Common predicates shared between type-tracking and data-flow for promises.
*/
private module PromiseFlow {
module Promises {
/**
* Gets the pseudo-field used to describe resolved values in a promise.
*/
string resolveField() { result = "$PromiseResolveField$" }
string valueProp() { result = "$PromiseResolveField$" }
/**
* Gets the pseudo-field used to describe rejected values in a promise.
*/
string rejectField() { result = "$PromiseRejectField$" }
string errorProp() { result = "$PromiseRejectField$" }
}
/**
* A module for supporting promises in type-tracking predicates.
* The `PromiseTypeTracking::promiseStep` predicate is used for type tracking in and out of promises,
* and is included in the standard type-tracking steps (`SourceNode::track`).
* The `TypeTracker::startInPromise()` predicate can be used to initiate a type-tracker
* where the tracked value is a promise.
*
* The below is an example of a type-tracking predicate where the initial value is a promise:
* ```
* DataFlow::SourceNode myType(DataFlow::TypeTracker t) {
* t.startInPromise() and
* result = <the promise value> and
* or
* exists(DataFlow::TypeTracker t2 | result = myType(t2).track(t2, t))
* }
* ```
*
* The type-tracking predicate above will only end (`t = DataFlow::TypeTracker::end()`) after the tracked value has been
* extracted from the promise.
*
* The `PromiseTypeTracking::promiseStep` predicate can be used instead of `SourceNode::track`
* to get type-tracking only for promise steps.
*
* Replace `t.startInPromise()` in the above example with `t.start()` to create a type-tracking predicate
* where the value is not initially inside a promise.
*/
module PromiseTypeTracking {
/**
* Gets the result from a single step through a promise, from `pred` to `result` summarized by `summary`.
* This can be loading a resolved value from a promise, storing a value in a promise, or copying a resolved value from one promise to another.
*/
DataFlow::SourceNode promiseStep(DataFlow::SourceNode pred, StepSummary summary) {
exists(PromiseFlowStep step, string field | field = Promises::valueProp() |
summary = LoadStep(field) and
step.load(pred, result, field)
or
summary = StoreStep(field) and
step.store(pred, result, field)
or
summary = LevelStep() and
step.loadStore(pred, result, field)
)
}
/**
* Gets the result from a single step through a promise, from `pred` with tracker `t2` to `result` with tracker `t`.
* This can be loading a resolved value from a promise, storing a value in a promise, or copying a resolved value from one promise to another.
*/
DataFlow::SourceNode promiseStep(
DataFlow::SourceNode pred, DataFlow::TypeTracker t, DataFlow::TypeTracker t2
) {
exists(StepSummary summary |
result = PromiseTypeTracking::promiseStep(pred, summary) and
t = t2.append(summary)
)
}
/**
* A class enabling the use of the `resolveField` as a pseudo-property in type-tracking predicates.
*/
private class ResolveFieldAsTypeTrackingProperty extends TypeTrackingPseudoProperty {
ResolveFieldAsTypeTrackingProperty() { this = Promises::valueProp() }
}
}
/**
* An `AdditionalFlowStep` used to model a data-flow step related to promises.
*
* The `loadStep`/`storeStep`/`loadStoreStep` methods are overloaded such that the new predicates
* `load`/`store`/`loadStore` can be used in the `PromiseTypeTracking` module.
* (Thereby avoiding conflicts with a "cousin" `AdditionalFlowStep` implementation.)
*
* The class is private and is only intended to be used inside the `PromiseTypeTracking` and `PromiseFlow` modules.
*/
abstract private class PromiseFlowStep extends DataFlow::AdditionalFlowStep {
final override predicate step(DataFlow::Node pred, DataFlow::Node succ) { none() }
final override predicate step(
DataFlow::Node p, DataFlow::Node s, DataFlow::FlowLabel pl, DataFlow::FlowLabel sl
) {
none()
}
/**
* Holds if the property `prop` of the object `pred` should be loaded into `succ`.
*/
predicate load(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
final override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
this.load(pred, succ, prop)
}
/**
* Holds if `pred` should be stored in the object `succ` under the property `prop`.
*/
predicate store(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
final override predicate storeStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
this.store(pred, succ, prop)
}
/**
* Holds if the property `prop` should be copied from the object `pred` to the object `succ`.
*/
predicate loadStore(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
final override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
this.loadStore(pred, succ, prop)
}
}
/**
* 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 {
private predicate valueProp = Promises::valueProp/0;
private predicate errorProp = Promises::errorProp/0;
/**
* 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 {
class PromiseDefitionStep extends PromiseFlowStep {
PromiseDefinition promise;
PromiseDefitionStep() { this = promise }
override predicate storeStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = resolveField() and
override predicate store(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = valueProp() and
pred = promise.getResolveParameter().getACall().getArgument(0) and
succ = this
or
prop = rejectField() and
prop = errorProp() and
(
pred = promise.getRejectParameter().getACall().getArgument(0) or
pred = promise.getExecutor().getExceptionalReturn()
@@ -158,9 +279,9 @@ private module PromiseFlow {
succ = this
}
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
override predicate loadStore(DataFlow::Node pred, DataFlow::Node succ, string prop) {
// Copy the value of a resolved promise to the value of this promise.
prop = resolveField() and
prop = valueProp() and
pred = promise.getResolveParameter().getACall().getArgument(0) and
succ = this
}
@@ -169,20 +290,20 @@ private module PromiseFlow {
/**
* A flow step describing the a Promise.resolve (and similar) call.
*/
class CreationStep extends DataFlow::AdditionalFlowStep {
class CreationStep extends PromiseFlowStep {
PromiseCreationCall promise;
CreationStep() { this = promise }
override predicate storeStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = resolveField() and
override predicate store(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = valueProp() and
pred = promise.getValue() and
succ = this
}
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
override predicate loadStore(DataFlow::Node pred, DataFlow::Node succ, string prop) {
// Copy the value of a resolved promise to the value of this promise.
prop = resolveField() and
prop = valueProp() and
pred = promise.getValue() and
succ = this
}
@@ -192,7 +313,7 @@ private module PromiseFlow {
* 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 {
class AwaitStep extends PromiseFlowStep {
DataFlow::Node operand;
AwaitExpr await;
@@ -201,12 +322,12 @@ private module PromiseFlow {
operand.getEnclosingExpr() = await.getOperand()
}
override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = resolveField() and
override predicate load(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = valueProp() and
succ = this and
pred = operand
or
prop = rejectField() and
prop = errorProp() and
succ = await.getExceptionTarget() and
pred = operand
}
@@ -215,37 +336,37 @@ private module PromiseFlow {
/**
* A flow step describing the data-flow related to the `.then` method of a promise.
*/
class ThenStep extends DataFlow::AdditionalFlowStep, DataFlow::MethodCallNode {
class ThenStep extends PromiseFlowStep, DataFlow::MethodCallNode {
ThenStep() { this.getMethodName() = "then" }
override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = resolveField() and
override predicate load(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = valueProp() and
pred = getReceiver() and
succ = getCallback(0).getParameter(0)
or
prop = rejectField() and
prop = errorProp() and
pred = getReceiver() and
succ = getCallback(1).getParameter(0)
}
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
override predicate loadStore(DataFlow::Node pred, DataFlow::Node succ, string prop) {
not exists(this.getArgument(1)) and
prop = rejectField() and
prop = errorProp() and
pred = getReceiver() and
succ = this
or
// read the value of a resolved/rejected promise that is returned
(prop = rejectField() or prop = resolveField()) and
(prop = errorProp() or prop = valueProp()) and
pred = getCallback([0 .. 1]).getAReturn() and
succ = this
}
override predicate storeStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = resolveField() and
override predicate store(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = valueProp() and
pred = getCallback([0 .. 1]).getAReturn() and
succ = this
or
prop = rejectField() and
prop = errorProp() and
pred = getCallback([0 .. 1]).getExceptionalReturn() and
succ = this
}
@@ -254,32 +375,32 @@ private module PromiseFlow {
/**
* A flow step describing the data-flow related to the `.catch` method of a promise.
*/
class CatchStep extends DataFlow::AdditionalFlowStep, DataFlow::MethodCallNode {
class CatchStep extends PromiseFlowStep, DataFlow::MethodCallNode {
CatchStep() { this.getMethodName() = "catch" }
override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = rejectField() and
override predicate load(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = errorProp() and
pred = getReceiver() and
succ = getCallback(0).getParameter(0)
}
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = resolveField() and
override predicate loadStore(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = valueProp() 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
(prop = errorProp() or prop = valueProp()) and
pred = getCallback(0).getAReturn() and
succ = this
}
override predicate storeStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = rejectField() and
override predicate store(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = errorProp() and
pred = getCallback(0).getExceptionalReturn() and
succ = this
or
prop = resolveField() and
prop = valueProp() and
pred = getCallback(0).getAReturn() and
succ = this
}
@@ -288,22 +409,22 @@ private module PromiseFlow {
/**
* A flow step describing the data-flow related to the `.finally` method of a promise.
*/
class FinallyStep extends DataFlow::AdditionalFlowStep, DataFlow::MethodCallNode {
class FinallyStep extends PromiseFlowStep, DataFlow::MethodCallNode {
FinallyStep() { this.getMethodName() = "finally" }
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
(prop = resolveField() or prop = rejectField()) and
override predicate loadStore(DataFlow::Node pred, DataFlow::Node succ, string prop) {
(prop = valueProp() or prop = errorProp()) and
pred = getReceiver() and
succ = this
or
// read the value of a rejected promise that is returned
prop = rejectField() and
prop = errorProp() and
pred = getCallback(0).getAReturn() and
succ = this
}
override predicate storeStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = rejectField() and
override predicate store(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = errorProp() and
pred = getCallback(0).getExceptionalReturn() and
succ = this
}

View File

@@ -8,147 +8,7 @@
private import javascript
private import internal.FlowSteps
private class PropertyName extends string {
PropertyName() {
this = any(DataFlow::PropRef pr).getPropertyName()
or
AccessPath::isAssignedInUniqueFile(this)
or
exists(AccessPath::getAnAssignmentTo(_, this))
}
}
private class OptionalPropertyName extends string {
OptionalPropertyName() { this instanceof PropertyName or this = "" }
}
/**
* A description of a step on an inter-procedural data flow path.
*/
private newtype TStepSummary =
LevelStep() or
CallStep() or
ReturnStep() or
StoreStep(PropertyName prop) or
LoadStep(PropertyName prop)
/**
* INTERNAL: Use `TypeTracker` or `TypeBackTracker` instead.
*
* A description of a step on an inter-procedural data flow path.
*/
class StepSummary extends TStepSummary {
/** Gets a textual representation of this step summary. */
string toString() {
this instanceof LevelStep and result = "level"
or
this instanceof CallStep and result = "call"
or
this instanceof ReturnStep and result = "return"
or
exists(string prop | this = StoreStep(prop) | result = "store " + prop)
or
exists(string prop | this = LoadStep(prop) | result = "load " + prop)
}
}
module StepSummary {
/**
* INTERNAL: Use `SourceNode.track()` or `SourceNode.backtrack()` instead.
*/
cached
predicate step(DataFlow::SourceNode pred, DataFlow::SourceNode succ, StepSummary summary) {
exists(DataFlow::Node mid | pred.flowsTo(mid) | smallstep(mid, succ, summary))
}
/**
* INTERNAL: Use `TypeBackTracker.smallstep()` instead.
*/
predicate smallstep(DataFlow::Node pred, DataFlow::Node succ, StepSummary summary) {
// Flow through properties of objects
propertyFlowStep(pred, succ) and
summary = LevelStep()
or
// Flow through global variables
globalFlowStep(pred, succ) and
summary = LevelStep()
or
// Flow into function
callStep(pred, succ) and
summary = CallStep()
or
// Flow out of function
returnStep(pred, succ) and
summary = ReturnStep()
or
// Flow through an instance field between members of the same class
DataFlow::localFieldStep(pred, succ) and
summary = LevelStep()
or
exists(string prop |
basicStoreStep(pred, succ, prop) and
summary = StoreStep(prop)
or
basicLoadStep(pred, succ, prop) and
summary = LoadStep(prop)
)
or
any(AdditionalTypeTrackingStep st).step(pred, succ) and
summary = LevelStep()
or
// Store to global access path
exists(string name |
pred = AccessPath::getAnAssignmentTo(name) and
AccessPath::isAssignedInUniqueFile(name) and
succ = DataFlow::globalAccessPathRootPseudoNode() and
summary = StoreStep(name)
)
or
// Load from global access path
exists(string name |
succ = AccessPath::getAReferenceTo(name) and
AccessPath::isAssignedInUniqueFile(name) and
pred = DataFlow::globalAccessPathRootPseudoNode() and
summary = LoadStep(name)
)
or
// Store to non-global access path
exists(string name |
pred = AccessPath::getAnAssignmentTo(succ, name) and
summary = StoreStep(name)
)
or
// Load from non-global access path
exists(string name |
succ = AccessPath::getAReferenceTo(pred, name) and
summary = LoadStep(name) and
name != ""
)
or
// Summarize calls with flow directly from a parameter to a return.
exists(DataFlow::ParameterNode param, DataFlow::FunctionNode fun |
(
param.flowsTo(fun.getAReturn()) and
summary = LevelStep()
or
exists(string prop |
param.getAPropertyRead(prop).flowsTo(fun.getAReturn()) and
summary = LoadStep(prop)
)
) and
if param = fun.getAParameter()
then
// Step from argument to call site.
argumentPassing(succ, pred, fun.getFunction(), param)
else (
// Step from captured parameter to local call sites
pred = param and
succ = fun.getAnInvocation()
)
)
}
}
private import internal.StepSummary
private newtype TTypeTracker = MkTypeTracker(Boolean hasCall, OptionalPropertyName prop)
@@ -216,6 +76,18 @@ class TypeTracker extends TTypeTracker {
*/
predicate start() { hasCall = false and prop = "" }
/**
* Holds if this is the starting point of type tracking, and the value starts in the property named `propName`.
* The type tracking only ends after the property has been loaded.
*/
predicate startInProp(PropertyName propName) { hasCall = false and prop = propName }
/**
* Holds if this is the starting point of type tracking, and the initial value is a promise.
* The type tracking only ends after the value has been extracted from the promise.
*/
predicate startInPromise() { startInProp(Promises::valueProp()) }
/**
* Holds if this is the starting point of type tracking
* when tracking a parameter into a call, but not out of it.

View File

@@ -3,6 +3,7 @@
*/
private import javascript
private import semmle.javascript.dataflow.internal.StepSummary
cached
module CallGraph {
@@ -83,7 +84,7 @@ module CallGraph {
getAFunctionReference(function, 0, t.continue()).flowsTo(callback)
)
or
exists(DataFlow::StepSummary summary, DataFlow::TypeTracker t2 |
exists(StepSummary summary, DataFlow::TypeTracker t2 |
result = getABoundFunctionReferenceAux(function, boundArgs, t2, summary) and
t = t2.append(summary)
)
@@ -91,12 +92,11 @@ module CallGraph {
pragma[noinline]
private DataFlow::SourceNode getABoundFunctionReferenceAux(
DataFlow::FunctionNode function, int boundArgs, DataFlow::TypeTracker t,
DataFlow::StepSummary summary
DataFlow::FunctionNode function, int boundArgs, DataFlow::TypeTracker t, StepSummary summary
) {
exists(DataFlow::SourceNode prev |
prev = getABoundFunctionReferenceAux(function, boundArgs, t) and
DataFlow::StepSummary::step(prev, result, summary)
StepSummary::step(prev, result, summary)
)
}

View File

@@ -0,0 +1,157 @@
import javascript
private import semmle.javascript.dataflow.TypeTracking
private import FlowSteps
class PropertyName extends string {
PropertyName() {
this = any(DataFlow::PropRef pr).getPropertyName()
or
AccessPath::isAssignedInUniqueFile(this)
or
exists(AccessPath::getAnAssignmentTo(_, this))
or
this instanceof TypeTrackingPseudoProperty
}
}
class OptionalPropertyName extends string {
OptionalPropertyName() { this instanceof PropertyName or this = "" }
}
/**
* A pseudo-property that can be used in type-tracking.
*/
abstract class TypeTrackingPseudoProperty extends string {
bindingset[this]
TypeTrackingPseudoProperty() { any() }
}
/**
* A description of a step on an inter-procedural data flow path.
*/
newtype TStepSummary =
LevelStep() or
CallStep() or
ReturnStep() or
StoreStep(PropertyName prop) or
LoadStep(PropertyName prop)
/**
* INTERNAL: Use `TypeTracker` or `TypeBackTracker` instead.
*
* A description of a step on an inter-procedural data flow path.
*/
class StepSummary extends TStepSummary {
/** Gets a textual representation of this step summary. */
string toString() {
this instanceof LevelStep and result = "level"
or
this instanceof CallStep and result = "call"
or
this instanceof ReturnStep and result = "return"
or
exists(string prop | this = StoreStep(prop) | result = "store " + prop)
or
exists(string prop | this = LoadStep(prop) | result = "load " + prop)
}
}
module StepSummary {
/**
* INTERNAL: Use `SourceNode.track()` or `SourceNode.backtrack()` instead.
*/
cached
predicate step(DataFlow::SourceNode pred, DataFlow::SourceNode succ, StepSummary summary) {
exists(DataFlow::Node mid | pred.flowsTo(mid) | smallstep(mid, succ, summary))
}
/**
* INTERNAL: Use `TypeBackTracker.smallstep()` instead.
*/
predicate smallstep(DataFlow::Node pred, DataFlow::Node succ, StepSummary summary) {
// Flow through properties of objects
propertyFlowStep(pred, succ) and
summary = LevelStep()
or
// Flow through global variables
globalFlowStep(pred, succ) and
summary = LevelStep()
or
// Flow into function
callStep(pred, succ) and
summary = CallStep()
or
// Flow out of function
returnStep(pred, succ) and
summary = ReturnStep()
or
// Flow through an instance field between members of the same class
DataFlow::localFieldStep(pred, succ) and
summary = LevelStep()
or
exists(string prop |
basicStoreStep(pred, succ, prop) and
summary = StoreStep(prop)
or
basicLoadStep(pred, succ, prop) and
summary = LoadStep(prop)
)
or
any(AdditionalTypeTrackingStep st).step(pred, succ) and
summary = LevelStep()
or
// Store to global access path
exists(string name |
pred = AccessPath::getAnAssignmentTo(name) and
AccessPath::isAssignedInUniqueFile(name) and
succ = DataFlow::globalAccessPathRootPseudoNode() and
summary = StoreStep(name)
)
or
// Load from global access path
exists(string name |
succ = AccessPath::getAReferenceTo(name) and
AccessPath::isAssignedInUniqueFile(name) and
pred = DataFlow::globalAccessPathRootPseudoNode() and
summary = LoadStep(name)
)
or
// Store to non-global access path
exists(string name |
pred = AccessPath::getAnAssignmentTo(succ, name) and
summary = StoreStep(name)
)
or
// Load from non-global access path
exists(string name |
succ = AccessPath::getAReferenceTo(pred, name) and
summary = LoadStep(name) and
name != ""
)
or
// Step in/out of a promise
succ = PromiseTypeTracking::promiseStep(pred, summary)
or
// Summarize calls with flow directly from a parameter to a return.
exists(DataFlow::ParameterNode param, DataFlow::FunctionNode fun |
(
param.flowsTo(fun.getAReturn()) and
summary = LevelStep()
or
exists(string prop |
param.getAPropertyRead(prop).flowsTo(fun.getAReturn()) and
summary = LoadStep(prop)
)
) and
if param = fun.getAParameter()
then
// Step from argument to call site.
argumentPassing(succ, pred, fun.getFunction(), param)
else (
// Step from captured parameter to local call sites
pred = param and
succ = fun.getAnInvocation()
)
)
}
}

View File

@@ -566,36 +566,18 @@ module ClientRequest {
* The `isPromise` parameter reflects whether the reference is a promise containing
* an instance of `chrome-remote-interface`, or an instance of `chrome-remote-interface`.
*/
private DataFlow::SourceNode chromeRemoteInterface(DataFlow::TypeTracker t, boolean isPromise) {
t.start() and
private DataFlow::SourceNode chromeRemoteInterface(DataFlow::TypeTracker t) {
exists(DataFlow::CallNode call |
call = DataFlow::moduleImport("chrome-remote-interface").getAnInvocation()
|
result = call and isPromise = true
// the client is inside in a promise.
t.startInPromise() and result = call
or
result = call.getCallback([0 .. 1]).getParameter(0) and isPromise = false
// the client is accessed directly using a callback.
t.start() and result = call.getCallback([0 .. 1]).getParameter(0)
)
or
exists(DataFlow::TypeTracker t2 | result = chromeRemoteInterface(t2, isPromise).track(t2, t))
or
// Simple promise tracking.
exists(DataFlow::TypeTracker t2, DataFlow::SourceNode pred |
pred = chromeRemoteInterface(t2, true) and
isPromise = false and
(
t2 = t and
exists(AwaitExpr await | DataFlow::valueNode(await.getOperand()).getALocalSource() = pred |
result.getEnclosingExpr() = await
)
or
t2 = t and
exists(DataFlow::MethodCallNode thenCall |
thenCall.getMethodName() = "then" and pred = thenCall.getReceiver().getALocalSource()
|
result = thenCall.getCallback(0).getParameter(0)
)
)
)
exists(DataFlow::TypeTracker t2 | result = chromeRemoteInterface(t2).track(t2, t))
}
/**
@@ -606,7 +588,7 @@ module ClientRequest {
ChromeRemoteInterfaceRequest() {
exists(DataFlow::SourceNode instance |
instance = chromeRemoteInterface(DataFlow::TypeTracker::end(), false)
instance = chromeRemoteInterface(DataFlow::TypeTracker::end())
|
optionsArg = 0 and
this = instance.getAPropertyRead("Page").getAMemberCall("navigate")

View File

@@ -74,54 +74,74 @@ private class GlobFileNameSource extends FileNameSource {
}
}
/**
* Gets a file name or an array of file names from the `globby` library.
* The predicate uses type-tracking. However, type-tracking is only used to track a step out of a promise.
*/
private DataFlow::SourceNode globbyFileNameSource(DataFlow::TypeTracker t) {
exists(string moduleName | moduleName = "globby" |
// `require('globby').sync(_)`
t.start() and
result = DataFlow::moduleMember(moduleName, "sync").getACall()
or
// `files` in `require('globby')(_).then(files => ...)`
t.startInPromise() and
result = DataFlow::moduleImport(moduleName).getACall()
)
or
// Tracking out of a promise
exists(DataFlow::TypeTracker t2 |
result = PromiseTypeTracking::promiseStep(globbyFileNameSource(t2), t, t2)
)
}
/**
* A file name or an array of file names from the `globby` library.
*/
private class GlobbyFileNameSource extends FileNameSource {
GlobbyFileNameSource() {
exists(string moduleName | moduleName = "globby" |
// `require('globby').sync(_)`
this = DataFlow::moduleMember(moduleName, "sync").getACall()
GlobbyFileNameSource() { this = globbyFileNameSource(DataFlow::TypeTracker::end()) }
}
/**
* Gets a file name or an array of file names from the `fast-glob` library.
* The predicate uses type-tracking. However, type-tracking is only used to track a step out of a promise.
*/
private DataFlow::Node fastGlobFileNameSource(DataFlow::TypeTracker t) {
exists(string moduleName | moduleName = "fast-glob" |
// `require('fast-glob').sync(_)
t.start() and result = DataFlow::moduleMember(moduleName, "sync").getACall()
or
exists(DataFlow::SourceNode f |
f = DataFlow::moduleImport(moduleName)
or
// `files` in `require('globby')(_).then(files => ...)`
this =
DataFlow::moduleImport(moduleName)
.getACall()
.getAMethodCall("then")
.getCallback(0)
.getParameter(0)
f = DataFlow::moduleMember(moduleName, "async")
|
// `files` in `require('fast-glob')(_).then(files => ...)` and
// `files` in `require('fast-glob').async(_).then(files => ...)`
t.startInPromise() and result = f.getACall()
)
}
or
// `file` in `require('fast-glob').stream(_).on(_, file => ...)`
t.start() and
result =
DataFlow::moduleMember(moduleName, "stream")
.getACall()
.getAMethodCall(EventEmitter::on())
.getCallback(1)
.getParameter(0)
)
or
// Tracking out of a promise
exists(DataFlow::TypeTracker t2 |
result = PromiseTypeTracking::promiseStep(fastGlobFileNameSource(t2), t, t2)
)
}
/**
* A file name or an array of file names from the `fast-glob` library.
*/
private class FastGlobFileNameSource extends FileNameSource {
FastGlobFileNameSource() {
exists(string moduleName | moduleName = "fast-glob" |
// `require('fast-glob').sync(_)`
this = DataFlow::moduleMember(moduleName, "sync").getACall()
or
exists(DataFlow::SourceNode f |
f = DataFlow::moduleImport(moduleName)
or
f = DataFlow::moduleMember(moduleName, "async")
|
// `files` in `require('fast-glob')(_).then(files => ...)` and
// `files` in `require('fast-glob').async(_).then(files => ...)`
this = f.getACall().getAMethodCall("then").getCallback(0).getParameter(0)
)
or
// `file` in `require('fast-glob').stream(_).on(_, file => ...)`
this =
DataFlow::moduleMember(moduleName, "stream")
.getACall()
.getAMethodCall(EventEmitter::on())
.getCallback(1)
.getParameter(0)
)
}
FastGlobFileNameSource() { this = fastGlobFileNameSource(DataFlow::TypeTracker::end()) }
}
/**