Files
codeql/javascript/ql/lib/semmle/javascript/dataflow/internal/StepSummary.qll
2024-03-12 13:01:39 +01:00

262 lines
7.8 KiB
Plaintext

import javascript
private import semmle.javascript.dataflow.TypeTracking
private import semmle.javascript.internal.CachedStages
private import FlowSteps
cached
private module Cached {
cached
module Public {
cached
predicate forceStage() { Stages::TypeTracking::ref() }
cached
class PropertyName extends string {
cached
PropertyName() {
this = any(DataFlow::PropRef pr).getPropertyName()
or
AccessPath::isAssignedInUniqueFile(this)
or
exists(AccessPath::getAnAssignmentTo(_, this))
or
SharedTypeTrackingStep::loadStep(_, _, this)
or
SharedTypeTrackingStep::storeStep(_, _, this)
or
SharedTypeTrackingStep::loadStoreStep(_, _, this, _)
or
SharedTypeTrackingStep::loadStoreStep(_, _, _, this)
or
this = DataFlow::PseudoProperties::arrayLikeElement()
}
}
/**
* A description of a step on an inter-procedural data flow path.
*/
cached
newtype TStepSummary =
LevelStep() or
CallStep() or
ReturnStep() or
StoreStep(PropertyName prop) or
LoadStep(PropertyName prop) or
CopyStep(PropertyName prop) or
LoadStoreStep(PropertyName fromProp, PropertyName toProp) {
SharedTypeTrackingStep::loadStoreStep(_, _, fromProp, toProp)
or
summarizedLoadStoreStep(_, _, fromProp, toProp)
} or
WithoutPropStep(PropertySet props) { SharedTypeTrackingStep::withoutPropStep(_, _, props) } or
NoPropStep() or
LoadAnyProp()
}
/**
* 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) | StepSummary::smallstep(mid, succ, summary))
}
pragma[nomagic]
private DataFlow::Node getAGlobalStepPredecessor(string global) {
result = AccessPath::getAnAssignmentTo(global) and
AccessPath::isAssignedInUniqueFile(global)
}
pragma[nomagic]
private DataFlow::Node getAGlobalStepSuccessor(string global) {
result = AccessPath::getAReferenceTo(global) and
AccessPath::isAssignedInUniqueFile(global)
}
bindingset[fun]
pragma[inline_late]
private DataFlow::PropRead getStoredPropRead(DataFlow::FunctionNode fun, string storeProp) {
result = fun.getAReturn().getALocalSource().getAPropertySource(storeProp)
}
/**
* Holds if `loadProp` of `param` is stored in the `storeProp` property of the return value of `fun`.
*/
pragma[nomagic]
private predicate summarizedLoadStoreStep(
DataFlow::ParameterNode param, DataFlow::FunctionNode fun, string loadProp, string storeProp
) {
exists(DataFlow::PropRead read |
read = getStoredPropRead(fun, storeProp) and
read.getBase().getALocalSource() = param and
read.getPropertyName() = loadProp
)
}
/**
* INTERNAL: Use `TypeBackTracker.smallstep()` instead.
*/
cached
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
// Implied flow of host object into 'this' of a method
CallGraph::impliedReceiverStep(pred, succ) and
summary = CallStep()
or
exists(string prop |
basicStoreStep(pred, succ, prop) and
summary = StoreStep(prop)
or
basicLoadStep(pred, succ, prop) and
summary = LoadStep(prop)
or
SharedTypeTrackingStep::storeStep(pred, succ, prop) and
summary = StoreStep(prop)
or
SharedTypeTrackingStep::loadStep(pred, succ, prop) and
summary = LoadStep(prop)
or
SharedTypeTrackingStep::loadStoreStep(pred, succ, prop) and
summary = CopyStep(prop)
)
or
exists(PropertySet props |
SharedTypeTrackingStep::withoutPropStep(pred, succ, props) and
summary = WithoutPropStep(props)
)
or
exists(string fromProp, string toProp |
SharedTypeTrackingStep::loadStoreStep(pred, succ, fromProp, toProp) and
summary = LoadStoreStep(fromProp, toProp)
)
or
SharedTypeTrackingStep::step(pred, succ) and
summary = LevelStep()
or
summary = LevelStep() and
exists(string global |
pred = getAGlobalStepPredecessor(global) and
succ = getAGlobalStepSuccessor(global)
)
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)
or
fun.getAReturn().getALocalSource().getAPropertySource(prop) = param and
summary = StoreStep(prop)
)
or
exists(string loadProp, string storeProp |
summarizedLoadStoreStep(param, fun, loadProp, storeProp) and
summary = LoadStoreStep(loadProp, storeProp)
)
) 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()
)
)
or
// Add 'return' steps from callback arguments to callback parameters
exists(DataFlow::ParameterNode parameter, int i |
pred = parameter.getAnInvocation().getArgument(i) and
succ = getACallbackSource(parameter).getParameter(i) and
summary = ReturnStep()
)
}
}
import Cached::Public
class OptionalPropertyName extends string {
OptionalPropertyName() { this instanceof PropertyName or this = "" }
}
/**
* 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 NoPropStep and result = "no-prop"
or
this instanceof LoadAnyProp and result = "load-any"
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)
or
exists(string prop | this = CopyStep(prop) | result = "copy " + prop)
or
exists(string prop | this = WithoutPropStep(prop) | result = "without " + prop)
or
exists(string fromProp, string toProp | this = LoadStoreStep(fromProp, toProp) |
result = "load " + fromProp + " and store to " + toProp
)
}
}
module StepSummary {
/**
* INTERNAL: Use `SourceNode.track()` or `SourceNode.backtrack()` instead.
*/
predicate step = Cached::step/3;
/**
* INTERNAL: Use `TypeBackTracker.smallstep()` instead.
*/
predicate smallstep = Cached::smallstep/3;
}