зеркало из https://github.com/github/codeql.git
refactor StepSummary into an internal .qll
This commit is contained in:
Родитель
cd6fe8115d
Коммит
7145a57db3
|
@ -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)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче