refactor StepSummary into an internal .qll

This commit is contained in:
Erik Krogh Kristensen 2020-03-16 17:52:04 +01:00
Родитель cd6fe8115d
Коммит 7145a57db3
5 изменённых файлов: 185 добавлений и 170 удалений

Просмотреть файл

@ -3,6 +3,7 @@
*/
import javascript
private import dataflow.internal.StepSummary
/**
* A definition of a `Promise` object.
@ -165,26 +166,37 @@ 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.
*
* See the qldoc for the `PromiseTypeTracking` module for an example of how to use this predicate.
*/
DataFlow::SourceNode promiseStep(DataFlow::SourceNode pred, DataFlow::StepSummary summary) {
DataFlow::SourceNode promiseStep(DataFlow::SourceNode pred, StepSummary summary) {
exists(PromiseFlowStep step, string field | field = Promises::valueProp() |
summary = DataFlow::LoadStep(field) and
summary = LoadStep(field) and
step.load(pred, result, field)
or
summary = DataFlow::StoreStep(field) and
summary = StoreStep(field) and
step.store(pred, result, field)
or
summary = DataFlow::LevelStep() and
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 DataFlow::TypeTrackingPseudoProperty {
private class ResolveFieldAsTypeTrackingProperty extends TypeTrackingPseudoProperty {
ResolveFieldAsTypeTrackingProperty() { this = Promises::valueProp() }
}
}

Просмотреть файл

@ -8,160 +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))
or
this instanceof TypeTrackingPseudoProperty
}
}
/**
* A pseudo-property that can be used in type-tracking.
*/
abstract class TypeTrackingPseudoProperty extends string {
bindingset[this]
TypeTrackingPseudoProperty() { any() }
}
private class OptionalPropertyName extends string {
OptionalPropertyName() { this instanceof PropertyName or this = "" }
}
/**
* 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()
)
)
}
}
private import internal.StepSummary
private newtype TTypeTracker = MkTypeTracker(Boolean hasCall, OptionalPropertyName prop)

Просмотреть файл

@ -3,6 +3,7 @@
*/
private import javascript
private import 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)
)
@ -92,11 +93,11 @@ module CallGraph {
pragma[noinline]
private DataFlow::SourceNode getABoundFunctionReferenceAux(
DataFlow::FunctionNode function, int boundArgs, DataFlow::TypeTracker t,
DataFlow::StepSummary summary
StepSummary summary
) {
exists(DataFlow::SourceNode prev |
prev = getABoundFunctionReferenceAux(function, boundArgs, t) and
DataFlow::StepSummary::step(prev, result, summary)
StepSummary::step(prev, result, summary)
)
}

Просмотреть файл

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

Просмотреть файл

@ -73,9 +73,8 @@ private DataFlow::SourceNode globbyFileNameSource(DataFlow::TypeTracker t) {
)
or
// Tracking out of a promise
exists(DataFlow::TypeTracker t2, DataFlow::StepSummary summary |
result = PromiseTypeTracking::promiseStep(globbyFileNameSource(t2), summary) and
t = t2.append(summary)
exists(DataFlow::TypeTracker t2 |
result = PromiseTypeTracking::promiseStep(globbyFileNameSource(t2), t, t2)
)
}
@ -116,9 +115,8 @@ private DataFlow::Node fastGlobFileNameSource(DataFlow::TypeTracker t) {
)
or
// Tracking out of a promise
exists(DataFlow::TypeTracker t2, DataFlow::StepSummary summary |
result = PromiseTypeTracking::promiseStep(fastGlobFileNameSource(t2), summary) and
t = t2.append(summary)
exists(DataFlow::TypeTracker t2 |
result = PromiseTypeTracking::promiseStep(fastGlobFileNameSource(t2), t, t2)
)
}