Merge rc/3.12 into main

This commit is contained in:
Aditya Sharad
2023-12-21 16:40:51 -08:00
1366 changed files with 97401 additions and 58074 deletions

View File

@@ -0,0 +1,8 @@
import codeql.ruby.DataFlow
import codeql.ruby.typetracking.internal.TypeTrackingImpl
private module ConsistencyChecksInput implements ConsistencyChecksInputSig {
predicate unreachableNodeExclude(DataFlow::Node n) { n instanceof DataFlow::PostUpdateNode }
}
import ConsistencyChecks<ConsistencyChecksInput>

View File

@@ -22,7 +22,7 @@ have no source code, so we include a flow summary for it:
private class ChompSummary extends SimpleSummarizedCallable {
ChompSummary() { this = "chomp" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[self]" and
output = "ReturnValue" and
preservesValue = false

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Ruby now makes use of the shared type tracking library, exposed as `codeql.ruby.typetracking.TypeTracking`. The existing type tracking library, `codeql.ruby.typetracking.TypeTracker`, has consequently been deprecated.

View File

@@ -8,8 +8,7 @@
private import codeql.ruby.AST
private import codeql.ruby.DataFlow
private import codeql.ruby.typetracking.ApiGraphShared
private import codeql.ruby.typetracking.TypeTracker
private import codeql.ruby.typetracking.TypeTrackerSpecific as TypeTrackerSpecific
private import codeql.ruby.typetracking.internal.TypeTrackingImpl
private import codeql.ruby.controlflow.CfgNodes
private import codeql.ruby.dataflow.internal.DataFlowPrivate as DataFlowPrivate
private import codeql.ruby.dataflow.internal.DataFlowDispatch as DataFlowDispatch
@@ -1050,9 +1049,9 @@ module API {
/** INTERNAL USE ONLY. */
module Internal {
private module Shared = ApiGraphShared<SharedArg>;
private module MkShared = ApiGraphShared<SharedArg>;
import Shared
import MkShared
/** Gets the API node corresponding to the module/class object for `mod`. */
bindingset[mod]
@@ -1093,7 +1092,7 @@ module API {
private predicate needsSinkNode(DataFlow::Node node) {
node instanceof DataFlowPrivate::ArgumentNode
or
TypeTrackerSpecific::basicStoreStep(node, _, _)
TypeTrackingInput::storeStep(node, _, _)
or
node = any(DataFlow::CallableNode callable).getAReturnNode()
or
@@ -1203,10 +1202,8 @@ module API {
cached
predicate contentEdge(Node pred, DataFlow::Content content, Node succ) {
exists(
DataFlow::Node object, DataFlow::Node value, TypeTrackerSpecific::TypeTrackerContent c
|
TypeTrackerSpecific::basicLoadStep(object, value, c) and
exists(DataFlow::Node object, DataFlow::Node value, DataFlow::ContentSet c |
TypeTrackingInput::loadStep(object, value, c) and
content = c.getAStoreContent() and
not c.isSingleton(any(DataFlow::Content::AttributeNameContent k)) and
// `x -> x.foo` with content "foo"
@@ -1214,7 +1211,7 @@ module API {
succ = getForwardStartNode(value)
or
// Based on `object.c = value` generate `object -> value` with content `c`
TypeTrackerSpecific::basicStoreStep(value, object, c) and
TypeTrackingInput::storeStep(value, object, c) and
content = c.getAStoreContent() and
pred = getForwardOrBackwardEndNode(getALocalSourceStrict(object)) and
succ = MkSinkNode(value)

View File

@@ -2,13 +2,12 @@
import codeql.ruby.AST
private import codeql.ruby.CFG
private import codeql.ruby.typetracking.TypeTracker
private import codeql.ruby.typetracking.TypeTracking
import codeql.ruby.DataFlow
private import internal.FlowSummaryImpl as Impl
private import internal.DataFlowDispatch
private import internal.DataFlowImplCommon as DataFlowImplCommon
private import internal.DataFlowPrivate
private import internal.FlowSummaryImplSpecific
// import all instances below
private module Summaries {
@@ -16,104 +15,13 @@ private module Summaries {
private import codeql.ruby.frameworks.data.ModelsAsData
}
class SummaryComponent = Impl::Public::SummaryComponent;
deprecated class SummaryComponent = Impl::Private::SummaryComponent;
/** Provides predicates for constructing summary components. */
module SummaryComponent {
private import Impl::Public::SummaryComponent as SC
deprecated module SummaryComponent = Impl::Private::SummaryComponent;
predicate parameter = SC::parameter/1;
deprecated class SummaryComponentStack = Impl::Private::SummaryComponentStack;
predicate argument = SC::argument/1;
predicate content = SC::content/1;
predicate withoutContent = SC::withoutContent/1;
predicate withContent = SC::withContent/1;
class SyntheticGlobal = SC::SyntheticGlobal;
/** Gets a summary component that represents a receiver. */
SummaryComponent receiver() { result = argument(any(ParameterPosition pos | pos.isSelf())) }
/** Gets a summary component that represents a block argument. */
SummaryComponent block() { result = argument(any(ParameterPosition pos | pos.isBlock())) }
/** Gets a summary component that represents an element in a collection at an unknown index. */
SummaryComponent elementUnknown() {
result = SC::content(TSingletonContent(TUnknownElementContent()))
}
/** Gets a summary component that represents an element in a collection at a known index. */
SummaryComponent elementKnown(ConstantValue cv) {
result = SC::content(TSingletonContent(DataFlow::Content::getElementContent(cv)))
}
/**
* Gets a summary component that represents an element in a collection at a specific
* known index `cv`, or an unknown index.
*/
SummaryComponent elementKnownOrUnknown(ConstantValue cv) {
result = SC::content(TKnownOrUnknownElementContent(TKnownElementContent(cv)))
or
not exists(TKnownElementContent(cv)) and
result = elementUnknown()
}
/**
* Gets a summary component that represents an element in a collection at either an unknown
* index or known index. This has the same semantics as
*
* ```ql
* elementKnown() or elementUnknown(_)
* ```
*
* but is more efficient, because it is represented by a single value.
*/
SummaryComponent elementAny() { result = SC::content(TAnyElementContent()) }
/**
* Gets a summary component that represents an element in a collection at known
* integer index `lower` or above.
*/
SummaryComponent elementLowerBound(int lower) {
result = SC::content(TElementLowerBoundContent(lower, false))
}
/**
* Gets a summary component that represents an element in a collection at known
* integer index `lower` or above, or possibly at an unknown index.
*/
SummaryComponent elementLowerBoundOrUnknown(int lower) {
result = SC::content(TElementLowerBoundContent(lower, true))
}
/** Gets a summary component that represents the return value of a call. */
SummaryComponent return() { result = SC::return(any(NormalReturnKind rk)) }
}
class SummaryComponentStack = Impl::Public::SummaryComponentStack;
/** Provides predicates for constructing stacks of summary components. */
module SummaryComponentStack {
private import Impl::Public::SummaryComponentStack as SCS
predicate singleton = SCS::singleton/1;
predicate push = SCS::push/2;
predicate argument = SCS::argument/1;
/** Gets a singleton stack representing a receiver. */
SummaryComponentStack receiver() { result = singleton(SummaryComponent::receiver()) }
/** Gets a singleton stack representing a block argument. */
SummaryComponentStack block() { result = singleton(SummaryComponent::block()) }
/** Gets a singleton stack representing the return value of a call. */
SummaryComponentStack return() { result = singleton(SummaryComponent::return()) }
}
deprecated module SummaryComponentStack = Impl::Private::SummaryComponentStack;
/** A callable with a flow summary, identified by a unique string. */
abstract class SummarizedCallable extends LibraryCallable, Impl::Public::SummarizedCallable {
@@ -121,18 +29,11 @@ abstract class SummarizedCallable extends LibraryCallable, Impl::Public::Summari
SummarizedCallable() { any() }
/**
* Same as
*
* ```ql
* propagatesFlow(
* SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
* )
* ```
*
* but uses an external (string) representation of the input and output stacks.
* DEPRECATED: Use `propagatesFlow` instead.
*/
pragma[nomagic]
predicate propagatesFlowExt(string input, string output, boolean preservesValue) { none() }
deprecated predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
this.propagatesFlow(input, output, preservesValue)
}
/**
* Gets the synthesized parameter that results from an input specification
@@ -141,7 +42,7 @@ abstract class SummarizedCallable extends LibraryCallable, Impl::Public::Summari
DataFlow::ParameterNode getParameter(string s) {
exists(ParameterPosition pos |
DataFlowImplCommon::parameterNode(result, TLibraryCallable(this), pos) and
s = getParameterPosition(pos)
s = Impl::Input::encodeParameterPosition(pos)
)
}
}
@@ -159,7 +60,7 @@ abstract class SimpleSummarizedCallable extends SummarizedCallable {
final override MethodCall getACallSimple() { result = mc }
}
class RequiredSummaryComponentStack = Impl::Public::RequiredSummaryComponentStack;
deprecated class RequiredSummaryComponentStack = Impl::Private::RequiredSummaryComponentStack;
/**
* Provides a set of special flow summaries to ensure that callbacks passed into
@@ -199,7 +100,7 @@ private module LibraryCallbackSummaries {
libraryCallHasLambdaArg(result.getAControlFlowNode(), _)
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
(
input = "Argument[block]" and
output = "Argument[block].Parameter[lambda-self]"

View File

@@ -1,182 +0,0 @@
/**
* Module for parsing access paths from MaD models, both the identifying access path used
* by dynamic languages, and the input/output specifications for summary steps.
*
* This file is used by the shared data flow library and by the JavaScript libraries
* (which does not use the shared data flow libraries).
*/
/**
* Convenience-predicate for extracting two capture groups at once.
*/
bindingset[input, regexp]
private predicate regexpCaptureTwo(string input, string regexp, string capture1, string capture2) {
capture1 = input.regexpCapture(regexp, 1) and
capture2 = input.regexpCapture(regexp, 2)
}
/** Companion module to the `AccessPath` class. */
module AccessPath {
/** A string that should be parsed as an access path. */
abstract class Range extends string {
bindingset[this]
Range() { any() }
}
/**
* Parses an integer constant `n` or interval `n1..n2` (inclusive) and gets the value
* of the constant or any value contained in the interval.
*/
bindingset[arg]
int parseInt(string arg) {
result = arg.toInt()
or
// Match "n1..n2"
exists(string lo, string hi |
regexpCaptureTwo(arg, "(-?\\d+)\\.\\.(-?\\d+)", lo, hi) and
result = [lo.toInt() .. hi.toInt()]
)
}
/**
* Parses a lower-bounded interval `n..` and gets the lower bound.
*/
bindingset[arg]
int parseLowerBound(string arg) { result = arg.regexpCapture("(-?\\d+)\\.\\.", 1).toInt() }
/**
* Parses an integer constant or interval (bounded or unbounded) that explicitly
* references the arity, such as `N-1` or `N-3..N-1`.
*
* Note that expressions of form `N-x` will never resolve to a negative index,
* even if `N` is zero (it will have no result in that case).
*/
bindingset[arg, arity]
private int parseIntWithExplicitArity(string arg, int arity) {
result >= 0 and // do not allow N-1 to resolve to a negative index
exists(string lo |
// N-x
lo = arg.regexpCapture("N-(\\d+)", 1) and
result = arity - lo.toInt()
or
// N-x..
lo = arg.regexpCapture("N-(\\d+)\\.\\.", 1) and
result = [arity - lo.toInt(), arity - 1]
)
or
exists(string lo, string hi |
// x..N-y
regexpCaptureTwo(arg, "(-?\\d+)\\.\\.N-(\\d+)", lo, hi) and
result = [lo.toInt() .. arity - hi.toInt()]
or
// N-x..N-y
regexpCaptureTwo(arg, "N-(\\d+)\\.\\.N-(\\d+)", lo, hi) and
result = [arity - lo.toInt() .. arity - hi.toInt()] and
result >= 0
or
// N-x..y
regexpCaptureTwo(arg, "N-(\\d+)\\.\\.(\\d+)", lo, hi) and
result = [arity - lo.toInt() .. hi.toInt()] and
result >= 0
)
}
/**
* Parses an integer constant or interval (bounded or unbounded) and gets any
* of the integers contained within (of which there may be infinitely many).
*
* Has no result for arguments involving an explicit arity, such as `N-1`.
*/
bindingset[arg, result]
int parseIntUnbounded(string arg) {
result = parseInt(arg)
or
result >= parseLowerBound(arg)
}
/**
* Parses an integer constant or interval (bounded or unbounded) that
* may reference the arity of a call, such as `N-1` or `N-3..N-1`.
*
* Note that expressions of form `N-x` will never resolve to a negative index,
* even if `N` is zero (it will have no result in that case).
*/
bindingset[arg, arity]
int parseIntWithArity(string arg, int arity) {
result = parseInt(arg)
or
result in [parseLowerBound(arg) .. arity - 1]
or
result = parseIntWithExplicitArity(arg, arity)
}
}
/** Gets the `n`th token on the access path as a string. */
private string getRawToken(AccessPath path, int n) {
// Avoid splitting by '.' since tokens may contain dots, e.g. `Field[foo.Bar.x]`.
// Instead use regexpFind to match valid tokens, and supplement with a final length
// check (in `AccessPath.hasSyntaxError`) to ensure all characters were included in a token.
result = path.regexpFind("\\w+(?:\\[[^\\]]*\\])?(?=\\.|$)", n, _)
}
/**
* A string that occurs as an access path (either identifying or input/output spec)
* which might be relevant for this database.
*/
class AccessPath extends string instanceof AccessPath::Range {
/** Holds if this string is not a syntactically valid access path. */
predicate hasSyntaxError() {
// If the lengths match, all characters must haven been included in a token
// or seen by the `.` lookahead pattern.
this != "" and
not this.length() = sum(int n | | getRawToken(this, n).length() + 1) - 1
}
/** Gets the `n`th token on the access path (if there are no syntax errors). */
AccessPathToken getToken(int n) {
result = getRawToken(this, n) and
not this.hasSyntaxError()
}
/** Gets the number of tokens on the path (if there are no syntax errors). */
int getNumToken() {
result = count(int n | exists(getRawToken(this, n))) and
not this.hasSyntaxError()
}
}
/**
* An access part token such as `Argument[1]` or `ReturnValue`, appearing in one or more access paths.
*/
class AccessPathToken extends string {
AccessPathToken() { this = getRawToken(_, _) }
private string getPart(int part) {
result = this.regexpCapture("([^\\[]+)(?:\\[([^\\]]*)\\])?", part)
}
/** Gets the name of the token, such as `Member` from `Member[x]` */
string getName() { result = this.getPart(1) }
/**
* Gets the argument list, such as `1,2` from `Member[1,2]`,
* or has no result if there are no arguments.
*/
string getArgumentList() { result = this.getPart(2) }
/** Gets the `n`th argument to this token, such as `x` or `y` from `Member[x,y]`. */
string getArgument(int n) { result = this.getArgumentList().splitAt(",", n).trim() }
/** Gets the `n`th argument to this `name` token, such as `x` or `y` from `Member[x,y]`. */
pragma[nomagic]
string getArgument(string name, int n) { name = this.getName() and result = this.getArgument(n) }
/** Gets an argument to this token, such as `x` or `y` from `Member[x,y]`. */
string getAnArgument() { result = this.getArgument(_) }
/** Gets an argument to this `name` token, such as `x` or `y` from `Member[x,y]`. */
string getAnArgument(string name) { result = this.getArgument(name, _) }
/** Gets the number of arguments to this token, such as 2 for `Member[x,y]` or zero for `ReturnValue`. */
int getNumArgument() { result = count(int n | exists(this.getArgument(n))) }
}

View File

@@ -1,11 +1,9 @@
private import codeql.ruby.AST
private import codeql.ruby.CFG
private import DataFlowPrivate
private import codeql.ruby.typetracking.TypeTracker
private import codeql.ruby.typetracking.TypeTrackerSpecific as TypeTrackerSpecific
private import codeql.ruby.typetracking.internal.TypeTrackingImpl
private import codeql.ruby.ast.internal.Module
private import FlowSummaryImpl as FlowSummaryImpl
private import FlowSummaryImplSpecific as FlowSummaryImplSpecific
private import codeql.ruby.dataflow.FlowSummary
private import codeql.ruby.dataflow.SSA
private import codeql.util.Boolean
@@ -427,14 +425,14 @@ private module Cached {
TPositionalArgumentPosition(int pos) {
exists(Call c | exists(c.getArgument(pos)))
or
FlowSummaryImplSpecific::ParsePositions::isParsedParameterPosition(_, pos)
FlowSummaryImpl::ParsePositions::isParsedParameterPosition(_, pos)
} or
TKeywordArgumentPosition(string name) {
name = any(KeywordParameter kp).getName()
or
exists(any(Call c).getKeywordArgument(name))
or
FlowSummaryImplSpecific::ParsePositions::isParsedKeywordParameterPosition(_, name)
FlowSummaryImpl::ParsePositions::isParsedKeywordParameterPosition(_, name)
} or
THashSplatArgumentPosition() or
TSynthHashSplatArgumentPosition() or
@@ -451,15 +449,17 @@ private module Cached {
TPositionalParameterPosition(int pos) {
pos = any(Parameter p).getPosition()
or
FlowSummaryImplSpecific::ParsePositions::isParsedArgumentPosition(_, pos)
FlowSummaryImpl::ParsePositions::isParsedArgumentPosition(_, pos)
} or
TPositionalParameterLowerBoundPosition(int pos) {
FlowSummaryImplSpecific::ParsePositions::isParsedArgumentLowerBoundPosition(_, pos)
FlowSummaryImpl::ParsePositions::isParsedArgumentLowerBoundPosition(_, pos)
} or
TKeywordParameterPosition(string name) {
name = any(KeywordParameter kp).getName()
or
FlowSummaryImplSpecific::ParsePositions::isParsedKeywordArgumentPosition(_, name)
exists(any(Call c).getKeywordArgument(name))
or
FlowSummaryImpl::ParsePositions::isParsedKeywordArgumentPosition(_, name)
} or
THashSplatParameterPosition() or
TSynthHashSplatParameterPosition() or
@@ -657,17 +657,18 @@ private module TrackInstanceInput implements CallGraphConstruction::InputSig {
predicate stepNoCall(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, StepSummary summary) {
// We exclude steps into `self` parameters. For those, we instead rely on the type of
// the enclosing module
StepSummary::smallstepNoCall(nodeFrom, nodeTo, summary) and
smallStepNoCall(nodeFrom, nodeTo, summary) and
isNotSelf(nodeTo)
or
// We exclude steps into type checked variables. For those, we instead rely on the
// type being checked against
localFlowStep(nodeFrom, nodeTo, summary) and
not hasAdjacentTypeCheckedReads(nodeTo)
not hasAdjacentTypeCheckedReads(nodeTo) and
not asModulePattern(nodeTo, _)
}
predicate stepCall(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, StepSummary summary) {
StepSummary::smallstepCall(nodeFrom, nodeTo, summary)
smallStepCall(nodeFrom, nodeTo, summary)
}
class StateProj = Unit;
@@ -941,7 +942,7 @@ private module TrackSingletonMethodOnInstanceInput implements CallGraphConstruct
RelevantCall call, DataFlow::Node arg, DataFlow::ParameterNode p,
CfgNodes::ExprCfgNode nodeFromPreExpr
|
TypeTrackerSpecific::callStep(call, arg, p) and
callStep(call, arg, p) and
nodeTo.getPreUpdateNode() = arg and
summary.toString() = "return" and
(
@@ -965,13 +966,13 @@ private module TrackSingletonMethodOnInstanceInput implements CallGraphConstruct
}
predicate stepNoCall(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, StepSummary summary) {
StepSummary::smallstepNoCall(nodeFrom, nodeTo, summary)
smallStepNoCall(nodeFrom, nodeTo, summary)
or
localFlowStep(nodeFrom, nodeTo, summary)
}
predicate stepCall(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, StepSummary summary) {
StepSummary::smallstepCall(nodeFrom, nodeTo, summary)
smallStepCall(nodeFrom, nodeTo, summary)
or
paramReturnFlow(nodeFrom, nodeTo, summary)
}

View File

@@ -10,10 +10,12 @@ private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
private import DataFlowImpl
import DataFlowImplCommonPublic
import FlowStateString
deprecated import FlowStateString
private import codeql.util.Unit
/**
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
*
* A configuration of interprocedural data flow analysis. This defines
* sources, sinks, and any other configurable aspect of the analysis. Each
* use of the global data flow library must define its own unique extension
@@ -48,7 +50,7 @@ private import codeql.util.Unit
* should instead depend on a `DataFlow2::Configuration`, a
* `DataFlow3::Configuration`, or a `DataFlow4::Configuration`.
*/
abstract class Configuration extends string {
abstract deprecated class Configuration extends string {
bindingset[this]
Configuration() { any() }
@@ -189,7 +191,7 @@ abstract class Configuration extends string {
* Good performance cannot be guaranteed in the presence of such recursion, so
* it should be replaced by using more than one copy of the data flow library.
*/
abstract private class ConfigurationRecursionPrevention extends Configuration {
abstract deprecated private class ConfigurationRecursionPrevention extends Configuration {
bindingset[this]
ConfigurationRecursionPrevention() { any() }
@@ -210,7 +212,7 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
}
}
private FlowState relevantState(Configuration config) {
deprecated private FlowState relevantState(Configuration config) {
config.isSource(_, result) or
config.isSink(_, result) or
config.isBarrier(_, result) or
@@ -219,17 +221,17 @@ private FlowState relevantState(Configuration config) {
}
private newtype TConfigState =
TMkConfigState(Configuration config, FlowState state) {
deprecated TMkConfigState(Configuration config, FlowState state) {
state = relevantState(config) or state instanceof FlowStateEmpty
}
private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
deprecated private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
deprecated private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
deprecated private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
private module Config implements FullStateConfigSig {
deprecated private module Config implements FullStateConfigSig {
class FlowState = TConfigState;
predicate isSource(Node source, FlowState state) {
@@ -296,13 +298,13 @@ private module Config implements FullStateConfigSig {
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
}
private import Impl<Config> as I
deprecated private import Impl<Config> as I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
*/
class PathNode instanceof I::PathNode {
deprecated class PathNode instanceof I::PathNode {
/** Gets a textual representation of this element. */
final string toString() { result = super.toString() }
@@ -329,10 +331,10 @@ class PathNode instanceof I::PathNode {
final Node getNode() { result = super.getNode() }
/** Gets the `FlowState` of this node. */
final FlowState getState() { result = getState(super.getState()) }
deprecated final FlowState getState() { result = getState(super.getState()) }
/** Gets the associated configuration. */
final Configuration getConfiguration() { result = getConfig(super.getState()) }
deprecated final Configuration getConfiguration() { result = getConfig(super.getState()) }
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() { result = super.getASuccessor() }
@@ -347,9 +349,9 @@ class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
module PathGraph = I::PathGraph;
deprecated module PathGraph = I::PathGraph;
private predicate hasFlow(Node source, Node sink, Configuration config) {
deprecated private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
source0.getNode() = source and
@@ -357,10 +359,10 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
)
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
deprecated private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
I::flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
deprecated private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
predicate flowsTo = hasFlow/3;
deprecated predicate flowsTo = hasFlow/3;

View File

@@ -10,10 +10,12 @@ private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
private import DataFlowImpl
import DataFlowImplCommonPublic
import FlowStateString
deprecated import FlowStateString
private import codeql.util.Unit
/**
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
*
* A configuration of interprocedural data flow analysis. This defines
* sources, sinks, and any other configurable aspect of the analysis. Each
* use of the global data flow library must define its own unique extension
@@ -48,7 +50,7 @@ private import codeql.util.Unit
* should instead depend on a `DataFlow2::Configuration`, a
* `DataFlow3::Configuration`, or a `DataFlow4::Configuration`.
*/
abstract class Configuration extends string {
abstract deprecated class Configuration extends string {
bindingset[this]
Configuration() { any() }
@@ -189,7 +191,7 @@ abstract class Configuration extends string {
* Good performance cannot be guaranteed in the presence of such recursion, so
* it should be replaced by using more than one copy of the data flow library.
*/
abstract private class ConfigurationRecursionPrevention extends Configuration {
abstract deprecated private class ConfigurationRecursionPrevention extends Configuration {
bindingset[this]
ConfigurationRecursionPrevention() { any() }
@@ -210,7 +212,7 @@ abstract private class ConfigurationRecursionPrevention extends Configuration {
}
}
private FlowState relevantState(Configuration config) {
deprecated private FlowState relevantState(Configuration config) {
config.isSource(_, result) or
config.isSink(_, result) or
config.isBarrier(_, result) or
@@ -219,17 +221,17 @@ private FlowState relevantState(Configuration config) {
}
private newtype TConfigState =
TMkConfigState(Configuration config, FlowState state) {
deprecated TMkConfigState(Configuration config, FlowState state) {
state = relevantState(config) or state instanceof FlowStateEmpty
}
private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
deprecated private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
deprecated private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
deprecated private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
private module Config implements FullStateConfigSig {
deprecated private module Config implements FullStateConfigSig {
class FlowState = TConfigState;
predicate isSource(Node source, FlowState state) {
@@ -296,13 +298,13 @@ private module Config implements FullStateConfigSig {
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
}
private import Impl<Config> as I
deprecated private import Impl<Config> as I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
*/
class PathNode instanceof I::PathNode {
deprecated class PathNode instanceof I::PathNode {
/** Gets a textual representation of this element. */
final string toString() { result = super.toString() }
@@ -329,10 +331,10 @@ class PathNode instanceof I::PathNode {
final Node getNode() { result = super.getNode() }
/** Gets the `FlowState` of this node. */
final FlowState getState() { result = getState(super.getState()) }
deprecated final FlowState getState() { result = getState(super.getState()) }
/** Gets the associated configuration. */
final Configuration getConfiguration() { result = getConfig(super.getState()) }
deprecated final Configuration getConfiguration() { result = getConfig(super.getState()) }
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() { result = super.getASuccessor() }
@@ -347,9 +349,9 @@ class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
module PathGraph = I::PathGraph;
deprecated module PathGraph = I::PathGraph;
private predicate hasFlow(Node source, Node sink, Configuration config) {
deprecated private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
source0.getNode() = source and
@@ -357,10 +359,10 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
)
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
deprecated private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
I::flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
deprecated private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
predicate flowsTo = hasFlow/3;
deprecated predicate flowsTo = hasFlow/3;

View File

@@ -8,7 +8,6 @@ private import DataFlowPublic
private import DataFlowDispatch
private import SsaImpl as SsaImpl
private import FlowSummaryImpl as FlowSummaryImpl
private import FlowSummaryImplSpecific as FlowSummaryImplSpecific
private import codeql.ruby.frameworks.data.ModelsAsData
/** Gets the callable in which this node occurs. */
@@ -113,7 +112,7 @@ module LocalFlow {
SsaImpl::lastRefBeforeRedefExt(def, bb, i, next.getDefinitionExt()) and
exprFrom = bb.getNode(i) and
exprFrom.getExpr() instanceof VariableReadAccess and
exprFrom = [nodeFrom.asExpr(), nodeFrom.(PostUpdateNode).getPreUpdateNode().asExpr()]
exprFrom = [nodeFrom.asExpr(), nodeFrom.(PostUpdateNodeImpl).getPreUpdateNode().asExpr()]
)
}
@@ -144,6 +143,32 @@ module LocalFlow {
SsaImpl::adjacentReadPairExt(def, nodeFrom.asExpr(), nodeTo.asExpr())
}
/**
* Holds if SSA definition `def` assigns `value` to the underlying variable.
*
* This is either a direct assignment, `x = value`, or an assignment via
* simple pattern matching
*
* ```rb
* case value
* in Foo => x then ...
* in y => then ...
* end
* ```
*/
predicate ssaDefAssigns(Ssa::WriteDefinition def, CfgNodes::ExprCfgNode value) {
def.assigns(value)
or
exists(CfgNodes::ExprNodes::CaseExprCfgNode case, CfgNodes::AstCfgNode pattern |
case.getValue() = value and
pattern = case.getBranch(_).(CfgNodes::ExprNodes::InClauseCfgNode).getPattern()
|
def.getWriteAccess() = pattern
or
def.getWriteAccess() = pattern.(CfgNodes::ExprNodes::AsPatternCfgNode).getVariableAccess()
)
}
/**
* Holds if there is a local flow step from `nodeFrom` to `nodeTo` involving
* SSA definition `def`.
@@ -151,7 +176,7 @@ module LocalFlow {
pragma[nomagic]
predicate localSsaFlowStep(SsaImpl::DefinitionExt def, Node nodeFrom, Node nodeTo) {
// Flow from assignment into SSA definition
def.(Ssa::WriteDefinition).assigns(nodeFrom.asExpr()) and
ssaDefAssigns(def, nodeFrom.asExpr()) and
nodeTo.(SsaDefinitionExtNode).getDefinitionExt() = def
or
// Flow from SSA definition to first read
@@ -159,7 +184,7 @@ module LocalFlow {
firstReadExt(def, nodeTo.asExpr())
or
// Flow from post-update read to next read
localSsaFlowStepUseUse(def, nodeFrom.(PostUpdateNode).getPreUpdateNode(), nodeTo)
localSsaFlowStepUseUse(def, nodeFrom.(PostUpdateNodeImpl).getPreUpdateNode(), nodeTo)
or
// Flow into phi (read) SSA definition node from def
localFlowSsaInputFromDef(nodeFrom, def, nodeTo)
@@ -274,7 +299,7 @@ module VariableCapture {
or
exists(Ssa::Definition def |
def.getARead() = e2 and
def.getAnUltimateDefinition().(Ssa::WriteDefinition).assigns(e1)
LocalFlow::ssaDefAssigns(def.getAnUltimateDefinition(), e1)
)
}
@@ -454,7 +479,7 @@ private predicate splatArgumentAt(CfgNodes::ExprNodes::CallCfgNode c, int pos) {
/** A collection of cached types and predicates to be evaluated in the same stage. */
cached
private module Cached {
private import codeql.ruby.typetracking.TypeTrackerSpecific as TypeTrackerSpecific
private import codeql.ruby.typetracking.internal.TypeTrackingImpl
cached
newtype TNode =
@@ -573,14 +598,9 @@ private module Cached {
VariableCapture::flowInsensitiveStep(nodeFrom, nodeTo)
}
/** Holds if `n` wraps an SSA definition without ingoing flow. */
private predicate entrySsaDefinition(SsaDefinitionExtNode n) {
n = LocalFlow::getParameterDefNode(_)
or
exists(SsaImpl::DefinitionExt def | def = n.getDefinitionExt() |
def instanceof Ssa::SelfDefinition
or
def instanceof Ssa::CapturedEntryDefinition
)
n.getDefinitionExt() = any(SsaImpl::WriteDefinition def | not LocalFlow::ssaDefAssigns(def, _))
}
pragma[nomagic]
@@ -597,6 +617,16 @@ private module Cached {
)
}
private predicate isStoreTargetNode(Node n) {
TypeTrackingInput::storeStep(_, n, _)
or
TypeTrackingInput::loadStoreStep(_, n, _, _)
or
TypeTrackingInput::withContentStepImpl(_, n, _)
or
TypeTrackingInput::withoutContentStepImpl(_, n, _)
}
cached
predicate isLocalSourceNode(Node n) {
n instanceof TSourceParameterNode
@@ -612,11 +642,9 @@ private module Cached {
entrySsaDefinition(n) and
not LocalFlow::localFlowSsaParamInput(_, n)
or
TypeTrackerSpecific::basicStoreStep(_, n, _)
isStoreTargetNode(n)
or
TypeTrackerSpecific::basicLoadStep(_, n, _)
or
TypeTrackerSpecific::basicLoadStoreStep(_, n, _, _)
TypeTrackingInput::loadStep(_, n, _)
}
cached
@@ -625,13 +653,12 @@ private module Cached {
TAnyElementContent() or
TKnownOrUnknownElementContent(Content::KnownElementContent c) or
TElementLowerBoundContent(int lower, boolean includeUnknown) {
FlowSummaryImplSpecific::ParsePositions::isParsedElementLowerBoundPosition(_, includeUnknown,
lower)
FlowSummaryImpl::ParsePositions::isParsedElementLowerBoundPosition(_, includeUnknown, lower)
} or
TElementContentOfTypeContent(string type, Boolean includeUnknown) {
type = any(Content::KnownElementContent content).getIndex().getValueType()
} or
TNoContentSet() // Only used by type-tracking
deprecated TNoContentSet() // Only used by type-tracking
cached
class TContentSet =
@@ -696,6 +723,21 @@ private module Cached {
THashSplatContentApprox(string approx) { approx = approxKnownElementIndex(_) } or
TNonElementContentApprox(Content c) { not c instanceof Content::ElementContent } or
TCapturedVariableContentApprox(VariableCapture::CapturedVariable v)
cached
newtype TDataFlowType =
TLambdaDataFlowType(Callable c) { c = any(LambdaSelfReferenceNode n).getCallable() } or
// In order to reduce the set of cons-candidates, we annotate all implicit (hash) splat
// creations with the name of the method that they are passed into. This includes
// array/hash literals as well (where the name is simply `[]`), because of how they
// are modeled (see `Array.qll` and `Hash.qll`).
TSynthHashSplatArgumentType(string methodName) {
methodName = any(SynthHashSplatArgumentNode n).getMethodName()
} or
TSynthSplatArgumentType(string methodName) {
methodName = any(SynthSplatArgumentNode n).getMethodName()
} or
TUnknownDataFlowType()
}
class TElementContent =
@@ -1250,11 +1292,11 @@ module ArgumentNodes {
}
private class SummaryArgumentNode extends FlowSummaryNode, ArgumentNode {
private DataFlowCall call_;
private SummaryCall call_;
private ArgumentPosition pos_;
SummaryArgumentNode() {
FlowSummaryImpl::Private::summaryArgumentNode(call_, this.getSummaryNode(), pos_)
FlowSummaryImpl::Private::summaryArgumentNode(call_.getReceiver(), this.getSummaryNode(), pos_)
}
override predicate sourceArgumentOf(CfgNodes::ExprNodes::CallCfgNode call, ArgumentPosition pos) {
@@ -1637,11 +1679,11 @@ private module OutNodes {
}
private class SummaryOutNode extends FlowSummaryNode, OutNode {
private DataFlowCall call;
private SummaryCall call;
private ReturnKind kind_;
SummaryOutNode() {
FlowSummaryImpl::Private::summaryOutNode(call, this.getSummaryNode(), kind_)
FlowSummaryImpl::Private::summaryOutNode(call.getReceiver(), this.getSummaryNode(), kind_)
}
override DataFlowCall getCall(ReturnKind kind) { result = call and kind = kind_ }
@@ -1799,20 +1841,6 @@ predicate expectsContent(Node n, ContentSet c) {
FlowSummaryImpl::Private::Steps::summaryExpectsContent(n.(FlowSummaryNode).getSummaryNode(), c)
}
private newtype TDataFlowType =
TLambdaDataFlowType(Callable c) { c = any(LambdaSelfReferenceNode n).getCallable() } or
// In order to reduce the set of cons-candidates, we annotate all implicit (hash) splat
// creations with the name of the method that they are passed into. This includes
// array/hash literals as well (where the name is simply `[]`), because of how they
// are modeled (see `Array.qll` and `Hash.qll`).
TSynthHashSplatArgumentType(string methodName) {
methodName = any(SynthHashSplatArgumentNode n).getMethodName()
} or
TSynthSplatArgumentType(string methodName) {
methodName = any(SynthSplatArgumentNode n).getMethodName()
} or
TUnknownDataFlowType()
class DataFlowType extends TDataFlowType {
string toString() { result = "" }
}
@@ -2039,7 +2067,10 @@ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preserves
* by default as a heuristic.
*/
predicate allowParameterReturnInSelf(ParameterNodeImpl p) {
FlowSummaryImpl::Private::summaryAllowParameterReturnInSelf(p)
exists(DataFlowCallable c, ParameterPosition pos |
p.isParameterOf(c, pos) and
FlowSummaryImpl::Private::summaryAllowParameterReturnInSelf(c.asLibraryCallable(), pos)
)
or
VariableCapture::Flow::heuristicAllowInstanceParameterReturnInSelf(p.(SelfParameterNode)
.getCallable())

View File

@@ -2,10 +2,9 @@ private import codeql.ruby.AST
private import DataFlowDispatch
private import DataFlowPrivate
private import codeql.ruby.CFG
private import codeql.ruby.typetracking.TypeTracker
private import codeql.ruby.typetracking.internal.TypeTrackingImpl
private import codeql.ruby.dataflow.SSA
private import FlowSummaryImpl as FlowSummaryImpl
private import SsaImpl as SsaImpl
private import codeql.ruby.ApiGraphs
/**
@@ -213,12 +212,14 @@ class ExprNode extends Node, TExprNode {
* The value of a parameter at function entry, viewed as a node in a data
* flow graph.
*/
class ParameterNode extends LocalSourceNode instanceof ParameterNodeImpl {
class ParameterNode extends LocalSourceNode {
ParameterNode() { exists(getParameterPosition(this, _)) }
/** Gets the parameter corresponding to this node, if any. */
final Parameter getParameter() { result = super.getParameter() }
final Parameter getParameter() { result = getParameter(this) }
/** Gets the callable that this parameter belongs to. */
final Callable getCallable() { result = super.getCfgScope() }
final Callable getCallable() { result = getCfgScope(this) }
/** Gets the name of the parameter, if any. */
final string getName() { result = this.getParameter().(NamedParameter).getName() }
@@ -245,7 +246,7 @@ class LocalSourceNode extends Node {
/** Holds if this `LocalSourceNode` can flow to `nodeTo` in one or more local flow steps. */
pragma[inline]
predicate flowsTo(Node nodeTo) { hasLocalSource(nodeTo, this) }
predicate flowsTo(Node nodeTo) { flowsTo(this, nodeTo) }
/**
* Gets a node that this node may flow to using one heap and/or interprocedural step.
@@ -261,14 +262,14 @@ class LocalSourceNode extends Node {
* See `TypeBackTracker` for more details about how to use this.
*/
pragma[inline]
LocalSourceNode backtrack(TypeBackTracker t2, TypeBackTracker t) { t2 = t.step(result, this) }
LocalSourceNode backtrack(TypeBackTracker t2, TypeBackTracker t) { t = t2.step(result, this) }
/**
* Gets a node to which data may flow from this node in zero or
* more local data-flow steps.
*/
pragma[inline]
Node getALocalUse() { hasLocalSource(result, this) }
Node getALocalUse() { flowsTo(this, result) }
/** Gets a method call where this node flows to the receiver. */
CallNode getAMethodCall() { Cached::hasMethodCall(this, result, _) }
@@ -349,26 +350,30 @@ class LocalSourceNode extends Node {
* Nodes corresponding to AST elements, for example `ExprNode`, usually refer
* to the value before the update.
*/
class PostUpdateNode extends Node instanceof PostUpdateNodeImpl {
class PostUpdateNode extends Node {
private Node pre;
PostUpdateNode() { pre = getPreUpdateNode(this) }
/** Gets the node before the state update. */
Node getPreUpdateNode() { result = super.getPreUpdateNode() }
Node getPreUpdateNode() { result = pre }
}
/** An SSA definition, viewed as a node in a data flow graph. */
class SsaDefinitionNode extends Node instanceof SsaDefinitionExtNode {
Ssa::Definition def;
SsaDefinitionNode() { this = TSsaDefinitionExtNode(def) }
/** Gets the underlying SSA definition. */
Ssa::Definition getDefinition() { result = def }
/** Gets the underlying variable. */
Variable getVariable() { result = def.getSourceVariable() }
}
cached
private module Cached {
cached
predicate hasLocalSource(Node sink, Node source) {
// Declaring `source` to be a `SourceNode` currently causes a redundant check in the
// recursive case, so instead we check it explicitly here.
source = sink and
source instanceof LocalSourceNode
or
exists(Node mid |
hasLocalSource(mid, source) and
localFlowStepTypeTracker(mid, sink)
)
}
cached
predicate hasMethodCall(LocalSourceNode source, CallNode call, string name) {
source.flowsTo(call.getReceiver()) and
@@ -384,6 +389,28 @@ private module Cached {
)
}
cached
CfgScope getCfgScope(NodeImpl node) { result = node.getCfgScope() }
cached
ReturnNode getAReturnNode(Callable callable) { getCfgScope(result) = callable }
cached
Parameter getParameter(ParameterNodeImpl param) { result = param.getParameter() }
cached
ParameterPosition getParameterPosition(ParameterNodeImpl param, DataFlowCallable c) {
param.isParameterOf(c, result)
}
cached
ParameterPosition getSourceParameterPosition(ParameterNodeImpl param, Callable c) {
param.isSourceParameterOf(c, result)
}
cached
Node getPreUpdateNode(PostUpdateNodeImpl node) { result = node.getPreUpdateNode() }
cached
predicate methodHasSuperCall(MethodNode method, CallNode call) {
call.isSuperCall() and method = call.getEnclosingMethod()
@@ -1272,7 +1299,7 @@ class CallableNode extends StmtSequenceNode {
Callable asCallableAstNode() { result = callable }
private ParameterPosition getParameterPosition(ParameterNodeImpl node) {
node.isSourceParameterOf(callable, result)
result = getSourceParameterPosition(node, callable)
}
/** Gets the `n`th positional parameter. */
@@ -1312,7 +1339,7 @@ class CallableNode extends StmtSequenceNode {
/**
* Gets a data flow node whose value is about to be returned by this callable.
*/
Node getAReturnNode() { result.(ReturnNode).(NodeImpl).getCfgScope() = callable }
Node getAReturnNode() { result = getAReturnNode(callable) }
/**
* DEPRECATED. Use `getAReturnNode` instead.

File diff suppressed because it is too large Load Diff

View File

@@ -1,430 +0,0 @@
/**
* Provides Ruby specific classes and predicates for defining flow summaries.
*/
private import codeql.ruby.AST
private import DataFlowDispatch
private import DataFlowPrivate
private import DataFlowPublic
private import DataFlowImplCommon
private import FlowSummaryImpl::Private
private import FlowSummaryImpl::Public
private import codeql.ruby.dataflow.FlowSummary as FlowSummary
/**
* A class of callables that are candidates for flow summary modeling.
*/
class SummarizedCallableBase = string;
/**
* A class of callables that are candidates for neutral modeling.
*/
class NeutralCallableBase = string;
DataFlowCallable inject(SummarizedCallable c) { result.asLibraryCallable() = c }
/** Gets the parameter position representing a callback itself, if any. */
ArgumentPosition callbackSelfParameterPosition() { result.isLambdaSelf() }
/** Gets the synthesized data-flow call for `receiver`. */
SummaryCall summaryDataFlowCall(SummaryNode receiver) { receiver = result.getReceiver() }
/** Gets the type of content `c`. */
DataFlowType getContentType(ContentSet c) { any() }
/** Gets the type of the parameter at the given position. */
DataFlowType getParameterType(SummarizedCallable c, ParameterPosition pos) { any() }
/** Gets the return type of kind `rk` for callable `c`. */
bindingset[c, rk]
DataFlowType getReturnType(SummarizedCallable c, ReturnKind rk) { any() }
/**
* Gets the type of the `i`th parameter in a synthesized call that targets a
* callback of type `t`.
*/
bindingset[t, pos]
DataFlowType getCallbackParameterType(DataFlowType t, ArgumentPosition pos) { any() }
/**
* Gets the return type of kind `rk` in a synthesized call that targets a
* callback of type `t`.
*/
DataFlowType getCallbackReturnType(DataFlowType t, ReturnKind rk) { any() }
/** Gets the type of synthetic global `sg`. */
DataFlowType getSyntheticGlobalType(SummaryComponent::SyntheticGlobal sg) { any() }
/**
* Holds if an external flow summary exists for `c` with input specification
* `input`, output specification `output`, kind `kind`, and provenance `provenance`.
*/
predicate summaryElement(
FlowSummary::SummarizedCallable c, string input, string output, string kind, string provenance
) {
exists(boolean preservesValue |
c.propagatesFlowExt(input, output, preservesValue) and
(if preservesValue = true then kind = "value" else kind = "taint") and
provenance = "manual"
)
}
/**
* Holds if a neutral model exists for `c` of kind `kind`
* and with provenance `provenance`.
* Note. Neutral models have not been implemented for Ruby.
*/
predicate neutralElement(NeutralCallableBase c, string kind, string provenance) { none() }
bindingset[arg]
private SummaryComponent interpretElementArg(string arg) {
arg = "?" and
result = FlowSummary::SummaryComponent::elementUnknown()
or
arg = "any" and
result = FlowSummary::SummaryComponent::elementAny()
or
exists(int lower, boolean includeUnknown |
ParsePositions::isParsedElementLowerBoundPosition(arg, includeUnknown, lower)
|
includeUnknown = false and
result = FlowSummary::SummaryComponent::elementLowerBound(lower)
or
includeUnknown = true and
result = FlowSummary::SummaryComponent::elementLowerBoundOrUnknown(lower)
)
or
exists(ConstantValue cv, string argAdjusted, boolean includeUnknown |
argAdjusted = ParsePositions::adjustElementArgument(arg, includeUnknown) and
(
includeUnknown = false and
result = FlowSummary::SummaryComponent::elementKnown(cv)
or
includeUnknown = true and
result = FlowSummary::SummaryComponent::elementKnownOrUnknown(cv)
)
|
cv.isInt(AccessPath::parseInt(argAdjusted))
or
not exists(AccessPath::parseInt(argAdjusted)) and
cv.serialize() = argAdjusted
)
}
/**
* Gets the summary component for specification component `c`, if any.
*
* This covers all the Ruby-specific components of a flow summary.
*/
SummaryComponent interpretComponentSpecific(AccessPathToken c) {
exists(string arg, ParameterPosition ppos |
arg = c.getAnArgument("Argument") and
result = FlowSummary::SummaryComponent::argument(ppos)
|
arg = "any" and
ppos.isAny()
or
ppos.isPositionalLowerBound(AccessPath::parseLowerBound(arg))
or
arg = "hash-splat" and
ppos.isHashSplat()
or
arg = "splat" and
ppos.isSplat(0)
)
or
result = interpretElementArg(c.getAnArgument("Element"))
or
result =
FlowSummary::SummaryComponent::content(TSingletonContent(TFieldContent(c.getAnArgument("Field"))))
or
exists(ContentSet cs |
FlowSummary::SummaryComponent::content(cs) = interpretElementArg(c.getAnArgument("WithElement")) and
result = FlowSummary::SummaryComponent::withContent(cs)
)
or
exists(ContentSet cs |
FlowSummary::SummaryComponent::content(cs) =
interpretElementArg(c.getAnArgument("WithoutElement")) and
result = FlowSummary::SummaryComponent::withoutContent(cs)
)
}
private string getContentSpecific(Content c) {
exists(string name | c = TFieldContent(name) and result = "Field[" + name + "]")
or
exists(ConstantValue cv |
c = TKnownElementContent(cv) and result = "Element[" + cv.serialize() + "!]"
)
or
c = TUnknownElementContent() and result = "Element[?]"
}
private string getContentSetSpecific(ContentSet cs) {
exists(Content c | cs = TSingletonContent(c) and result = getContentSpecific(c))
or
cs = TAnyElementContent() and result = "Element[any]"
or
exists(Content::KnownElementContent kec |
cs = TKnownOrUnknownElementContent(kec) and
result = "Element[" + kec.getIndex().serialize() + "]"
)
or
exists(int lower, boolean includeUnknown, string unknown |
cs = TElementLowerBoundContent(lower, includeUnknown) and
(if includeUnknown = true then unknown = "" else unknown = "!") and
result = "Element[" + lower + ".." + unknown + "]"
)
}
/** Gets the textual representation of a summary component in the format used for MaD models. */
string getMadRepresentationSpecific(SummaryComponent sc) {
exists(ContentSet cs | sc = TContentSummaryComponent(cs) and result = getContentSetSpecific(cs))
or
exists(ContentSet cs |
sc = TWithoutContentSummaryComponent(cs) and
result = "WithoutElement[" + getContentSetSpecific(cs) + "]"
)
or
exists(ContentSet cs |
sc = TWithContentSummaryComponent(cs) and
result = "WithElement[" + getContentSetSpecific(cs) + "]"
)
or
exists(ReturnKind rk |
sc = TReturnSummaryComponent(rk) and
not rk = getReturnValueKind() and
result = "ReturnValue[" + rk + "]"
)
}
/** Gets the textual representation of a parameter position in the format used for flow summaries. */
string getParameterPosition(ParameterPosition pos) {
exists(int i |
pos.isPositional(i) and
result = i.toString()
)
or
exists(int i |
pos.isPositionalLowerBound(i) and
result = i + ".."
)
or
exists(string name |
pos.isKeyword(name) and
result = name + ":"
)
or
pos.isSelf() and
result = "self"
or
pos.isLambdaSelf() and
result = "lambda-self"
or
pos.isBlock() and
result = "block"
or
pos.isAny() and
result = "any"
or
pos.isAnyNamed() and
result = "any-named"
or
pos.isHashSplat() and
result = "hash-splat"
or
pos.isSplat(0) and
result = "splat"
}
/** Gets the textual representation of an argument position in the format used for flow summaries. */
string getArgumentPosition(ArgumentPosition pos) {
pos.isSelf() and result = "self"
or
pos.isLambdaSelf() and result = "lambda-self"
or
pos.isBlock() and result = "block"
or
exists(int i |
pos.isPositional(i) and
result = i.toString()
)
or
exists(string name |
pos.isKeyword(name) and
result = name + ":"
)
}
/** Holds if input specification component `c` needs a reference. */
predicate inputNeedsReferenceSpecific(string c) { none() }
/** Holds if output specification component `c` needs a reference. */
predicate outputNeedsReferenceSpecific(string c) { none() }
/** Gets the return kind corresponding to specification `"ReturnValue"`. */
NormalReturnKind getReturnValueKind() { any() }
/**
* All definitions in this module are required by the shared implementation
* (for source/sink interpretation), but they are unused for Ruby, where
* we rely on API graphs instead.
*/
private module UnusedSourceSinkInterpretation {
/**
* Holds if an external source specification exists for `n` with output specification
* `output`, kind `kind`, and provenance `provenance`.
*/
predicate sourceElement(AstNode n, string output, string kind, string provenance) { none() }
/**
* Holds if an external sink specification exists for `n` with input specification
* `input`, kind `kind` and provenance `provenance`.
*/
predicate sinkElement(AstNode n, string input, string kind, string provenance) { none() }
class SourceOrSinkElement = AstNode;
/** An entity used to interpret a source/sink specification. */
class InterpretNode extends AstNode {
/** Gets the element that this node corresponds to, if any. */
SourceOrSinkElement asElement() { none() }
/** Gets the data-flow node that this node corresponds to, if any. */
Node asNode() { none() }
/** Gets the call that this node corresponds to, if any. */
DataFlowCall asCall() { none() }
/** Gets the callable that this node corresponds to, if any. */
DataFlowCallable asCallable() { none() }
/** Gets the target of this call, if any. */
Callable getCallTarget() { none() }
}
/** Provides additional sink specification logic. */
predicate interpretOutputSpecific(string c, InterpretNode mid, InterpretNode node) { none() }
/** Provides additional source specification logic. */
predicate interpretInputSpecific(string c, InterpretNode mid, InterpretNode node) { none() }
}
import UnusedSourceSinkInterpretation
module ParsePositions {
private import FlowSummaryImpl
private predicate isParamBody(string body) {
body = any(AccessPathToken tok).getAnArgument("Parameter")
}
private predicate isArgBody(string body) {
body = any(AccessPathToken tok).getAnArgument("Argument")
}
private predicate isElementBody(string body) {
body = any(AccessPathToken tok).getAnArgument(["Element", "WithElement", "WithoutElement"])
}
predicate isParsedParameterPosition(string c, int i) {
isParamBody(c) and
i = AccessPath::parseInt(c)
}
predicate isParsedArgumentPosition(string c, int i) {
isArgBody(c) and
i = AccessPath::parseInt(c)
}
predicate isParsedArgumentLowerBoundPosition(string c, int i) {
isArgBody(c) and
i = AccessPath::parseLowerBound(c)
}
predicate isParsedKeywordParameterPosition(string c, string paramName) {
isParamBody(c) and
c = paramName + ":"
}
predicate isParsedKeywordArgumentPosition(string c, string paramName) {
isArgBody(c) and
c = paramName + ":"
}
bindingset[arg]
string adjustElementArgument(string arg, boolean includeUnknown) {
result = arg.regexpCapture("(.*)!", 1) and
includeUnknown = false
or
result = arg and
not arg.matches("%!") and
includeUnknown = true
}
predicate isParsedElementLowerBoundPosition(string c, boolean includeUnknown, int lower) {
isElementBody(c) and
lower = AccessPath::parseLowerBound(adjustElementArgument(c, includeUnknown))
}
}
/** Gets the argument position obtained by parsing `X` in `Parameter[X]`. */
ArgumentPosition parseParamBody(string s) {
exists(int i |
ParsePositions::isParsedParameterPosition(s, i) and
result.isPositional(i)
)
or
exists(string name |
ParsePositions::isParsedKeywordParameterPosition(s, name) and
result.isKeyword(name)
)
or
s = "self" and
result.isSelf()
or
s = "lambda-self" and
result.isLambdaSelf()
or
s = "block" and
result.isBlock()
or
s = "any" and
result.isAny()
or
s = "any-named" and
result.isAnyNamed()
}
/** Gets the parameter position obtained by parsing `X` in `Argument[X]`. */
ParameterPosition parseArgBody(string s) {
exists(int i |
ParsePositions::isParsedArgumentPosition(s, i) and
result.isPositional(i)
)
or
exists(int i |
ParsePositions::isParsedArgumentLowerBoundPosition(s, i) and
result.isPositionalLowerBound(i)
)
or
exists(string name |
ParsePositions::isParsedKeywordArgumentPosition(s, name) and
result.isKeyword(name)
)
or
s = "self" and
result.isSelf()
or
s = "lambda-self" and
result.isLambdaSelf()
or
s = "block" and
result.isBlock()
or
s = "any" and
result.isAny()
or
s = "any-named" and
result.isAnyNamed()
}

View File

@@ -79,11 +79,16 @@ private module Cached {
cached
predicate defaultAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
// value of `case` expression into variables in patterns
exists(CfgNodes::ExprNodes::CaseExprCfgNode case, CfgNodes::ExprNodes::InClauseCfgNode clause |
nodeFrom.asExpr() = case.getValue() and
exists(
CfgNodes::ExprNodes::CaseExprCfgNode case, CfgNodes::ExprCfgNode value,
CfgNodes::ExprNodes::InClauseCfgNode clause, Ssa::Definition def
|
nodeFrom.asExpr() = value and
value = case.getValue() and
clause = case.getBranch(_) and
nodeTo.(SsaDefinitionExtNode).getDefinitionExt().(Ssa::Definition).getControlFlowNode() =
variablesInPattern(clause.getPattern())
def = nodeTo.(SsaDefinitionExtNode).getDefinitionExt() and
def.getControlFlowNode() = variablesInPattern(clause.getPattern()) and
not LocalFlow::ssaDefAssigns(def, value)
)
or
// operation involving `nodeFrom`

View File

@@ -1,4 +1,6 @@
/**
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
*
* Provides an implementation of global (interprocedural) taint tracking.
* This file re-exports the local (intraprocedural) taint-tracking analysis
* from `TaintTrackingParameter::Public` and adds a global analysis, mainly
@@ -12,6 +14,8 @@ import TaintTrackingParameter::Public
private import TaintTrackingParameter::Private
/**
* DEPRECATED: Use `Global` and `GlobalWithState` instead.
*
* A configuration of interprocedural taint tracking analysis. This defines
* sources, sinks, and any other configurable aspect of the analysis. Each
* use of the taint tracking library must define its own unique extension of
@@ -51,7 +55,7 @@ private import TaintTrackingParameter::Private
* Instead, the dependency should go to a `TaintTracking2::Configuration` or a
* `DataFlow2::Configuration`, `DataFlow3::Configuration`, etc.
*/
abstract class Configuration extends DataFlow::Configuration {
abstract deprecated class Configuration extends DataFlow::Configuration {
bindingset[this]
Configuration() { any() }

View File

@@ -541,7 +541,7 @@ private module ParamsSummaries {
result = paramsInstance().getAMethodCall(methodReturnsTaintFromSelf()).asExpr().getExpr()
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[self]" and
output = "ReturnValue" and
preservesValue = false
@@ -564,7 +564,7 @@ private module ParamsSummaries {
[result.getReceiver(), result.getArgument(0)]
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = ["Argument[self]", "Argument[0]"] and
output = "ReturnValue" and
preservesValue = false
@@ -588,7 +588,7 @@ private module ParamsSummaries {
[result.getReceiver(), result.getArgument(0)]
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = ["Argument[self]", "Argument[0]"] and
output = ["ReturnValue", "Argument[self]"] and
preservesValue = false

View File

@@ -61,7 +61,7 @@ module ActiveSupport {
]
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[self]" and output = "ReturnValue" and preservesValue = false
}
}
@@ -75,7 +75,7 @@ module ActiveSupport {
private class IdentitySummary extends SimpleSummarizedCallable {
IdentitySummary() { this = ["presence", "deep_dup"] }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[self]" and
output = "ReturnValue" and
preservesValue = true
@@ -109,7 +109,7 @@ module ActiveSupport {
private class ToJsonSummary extends SimpleSummarizedCallable {
ToJsonSummary() { this = "to_json" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = ["Argument[self]", "Argument[self].Element[any]"] and
output = "ReturnValue" and
preservesValue = false
@@ -124,7 +124,7 @@ module ActiveSupport {
private class WithIndifferentAccessSummary extends SimpleSummarizedCallable {
WithIndifferentAccessSummary() { this = "with_indifferent_access" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[self].Element[any]" and
output = "ReturnValue.Element[any]" and
preservesValue = true
@@ -137,7 +137,7 @@ module ActiveSupport {
private class ReverseMergeSummary extends SimpleSummarizedCallable {
ReverseMergeSummary() { this = ["reverse_merge", "with_defaults"] }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[self,0].WithElement[any]" and
output = "ReturnValue" and
preservesValue = true
@@ -150,7 +150,7 @@ module ActiveSupport {
private class ReverseMergeBangSummary extends SimpleSummarizedCallable {
ReverseMergeBangSummary() { this = ["reverse_merge!", "with_defaults!", "reverse_update"] }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[self,0].WithElement[any]" and
output = ["ReturnValue", "Argument[self]"] and
preservesValue = true
@@ -166,7 +166,7 @@ module ActiveSupport {
]
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[self].Element[any]" and
output = "ReturnValue.Element[?]" and
preservesValue = true
@@ -209,7 +209,7 @@ module ActiveSupport {
final override MethodCall getACall() { result = mc }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
(
exists(string s | s = getExtractComponent(mc, _) |
input = "Argument[self].Element[" + s + "!]" and
@@ -244,7 +244,7 @@ module ActiveSupport {
private class CompactBlankSummary extends SimpleSummarizedCallable {
CompactBlankSummary() { this = "compact_blank" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[self].Element[any]" and
output = "ReturnValue.Element[?]" and
preservesValue = true
@@ -254,7 +254,7 @@ module ActiveSupport {
private class ExcludingSummary extends SimpleSummarizedCallable {
ExcludingSummary() { this = ["excluding", "without"] }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[self].Element[any]" and
output = "ReturnValue.Element[?]" and
preservesValue = true
@@ -264,7 +264,7 @@ module ActiveSupport {
private class InOrderOfSummary extends SimpleSummarizedCallable {
InOrderOfSummary() { this = "in_order_of" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[self].Element[any]" and
output = "ReturnValue.Element[?]" and
preservesValue = true
@@ -277,7 +277,7 @@ module ActiveSupport {
private class IncludingSummary extends SimpleSummarizedCallable {
IncludingSummary() { this = "including" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
(
exists(ArrayIndex i |
input = "Argument[self].Element[" + i + "]" and
@@ -299,7 +299,7 @@ module ActiveSupport {
private class IndexBySummary extends SimpleSummarizedCallable {
IndexBySummary() { this = "index_by" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[self].Element[any]" and
output = ["Argument[block].Parameter[0]", "ReturnValue.Element[?]"] and
preservesValue = true
@@ -309,7 +309,7 @@ module ActiveSupport {
private class IndexWithSummary extends SimpleSummarizedCallable {
IndexWithSummary() { this = "index_with" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[self].Element[any]" and
output = "Argument[block].Parameter[0]" and
preservesValue = true
@@ -338,7 +338,7 @@ module ActiveSupport {
override MethodCall getACall() { result = mc }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[self].Element[0].Element[" + key + "]" and
output = "ReturnValue" and
preservesValue = true
@@ -369,7 +369,7 @@ module ActiveSupport {
override MethodCall getACall() { result = mc }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
exists(string s, int i |
s = getKeyArgument(mc, i) and
input = "Argument[self].Element[0].Element[" + s + "]" and
@@ -392,7 +392,7 @@ module ActiveSupport {
override MethodCall getACall() { result = mc }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[self].Element[any].Element[" + key + "]" and
output = "ReturnValue.Element[any]" and
preservesValue = true
@@ -423,7 +423,7 @@ module ActiveSupport {
override MethodCall getACall() { result = mc }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
exists(string s, int i |
s = getKeyArgument(mc, i) and
input = "Argument[self].Element[any].Element[" + s + "]" and
@@ -436,7 +436,7 @@ module ActiveSupport {
private class SoleSummary extends SimpleSummarizedCallable {
SoleSummary() { this = "sole" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[self].Element[0]" and
output = "ReturnValue" and
preservesValue = true
@@ -470,7 +470,7 @@ module ActiveSupport {
private class JsonEscapeSummary extends SimpleSummarizedCallable {
JsonEscapeSummary() { this = "json_escape" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[0]" and
output = "ReturnValue" and
preservesValue = false

View File

@@ -25,7 +25,7 @@ module Arel {
result = API::getTopLevelMember("Arel").getAMethodCall("sql").asExpr().getExpr()
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
}
}

View File

@@ -63,7 +63,7 @@ private class SplatSummary extends SummarizedCallable {
override SplatExpr getACallSimple() { any() }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
(
// *1 = [1]
input = "Argument[self].WithoutElement[any]" and
@@ -82,7 +82,7 @@ private class HashSplatSummary extends SummarizedCallable {
override HashSplatExpr getACallSimple() { any() }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[self].WithElement[any]" and
output = "ReturnValue" and
preservesValue = true

View File

@@ -18,7 +18,7 @@ module Erb {
override MethodCall getACall() { result = any(ErbTemplateNewCall c).asExpr().getExpr() }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
}
}

View File

@@ -115,7 +115,7 @@ module File {
result = API::getTopLevelMember("File").getAMethodCall(methodName).asExpr().getExpr()
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[0]" and
output = "ReturnValue" and
preservesValue = false
@@ -133,7 +133,7 @@ module File {
result = API::getTopLevelMember("File").getAMethodCall("join").asExpr().getExpr()
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[0,1..]" and
output = "ReturnValue" and
preservesValue = false

View File

@@ -19,7 +19,7 @@ module NetLdap {
override MethodCall getACall() { result = any(NetLdapConnection l).asExpr().getExpr() }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
}
}
@@ -32,7 +32,7 @@ module NetLdap {
override MethodCall getACall() { result = any(NetLdapFilter l).asExpr().getExpr() }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = ["Argument[0]", "Argument[1]"] and output = "ReturnValue" and preservesValue = false
}
}

View File

@@ -18,7 +18,7 @@ module Mysql2 {
override MethodCall getACall() { result = any(Mysql2Connection c).asExpr().getExpr() }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
}
}
@@ -66,7 +66,7 @@ module Mysql2 {
override MethodCall getACall() { result = any(Mysql2EscapeSanitization c).asExpr().getExpr() }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
}
}

View File

@@ -18,7 +18,7 @@ module Pg {
override MethodCall getACall() { result = any(PgConnection c).asExpr().getExpr() }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
}
}

View File

@@ -314,7 +314,7 @@ private predicate isPotentialRenderCall(MethodCall renderCall, Location loc, Erb
// TODO: initialization hooks, e.g. before_configuration, after_initialize...
// TODO: initializers
/** A synthetic global to represent the value passed to the `locals` argument of a render call for a specific ERB file. */
private class LocalAssignsHashSyntheticGlobal extends SummaryComponent::SyntheticGlobal {
private class LocalAssignsHashSyntheticGlobal extends string {
private ErbFile erbFile;
private string id;
// Note that we can't use an actual `Rails::RenderCall` here due to problems with non-monotonic recursion
@@ -346,7 +346,7 @@ private class RenderLocalsSummary extends SummarizedCallable {
override Rails::RenderCall getACall() { result = glob.getARenderCall() }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[locals:]" and
output = "SyntheticGlobal[" + glob + "]" and
preservesValue = true
@@ -364,7 +364,7 @@ private class AccessLocalsSummary extends SummarizedCallable {
result.getMethodName() = "local_assigns"
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "SyntheticGlobal[" + glob + "]" and
output = "ReturnValue" and
preservesValue = true
@@ -394,7 +394,7 @@ private class AccessLocalsKeySummary extends SummarizedCallable {
result.getReceiver() instanceof SelfVariableReadAccess
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "SyntheticGlobal[" + glob + "].Element[:" + methodName + "]" and
output = "ReturnValue" and
preservesValue = true

View File

@@ -19,7 +19,7 @@ module Sequel {
override MethodCall getACall() { result = any(SequelConnection c).asExpr().getExpr() }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
}
}

View File

@@ -133,7 +133,7 @@ module Sinatra {
/**
* A synthetic global representing the hash of local variables passed to an ERB template.
*/
class ErbLocalsHashSyntheticGlobal extends SummaryComponent::SyntheticGlobal {
class ErbLocalsHashSyntheticGlobal extends string {
private string id;
private MethodCall erbCall;
private ErbFile erbFile;
@@ -172,7 +172,7 @@ module Sinatra {
override MethodCall getACall() { result = any(ErbCall c).asExpr().getExpr() }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[locals:]" and
output = "SyntheticGlobal[" + any(ErbLocalsHashSyntheticGlobal global) + "]" and
preservesValue = true
@@ -207,7 +207,7 @@ module Sinatra {
result.getReceiver() instanceof SelfVariableReadAccess
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "SyntheticGlobal[" + global + "].Element[:" + local + "]" and
output = "ReturnValue" and
preservesValue = true

View File

@@ -94,7 +94,7 @@ module Sqlite3 {
override MethodCall getACall() { result = any(SQLite3QuoteSanitization c).asExpr().getExpr() }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
}
}

View File

@@ -5,7 +5,7 @@
private import codeql.ruby.Concepts
private import codeql.ruby.AST
private import codeql.ruby.DataFlow
private import codeql.ruby.typetracking.TypeTracker
private import codeql.ruby.typetracking.TypeTracking
private import codeql.ruby.ApiGraphs
private import codeql.ruby.controlflow.CfgNodes as CfgNodes

File diff suppressed because it is too large Load Diff

View File

@@ -17,7 +17,7 @@ private class Base64Decode extends SummarizedCallable {
.getExpr()
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[0]" and
output = "ReturnValue" and
preservesValue = false

View File

@@ -57,7 +57,10 @@ module Gem {
}
/** Gets the name of the gem */
string getName() { result = this.getSpecProperty("name").getConstantValue().getString() }
string getName() {
result = this.getSpecProperty("name").getConstantValue().getString() or
result = specCall.getArgument(0).getAValueReachingSink().getConstantValue().getString()
}
/** Gets a path that is loaded when the gem is required */
private string getARequirePath() {

View File

@@ -31,7 +31,7 @@ module Hash {
final override MethodCall getACallSimple() { result = getAStaticHashCall("[]") }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
// we make use of the special `hash-splat` argument kind, which contains all keyword
// arguments wrapped in an implicit hash, as well as explicit hash splat arguments
input = "Argument[hash-splat]" and
@@ -62,7 +62,7 @@ module Hash {
result.getNumberOfArguments() = 1
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
(
// Hash[{symbol: x}]
input = "Argument[0].WithElement[any]" and
@@ -102,7 +102,7 @@ module Hash {
exists(result.getArgument(i))
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
// Hash[:symbol, x]
input = "Argument[" + i + "]" and
output = "ReturnValue.Element[" + key.serialize() + "]" and
@@ -115,7 +115,7 @@ module Hash {
override MethodCall getACallSimple() { result = getAStaticHashCall("try_convert") }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[0].WithElement[any]" and
output = "ReturnValue" and
preservesValue = true
@@ -130,7 +130,7 @@ module Hash {
final override MethodCall getACallSimple() { result = mc }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[1]" and
output = "ReturnValue" and
preservesValue = true
@@ -145,8 +145,8 @@ module Hash {
this = "store(" + key.serialize() + ")"
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
super.propagatesFlowExt(input, output, preservesValue)
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
super.propagatesFlow(input, output, preservesValue)
or
input = "Argument[1]" and
output = "Argument[self].Element[" + key.serialize() + "]" and
@@ -164,8 +164,8 @@ module Hash {
this = "store"
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
super.propagatesFlowExt(input, output, preservesValue)
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
super.propagatesFlow(input, output, preservesValue)
or
input = "Argument[1]" and
output = "Argument[self].Element[?]" and
@@ -192,7 +192,7 @@ module Hash {
key = DataFlow::Content::getKnownElementIndex(mc.getArgument(0))
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[self].Element[" + key.serialize() + "]" and
output = "ReturnValue.Element[1]" and
preservesValue = true
@@ -208,7 +208,7 @@ module Hash {
not exists(DataFlow::Content::getKnownElementIndex(result.getArgument(0)))
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[self].Element[any].WithoutElement[any]" and
output = "ReturnValue.Element[1]" and
preservesValue = true
@@ -218,7 +218,7 @@ module Hash {
private class EachPairSummary extends SimpleSummarizedCallable {
EachPairSummary() { this = "each_pair" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
(
input = "Argument[self].Element[any]" and
output = "Argument[block].Parameter[1]"
@@ -233,7 +233,7 @@ module Hash {
private class EachValueSummary extends SimpleSummarizedCallable {
EachValueSummary() { this = "each_value" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
(
input = "Argument[self].Element[any]" and
output = "Argument[block].Parameter[0]"
@@ -264,7 +264,7 @@ module Hash {
final override MethodCall getACallSimple() { result = mc }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input =
"Argument[self]" +
concat(int i, string s |
@@ -290,7 +290,7 @@ abstract private class FetchValuesSummary extends SummarizedCallable {
final override MethodCall getACallSimple() { result = mc }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
(
input = "Argument[self].WithElement[?]" and
output = "ReturnValue"
@@ -314,8 +314,8 @@ private class FetchValuesKnownSummary extends FetchValuesSummary {
this = "fetch_values(" + key.serialize() + ")"
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
super.propagatesFlowExt(input, output, preservesValue)
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
super.propagatesFlow(input, output, preservesValue)
or
input = "Argument[self].Element[" + key.serialize() + "]" and
output = "ReturnValue.Element[?]" and
@@ -329,8 +329,8 @@ private class FetchValuesUnknownSummary extends FetchValuesSummary {
this = "fetch_values(?)"
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
super.propagatesFlowExt(input, output, preservesValue)
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
super.propagatesFlow(input, output, preservesValue)
or
input = "Argument[self].Element[any]" and
output = "ReturnValue.Element[?]" and
@@ -345,7 +345,7 @@ private class MergeSummary extends SimpleSummarizedCallable {
this = ["merge", "deep_merge"]
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
(
input = "Argument[self,any].WithElement[any]" and
output = "ReturnValue"
@@ -364,7 +364,7 @@ private class MergeBangSummary extends SimpleSummarizedCallable {
this = ["merge!", "deep_merge!", "update"]
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
(
input = "Argument[self,any].WithElement[any]" and
output = ["ReturnValue", "Argument[self]"]
@@ -379,7 +379,7 @@ private class MergeBangSummary extends SimpleSummarizedCallable {
private class RassocSummary extends SimpleSummarizedCallable {
RassocSummary() { this = "rassoc" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[self].Element[any].WithoutElement[any]" and
output = "ReturnValue.Element[1]" and
preservesValue = true
@@ -404,7 +404,7 @@ private class SliceKnownSummary extends SliceSummary {
not key.isInt(_) // covered in `Array.qll`
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[self].WithElement[" + key.serialize() + "]" and
output = "ReturnValue" and
preservesValue = true
@@ -417,7 +417,7 @@ private class SliceUnknownSummary extends SliceSummary {
this = "slice(?)"
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[self].WithoutElement[0..!].WithElement[any]" and
output = "ReturnValue" and
preservesValue = true
@@ -427,7 +427,7 @@ private class SliceUnknownSummary extends SliceSummary {
private class ToASummary extends SimpleSummarizedCallable {
ToASummary() { this = "to_a" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[self].WithoutElement[0..!].Element[any]" and
output = "ReturnValue.Element[?].Element[1]" and
preservesValue = true
@@ -437,7 +437,7 @@ private class ToASummary extends SimpleSummarizedCallable {
private class ToHWithoutBlockSummary extends SimpleSummarizedCallable {
ToHWithoutBlockSummary() { this = ["to_h", "to_hash"] and not exists(mc.getBlock()) }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[self].WithElement[any]" and
output = "ReturnValue" and
preservesValue = true
@@ -447,7 +447,7 @@ private class ToHWithoutBlockSummary extends SimpleSummarizedCallable {
private class ToHWithBlockSummary extends SimpleSummarizedCallable {
ToHWithBlockSummary() { this = "to_h" and exists(mc.getBlock()) }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
(
input = "Argument[self].Element[any]" and
output = "Argument[block].Parameter[1]"
@@ -462,7 +462,7 @@ private class ToHWithBlockSummary extends SimpleSummarizedCallable {
private class TransformKeysSummary extends SimpleSummarizedCallable {
TransformKeysSummary() { this = "transform_keys" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[self].Element[any]" and
output = "ReturnValue.Element[?]" and
preservesValue = true
@@ -472,7 +472,7 @@ private class TransformKeysSummary extends SimpleSummarizedCallable {
private class TransformKeysBangSummary extends SimpleSummarizedCallable {
TransformKeysBangSummary() { this = "transform_keys!" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
(
input = "Argument[self].Element[any]" and
output = "Argument[self].Element[?]"
@@ -484,7 +484,7 @@ private class TransformKeysBangSummary extends SimpleSummarizedCallable {
private class TransformValuesSummary extends SimpleSummarizedCallable {
TransformValuesSummary() { this = "transform_values" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
(
input = "Argument[self].Element[any]" and
output = "Argument[block].Parameter[0]"
@@ -499,7 +499,7 @@ private class TransformValuesSummary extends SimpleSummarizedCallable {
private class TransformValuesBangSummary extends SimpleSummarizedCallable {
TransformValuesBangSummary() { this = "transform_values!" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
(
input = "Argument[self].Element[any]" and
output = "Argument[block].Parameter[0]"
@@ -517,7 +517,7 @@ private class TransformValuesBangSummary extends SimpleSummarizedCallable {
private class ValuesSummary extends SimpleSummarizedCallable {
ValuesSummary() { this = "values" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[self].Element[any]" and
output = "ReturnValue.Element[?]" and
preservesValue = true

View File

@@ -177,7 +177,7 @@ module Kernel {
private class TapSummary extends SimpleSummarizedCallable {
TapSummary() { this = "tap" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[self]" and
output = ["ReturnValue", "Argument[block].Parameter[0]"] and
preservesValue = true
@@ -219,7 +219,7 @@ module Kernel {
)
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
(
// already an array
input = "Argument[0].WithElement[0..]" and

View File

@@ -36,7 +36,7 @@ module Object {
private class DupSummary extends SimpleSummarizedCallable {
DupSummary() { this = "dup" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[self]" and
output = "ReturnValue" and
preservesValue = true

View File

@@ -127,7 +127,7 @@ module String {
result = API::getTopLevelMember("String").getAnInstantiation().getExprNode().getExpr()
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[0]" and
output = "ReturnValue" and
preservesValue = true
@@ -142,7 +142,7 @@ module String {
API::getTopLevelMember("String").getAMethodCall("try_convert").getExprNode().getExpr()
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[0]" and
output = "ReturnValue" and
preservesValue = false
@@ -155,7 +155,7 @@ module String {
private class FormatSummary extends SimpleSummarizedCallable {
FormatSummary() { this = "%" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = ["Argument[self]", "Argument[0]", "Argument[0].Element[any]"] and
output = "ReturnValue" and
preservesValue = false
@@ -169,7 +169,7 @@ module String {
private class BSummary extends SimpleSummarizedCallable {
BSummary() { this = "b" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
taintIdentityFlow(input, output, preservesValue)
}
}
@@ -180,7 +180,7 @@ module String {
private class BytesliceSummary extends SimpleSummarizedCallable {
BytesliceSummary() { this = "byteslice" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
taintIdentityFlow(input, output, preservesValue)
}
}
@@ -191,7 +191,7 @@ module String {
private class CapitalizeSummary extends SimpleSummarizedCallable {
CapitalizeSummary() { this = ["capitalize", "capitalize!"] }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[self]" and
preservesValue = false and
output = "ReturnValue"
@@ -204,7 +204,7 @@ module String {
private class CenterSummary extends SimpleSummarizedCallable {
CenterSummary() { this = ["center", "ljust", "rjust"] }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
taintIdentityFlow(input, output, preservesValue)
or
input = "Argument[1]" and
@@ -219,7 +219,7 @@ module String {
private class ChompSummary extends SimpleSummarizedCallable {
ChompSummary() { this = ["chomp", "chomp!", "chop", "chop!"] }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
taintIdentityFlow(input, output, preservesValue)
or
this = ["chomp!", "chop!"] and
@@ -236,6 +236,10 @@ module String {
*/
private class ClearSummary extends SimpleSummarizedCallable {
ClearSummary() { none() }
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
none()
}
}
/**
@@ -249,7 +253,7 @@ module String {
none()
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[self,0..]" and
output = ["ReturnValue", "Argument[self]"] and
preservesValue = false
@@ -262,7 +266,7 @@ module String {
private class DeleteSummary extends SimpleSummarizedCallable {
DeleteSummary() { this = ["delete", "delete_prefix", "delete_suffix"] + ["", "!"] }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
taintIdentityFlow(input, output, preservesValue)
}
}
@@ -273,7 +277,7 @@ module String {
private class DowncaseSummary extends SimpleSummarizedCallable {
DowncaseSummary() { this = ["downcase", "upcase", "swapcase"] + ["", "!"] }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
taintIdentityFlow(input, output, preservesValue)
}
}
@@ -284,7 +288,7 @@ module String {
private class DumpSummary extends SimpleSummarizedCallable {
DumpSummary() { this = ["dump", "undump"] }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
taintIdentityFlow(input, output, preservesValue)
}
}
@@ -308,7 +312,7 @@ module String {
private class EachLineBlockSummary extends EachLineSummary {
EachLineBlockSummary() { this = "each_line_with_block" and exists(mc.getBlock()) }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = false and
input = "Argument[self]" and
output = ["Argument[block].Parameter[0]", "ReturnValue"]
@@ -321,7 +325,7 @@ module String {
private class EachLineNoBlockSummary extends EachLineSummary {
EachLineNoBlockSummary() { this = "each_line_without_block" and not exists(mc.getBlock()) }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = false and
input = "Argument[self]" and
output = "ReturnValue.Element[?]"
@@ -334,7 +338,7 @@ module String {
private class EncodeSummary extends SimpleSummarizedCallable {
EncodeSummary() { this = ["encode", "unicode_normalize"] + ["", "!"] }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
taintIdentityFlow(input, output, preservesValue)
}
}
@@ -345,7 +349,7 @@ module String {
private class ForceEncodingSummary extends SimpleSummarizedCallable {
ForceEncodingSummary() { this = "force_encoding" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
taintIdentityFlow(input, output, preservesValue)
}
}
@@ -356,7 +360,7 @@ module String {
private class FreezeSummary extends SimpleSummarizedCallable {
FreezeSummary() { this = "freeze" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
taintIdentityFlow(input, output, preservesValue)
}
}
@@ -370,7 +374,7 @@ module String {
// str.gsub(pattern, replacement) -> new_str
// str.gsub(pattern) {|match| block } -> new_str
// str.gsub(pattern) -> enumerator of matches
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
// receiver -> return value
// replacement -> return value
// block return -> return value
@@ -390,7 +394,7 @@ module String {
none()
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
taintIdentityFlow(input, output, preservesValue)
or
input = "Argument[1]" and output = "ReturnValue" and preservesValue = false
@@ -403,7 +407,7 @@ module String {
private class InspectSummary extends SimpleSummarizedCallable {
InspectSummary() { this = "inspect" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
taintIdentityFlow(input, output, preservesValue)
}
}
@@ -414,7 +418,7 @@ module String {
private class StripSummary extends SimpleSummarizedCallable {
StripSummary() { this = ["strip", "lstrip", "rstrip"] + ["", "!"] }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
taintIdentityFlow(input, output, preservesValue)
}
}
@@ -425,7 +429,7 @@ module String {
private class NextSummary extends SimpleSummarizedCallable {
NextSummary() { this = ["next", "succ"] + ["", "!"] }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
taintIdentityFlow(input, output, preservesValue)
}
}
@@ -436,7 +440,7 @@ module String {
private class PartitionSummary extends SimpleSummarizedCallable {
PartitionSummary() { this = ["partition", "rpartition"] }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[self]" and
output = "ReturnValue.Element[0,1,2]" and
preservesValue = false
@@ -449,7 +453,7 @@ module String {
private class ReplaceSummary extends SimpleSummarizedCallable {
ReplaceSummary() { this = "replace" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[0]" and
output = ["ReturnValue", "Argument[self]"] and
preservesValue = false
@@ -463,7 +467,7 @@ module String {
private class ReverseSummary extends SimpleSummarizedCallable {
ReverseSummary() { this = ["reverse", "reverse!"] }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
taintIdentityFlow(input, output, preservesValue)
}
}
@@ -483,7 +487,7 @@ module String {
private class ScanBlockSummary extends ScanSummary {
ScanBlockSummary() { this = "scan_with_block" and exists(mc.getBlock()) }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[self]" and
preservesValue = false and
output =
@@ -500,7 +504,7 @@ module String {
private class ScanNoBlockSummary extends ScanSummary {
ScanNoBlockSummary() { this = "scan_no_block" and not exists(mc.getBlock()) }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
// scan(pattern) -> array
input = "Argument[self]" and
output = "ReturnValue.Element[?]" and
@@ -523,7 +527,7 @@ module String {
private class ScrubBlockSummary extends ScrubSummary {
ScrubBlockSummary() { this = "scrub_block" and exists(mc.getBlock()) }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
taintIdentityFlow(input, output, preservesValue)
or
preservesValue = false and
@@ -542,7 +546,7 @@ module String {
private class ScrubNoBlockSummary extends ScrubSummary {
ScrubNoBlockSummary() { this = "scrub_no_block" and not exists(mc.getBlock()) }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
taintIdentityFlow(input, output, preservesValue)
or
preservesValue = false and
@@ -557,7 +561,7 @@ module String {
private class ShellescapeSummary extends SimpleSummarizedCallable {
ShellescapeSummary() { this = "shellescape" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
taintIdentityFlow(input, output, preservesValue)
}
}
@@ -568,7 +572,7 @@ module String {
private class ShellSplitSummary extends SimpleSummarizedCallable {
ShellSplitSummary() { this = "shellsplit" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[self]" and
output = "ReturnValue.Element[?]" and
preservesValue = false
@@ -581,7 +585,7 @@ module String {
private class SliceSummary extends SimpleSummarizedCallable {
SliceSummary() { this = ["slice", "slice!", "split", "[]"] }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
taintIdentityFlow(input, output, preservesValue)
}
}
@@ -592,7 +596,7 @@ module String {
private class SqueezeSummary extends SimpleSummarizedCallable {
SqueezeSummary() { this = ["squeeze", "squeeze!"] }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
taintIdentityFlow(input, output, preservesValue)
}
}
@@ -603,7 +607,7 @@ module String {
private class ToStrSummary extends SimpleSummarizedCallable {
ToStrSummary() { this = ["to_str", "to_s"] }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
taintIdentityFlow(input, output, preservesValue)
}
}
@@ -614,7 +618,7 @@ module String {
private class TrSummary extends SimpleSummarizedCallable {
TrSummary() { this = ["tr", "tr_s"] + ["", "!"] }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
taintIdentityFlow(input, output, preservesValue)
or
input = "Argument[1]" and output = "ReturnValue" and preservesValue = false
@@ -646,7 +650,7 @@ module String {
}
// TODO: if second arg ('exclusive') is true, the first arg is excluded
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
taintIdentityFlow(input, output, preservesValue)
or
input = ["Argument[self]", "Argument[0]"] and
@@ -668,7 +672,7 @@ module String {
mc.getArgument(1).getConstantValue().isBoolean(true)
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[self]" and
output = "Argument[block].Parameter[0]" and
preservesValue = false

View File

@@ -48,7 +48,7 @@ private class SummarizedCallableFromModel extends SummarizedCallable {
)
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
exists(string kind | ModelOutput::relevantSummaryModel(type, path, input, output, kind) |
kind = "value" and
preservesValue = true

View File

@@ -70,8 +70,8 @@ private module API = Specific::API;
private module DataFlow = Specific::DataFlow;
private import Specific::AccessPathSyntax
private import ApiGraphModelsExtensions as Extensions
private import codeql.dataflow.internal.AccessPathSyntax
/** Module containing hooks for providing input data to be interpreted as a model. */
module ModelInput {
@@ -327,29 +327,29 @@ predicate isRelevantFullPath(string type, string path) {
}
/** A string from a CSV row that should be parsed as an access path. */
private class AccessPathRange extends AccessPath::Range {
AccessPathRange() {
isRelevantFullPath(_, this)
or
exists(string type | isRelevantType(type) |
summaryModel(type, _, this, _, _) or
summaryModel(type, _, _, this, _)
)
or
typeVariableModel(_, this)
}
private predicate accessPathRange(string s) {
isRelevantFullPath(_, s)
or
exists(string type | isRelevantType(type) |
summaryModel(type, _, s, _, _) or
summaryModel(type, _, _, s, _)
)
or
typeVariableModel(_, s)
}
import AccessPath<accessPathRange/1>
/**
* Gets a successor of `node` in the API graph.
*/
bindingset[token]
API::Node getSuccessorFromNode(API::Node node, AccessPathToken token) {
API::Node getSuccessorFromNode(API::Node node, AccessPathTokenBase token) {
// API graphs use the same label for arguments and parameters. An edge originating from a
// use-node represents an argument, and an edge originating from a def-node represents a parameter.
// We just map both to the same thing.
token.getName() = ["Argument", "Parameter"] and
result = node.getParameter(AccessPath::parseIntUnbounded(token.getAnArgument()))
result = node.getParameter(parseIntUnbounded(token.getAnArgument()))
or
token.getName() = "ReturnValue" and
result = node.getReturn()
@@ -362,11 +362,9 @@ API::Node getSuccessorFromNode(API::Node node, AccessPathToken token) {
* Gets an API-graph successor for the given invocation.
*/
bindingset[token]
API::Node getSuccessorFromInvoke(Specific::InvokeNode invoke, AccessPathToken token) {
API::Node getSuccessorFromInvoke(Specific::InvokeNode invoke, AccessPathTokenBase token) {
token.getName() = "Argument" and
result =
invoke
.getParameter(AccessPath::parseIntWithArity(token.getAnArgument(), invoke.getNumArgument()))
result = invoke.getParameter(parseIntWithArity(token.getAnArgument(), invoke.getNumArgument()))
or
token.getName() = "ReturnValue" and
result = invoke.getReturn()
@@ -378,10 +376,12 @@ API::Node getSuccessorFromInvoke(Specific::InvokeNode invoke, AccessPathToken to
/**
* Holds if `invoke` invokes a call-site filter given by `token`.
*/
pragma[inline]
private predicate invocationMatchesCallSiteFilter(Specific::InvokeNode invoke, AccessPathToken token) {
bindingset[token]
private predicate invocationMatchesCallSiteFilter(
Specific::InvokeNode invoke, AccessPathTokenBase token
) {
token.getName() = "WithArity" and
invoke.getNumArgument() = AccessPath::parseIntUnbounded(token.getAnArgument())
invoke.getNumArgument() = parseIntUnbounded(token.getAnArgument())
or
Specific::invocationMatchesExtraCallSiteFilter(invoke, token)
}

View File

@@ -4,14 +4,13 @@
* It must export the following members:
* ```ql
* class Unit // a unit type
* module AccessPathSyntax // a re-export of the AccessPathSyntax module
* class InvokeNode // a type representing an invocation connected to the API graph
* module API // the API graph module
* predicate isPackageUsed(string package)
* API::Node getExtraNodeFromPath(string package, string type, string path, int n)
* API::Node getExtraSuccessorFromNode(API::Node node, AccessPathToken token)
* API::Node getExtraSuccessorFromInvoke(InvokeNode node, AccessPathToken token)
* predicate invocationMatchesExtraCallSiteFilter(InvokeNode invoke, AccessPathToken token)
* API::Node getExtraSuccessorFromNode(API::Node node, AccessPathTokenBase token)
* API::Node getExtraSuccessorFromInvoke(InvokeNode node, AccessPathTokenBase token)
* predicate invocationMatchesExtraCallSiteFilter(InvokeNode invoke, AccessPathTokenBase token)
* InvokeNode getAnInvocationOf(API::Node node)
* predicate isExtraValidTokenNameInIdentifyingAccessPath(string name)
* predicate isExtraValidNoArgumentTokenInIdentifyingAccessPath(string name)
@@ -21,13 +20,12 @@
private import codeql.ruby.AST
private import ApiGraphModels
private import codeql.ruby.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
private import codeql.dataflow.internal.AccessPathSyntax
// Re-export libraries needed by ApiGraphModels.qll
import codeql.ruby.ApiGraphs
import codeql.ruby.dataflow.internal.AccessPathSyntax as AccessPathSyntax
import codeql.ruby.DataFlow::DataFlow as DataFlow
private import AccessPathSyntax
private import codeql.ruby.dataflow.internal.FlowSummaryImplSpecific as FlowSummaryImplSpecific
private import codeql.ruby.dataflow.internal.FlowSummaryImpl::Public
private import FlowSummaryImpl::Public
private import codeql.ruby.dataflow.internal.DataFlowDispatch as DataFlowDispatch
pragma[nomagic]
@@ -140,7 +138,7 @@ private predicate methodMatchedByName(AccessPath path, string methodName) {
* Gets a Ruby-specific API graph successor of `node` reachable by resolving `token`.
*/
bindingset[token]
API::Node getExtraSuccessorFromNode(API::Node node, AccessPathToken token) {
API::Node getExtraSuccessorFromNode(API::Node node, AccessPathTokenBase token) {
token.getName() = "Member" and
result = node.getMember(token.getAnArgument())
or
@@ -152,13 +150,13 @@ API::Node getExtraSuccessorFromNode(API::Node node, AccessPathToken token) {
or
token.getName() = "Parameter" and
exists(DataFlowDispatch::ArgumentPosition argPos, DataFlowDispatch::ParameterPosition paramPos |
argPos = FlowSummaryImplSpecific::parseParamBody(token.getAnArgument()) and
token.getAnArgument() = FlowSummaryImpl::Input::encodeArgumentPosition(argPos) and
DataFlowDispatch::parameterMatch(paramPos, argPos) and
result = node.getParameterAtPosition(paramPos)
)
or
exists(DataFlow::ContentSet contents |
SummaryComponent::content(contents) = FlowSummaryImplSpecific::interpretComponentSpecific(token) and
token.getName() = FlowSummaryImpl::Input::encodeContent(contents, token.getAnArgument()) and
result = node.getContents(contents)
)
}
@@ -167,10 +165,10 @@ API::Node getExtraSuccessorFromNode(API::Node node, AccessPathToken token) {
* Gets a Ruby-specific API graph successor of `node` reachable by resolving `token`.
*/
bindingset[token]
API::Node getExtraSuccessorFromInvoke(InvokeNode node, AccessPathToken token) {
API::Node getExtraSuccessorFromInvoke(InvokeNode node, AccessPathTokenBase token) {
token.getName() = "Argument" and
exists(DataFlowDispatch::ArgumentPosition argPos, DataFlowDispatch::ParameterPosition paramPos |
paramPos = FlowSummaryImplSpecific::parseArgBody(token.getAnArgument()) and
token.getAnArgument() = FlowSummaryImpl::Input::encodeParameterPosition(paramPos) and
DataFlowDispatch::parameterMatch(paramPos, argPos) and
result = node.getArgumentAtPosition(argPos)
)
@@ -199,7 +197,7 @@ API::Node getAFuzzySuccessor(API::Node node) {
* Holds if `invoke` matches the Ruby-specific call site filter in `token`.
*/
bindingset[token]
predicate invocationMatchesExtraCallSiteFilter(InvokeNode invoke, AccessPathToken token) {
predicate invocationMatchesExtraCallSiteFilter(InvokeNode invoke, AccessPathTokenBase token) {
token.getName() = "WithBlock" and
exists(invoke.getBlock())
or

View File

@@ -6,7 +6,7 @@ private import codeql.ruby.AST
private import codeql.ruby.ApiGraphs
private import codeql.ruby.Concepts
private import codeql.ruby.DataFlow
private import codeql.ruby.typetracking.TypeTracker
private import codeql.ruby.typetracking.TypeTracking
private import Response::Private as RP
/**

View File

@@ -7,7 +7,7 @@ private import codeql.ruby.ApiGraphs
private import codeql.ruby.Concepts
private import codeql.ruby.controlflow.CfgNodes::ExprNodes
private import codeql.ruby.DataFlow
private import codeql.ruby.typetracking.TypeTracker
private import codeql.ruby.typetracking.TypeTracking
private import App as A
/** Contains implementation details for modeling `Rack::Response`. */

View File

@@ -22,7 +22,7 @@ module Utils {
.getExpr()
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
}
}

View File

@@ -17,7 +17,7 @@ private import codeql.ruby.AST as Ast
private import codeql.ruby.CFG
private import codeql.ruby.DataFlow
private import codeql.ruby.controlflow.CfgNodes
private import codeql.ruby.typetracking.TypeTracker
private import codeql.ruby.typetracking.internal.TypeTrackingImpl
private import codeql.ruby.ApiGraphs
private import codeql.ruby.Concepts
private import codeql.ruby.dataflow.internal.DataFlowPrivate as DataFlowPrivate
@@ -67,7 +67,7 @@ private signature module TypeTrackInputSig {
* Provides a version of type tracking where we first prune for reachable nodes,
* before doing the type tracking computation.
*/
private module TypeTrack<TypeTrackInputSig Input> {
private module PrunedTypeTrack<TypeTrackInputSig Input> {
private predicate additionalStep(
DataFlow::LocalSourceNode nodeFrom, DataFlow::LocalSourceNode nodeTo
) {
@@ -130,10 +130,10 @@ private module TypeTrack<TypeTrackInputSig Input> {
TypeTracker t, DataFlow::LocalSourceNode nodeFrom, DataFlow::LocalSourceNode nodeTo
) {
exists(StepSummary summary |
StepSummary::step(nodeFrom, nodeTo, summary) and
step(nodeFrom, nodeTo, summary) and
reached(nodeFrom, t) and
reached(nodeTo, result) and
result = t.append(summary)
result = append(t, summary)
)
or
additionalStep(nodeFrom, nodeTo) and
@@ -195,7 +195,7 @@ private module StringTypeTrackInput implements TypeTrackInputSig {
* This is used to figure out where `start` is evaluated as a regular expression against an input string,
* or where `start` is compiled into a regular expression.
*/
private predicate trackStrings = TypeTrack<StringTypeTrackInput>::track/2;
private predicate trackStrings = PrunedTypeTrack<StringTypeTrackInput>::track/2;
/** Holds if `strConst` flows to a regex compilation (tracked by `t`), where the resulting regular expression is stored in `reg`. */
pragma[nomagic]
@@ -222,7 +222,7 @@ private module RegTypeTrackInput implements TypeTrackInputSig {
* Gets a node that has been tracked from the regular expression `start` to some node.
* This is used to figure out where `start` is executed against an input string.
*/
private predicate trackRegs = TypeTrack<RegTypeTrackInput>::track/2;
private predicate trackRegs = PrunedTypeTrack<RegTypeTrackInput>::track/2;
/** Gets a node that references a regular expression. */
private DataFlow::LocalSourceNode trackRegexpType(TypeTracker t) {

View File

@@ -4,6 +4,7 @@ private import codeql.ruby.Concepts
private import codeql.ruby.Frameworks
private import codeql.ruby.dataflow.RemoteFlowSources
private import codeql.ruby.dataflow.BarrierGuards
private import codeql.ruby.frameworks.data.internal.ApiGraphModels
/**
* Provides default sources, sinks and sanitizers for detecting
@@ -156,4 +157,8 @@ module CodeInjection {
override FlowState::State getAState() { result instanceof FlowState::Full }
}
private class ExternalCodeInjectionSink extends Sink {
ExternalCodeInjectionSink() { this = ModelOutput::getASinkNode("code-injection").asSink() }
}
}

View File

@@ -9,6 +9,7 @@ private import codeql.ruby.dataflow.RemoteFlowSources
private import codeql.ruby.Concepts
private import codeql.ruby.Frameworks
private import codeql.ruby.ApiGraphs
private import codeql.ruby.frameworks.data.internal.ApiGraphModels
module CommandInjection {
/**
@@ -52,4 +53,10 @@ module CommandInjection {
this.(DataFlow::CallNode).getMethodName() = "shellescape"
}
}
private class ExternalCommandInjectionSink extends Sink {
ExternalCommandInjectionSink() {
this = ModelOutput::getASinkNode("command-injection").asSink()
}
}
}

View File

@@ -7,7 +7,7 @@
private import codeql.ruby.AST
private import codeql.ruby.DataFlow
private import codeql.ruby.Concepts
private import codeql.ruby.typetracking.TypeTracker
private import codeql.ruby.typetracking.TypeTracking
private import codeql.ruby.frameworks.Files
private import codeql.ruby.frameworks.core.IO

View File

@@ -8,6 +8,7 @@ import codeql.ruby.DataFlow
import codeql.ruby.TaintTracking
import codeql.ruby.dataflow.RemoteFlowSources
import codeql.ruby.frameworks.Core
private import codeql.ruby.frameworks.data.internal.ApiGraphModels
/**
* A data flow source for user input used in log entries.
@@ -50,6 +51,10 @@ class LoggingSink extends Sink {
LoggingSink() { this = any(Logging logging).getAnInput() }
}
private class ExternalLogInjectionSink extends Sink {
ExternalLogInjectionSink() { this = ModelOutput::getASinkNode("log-injection").asSink() }
}
/**
* A call to `String#replace` that replaces `\n` is considered to sanitize the replaced string (reduce false positive).
*/

View File

@@ -7,7 +7,7 @@ private import internal.CryptoAlgorithmNames
private import codeql.ruby.Concepts
private import codeql.ruby.DataFlow
private import codeql.ruby.ApiGraphs
private import codeql.ruby.typetracking.TypeTracker
private import codeql.ruby.typetracking.TypeTracking
bindingset[algorithmString]
private string algorithmRegex(string algorithmString) {

View File

@@ -11,6 +11,7 @@ private import codeql.ruby.Concepts
private import codeql.ruby.DataFlow
private import codeql.ruby.dataflow.BarrierGuards
private import codeql.ruby.dataflow.RemoteFlowSources
private import codeql.ruby.frameworks.data.internal.ApiGraphModels
module PathInjection {
/**
@@ -52,4 +53,8 @@ module PathInjection {
class StringConstArrayInclusionCallAsSanitizer extends Sanitizer,
StringConstArrayInclusionCallBarrier
{ }
private class ExternalPathInjectionSink extends Sink {
ExternalPathInjectionSink() { this = ModelOutput::getASinkNode("path-injection").asSink() }
}
}

View File

@@ -8,6 +8,7 @@ private import codeql.ruby.DataFlow
private import codeql.ruby.dataflow.BarrierGuards
private import codeql.ruby.dataflow.RemoteFlowSources
private import codeql.ruby.ApiGraphs
private import codeql.ruby.frameworks.data.internal.ApiGraphModels
/**
* Provides default sources, sinks and sanitizers for detecting SQL injection
@@ -56,4 +57,8 @@ module SqlInjection {
{ }
private class SqlSanitizationAsSanitizer extends Sanitizer, SqlSanitization { }
private class ExternalSqlInjectionSink extends Sink {
ExternalSqlInjectionSink() { this = ModelOutput::getASinkNode("sql-injection").asSink() }
}
}

View File

@@ -6,6 +6,7 @@
private import codeql.ruby.AST
private import codeql.ruby.Concepts
private import codeql.ruby.DataFlow
private import codeql.ruby.dataflow.SSA
private import codeql.ruby.controlflow.CfgNodes
private import codeql.ruby.frameworks.core.Kernel
@@ -29,10 +30,10 @@ module StackTraceExposure {
*/
class BacktraceCall extends Source, DataFlow::CallNode {
BacktraceCall() {
exists(DataFlow::LocalSourceNode varAccess |
varAccess.asExpr().(ExprNodes::VariableReadAccessCfgNode).getExpr().getVariable() =
any(RescueClause rc).getVariableExpr().(VariableAccess).getVariable() and
varAccess.flowsTo(this.getReceiver())
exists(DataFlow::SsaDefinitionNode ssaDef |
ssaDef.getDefinition().(Ssa::WriteDefinition).getWriteAccess().getAstNode() =
any(RescueClause rc).getVariableExpr() and
ssaDef.(DataFlow::LocalSourceNode).flowsTo(this.getReceiver())
) and
this.getMethodName() = ["backtrace", "backtrace_locations"]
}

View File

@@ -8,6 +8,7 @@ private import ruby
private import codeql.ruby.ApiGraphs
private import codeql.ruby.frameworks.core.Gem::Gem as Gem
private import codeql.ruby.Concepts as Concepts
private import codeql.ruby.typetracking.TypeTracking
/**
* Module containing sources, sinks, and sanitizers for code constructed from library input.
@@ -39,22 +40,20 @@ module UnsafeCodeConstruction {
/** Gets a node that is eventually executed as code at `codeExec`. */
DataFlow::Node getANodeExecutedAsCode(Concepts::CodeExecution codeExec) {
result = getANodeExecutedAsCode(TypeTracker::TypeBackTracker::end(), codeExec)
result = getANodeExecutedAsCode(TypeBackTracker::end(), codeExec)
}
import codeql.ruby.typetracking.TypeTracker as TypeTracker
deprecated import codeql.ruby.typetracking.TypeTracker as TypeTracker
/** Gets a node that is eventually executed as code at `codeExec`, type-tracked with `t`. */
private DataFlow::LocalSourceNode getANodeExecutedAsCode(
TypeTracker::TypeBackTracker t, Concepts::CodeExecution codeExec
TypeBackTracker t, Concepts::CodeExecution codeExec
) {
t.start() and
result = codeExec.getCode().getALocalSource() and
codeExec.runsArbitraryCode() // methods like `Object.send` is benign here, because of the string-construction the attacker cannot control the entire method name
or
exists(TypeTracker::TypeBackTracker t2 |
result = getANodeExecutedAsCode(t2, codeExec).backtrack(t2, t)
)
exists(TypeBackTracker t2 | result = getANodeExecutedAsCode(t2, codeExec).backtrack(t2, t))
}
/**

View File

@@ -7,6 +7,7 @@
private import ruby
private import codeql.ruby.ApiGraphs
private import codeql.ruby.frameworks.core.Gem::Gem as Gem
private import codeql.ruby.typetracking.TypeTracking
/**
* Module containing sources, sinks, and sanitizers for HTML constructed from library input.
@@ -37,21 +38,17 @@ module UnsafeHtmlConstruction {
/** Gets a node that eventually ends up in the XSS `sink`. */
private DataFlow::Node getANodeThatEndsInXssSink(ReflectedXss::Sink sink) {
result = getANodeThatEndsInXssSink(TypeTracker::TypeBackTracker::end(), sink)
result = getANodeThatEndsInXssSink(TypeBackTracker::end(), sink)
}
private import codeql.ruby.typetracking.TypeTracker as TypeTracker
/** Gets a node that is eventually ends up in the XSS `sink`, type-tracked with `t`. */
private DataFlow::LocalSourceNode getANodeThatEndsInXssSink(
TypeTracker::TypeBackTracker t, ReflectedXss::Sink sink
TypeBackTracker t, ReflectedXss::Sink sink
) {
t.start() and
result = sink.getALocalSource()
or
exists(TypeTracker::TypeBackTracker t2 |
result = getANodeThatEndsInXssSink(t2, sink).backtrack(t2, t)
)
exists(TypeBackTracker t2 | result = getANodeThatEndsInXssSink(t2, sink).backtrack(t2, t))
}
/**

View File

@@ -9,6 +9,7 @@ private import codeql.ruby.DataFlow
private import codeql.ruby.ApiGraphs
private import codeql.ruby.frameworks.core.Gem::Gem as Gem
private import codeql.ruby.Concepts as Concepts
import codeql.ruby.typetracking.TypeTracking
/**
* Module containing sources, sinks, and sanitizers for shell command constructed from library input.
@@ -44,20 +45,18 @@ module UnsafeShellCommandConstruction {
/** Holds if the string constructed at `source` is executed at `shellExec` */
predicate isUsedAsShellCommand(DataFlow::Node source, Concepts::SystemCommandExecution shellExec) {
source = backtrackShellExec(TypeTracker::TypeBackTracker::end(), shellExec)
source = backtrackShellExec(TypeBackTracker::end(), shellExec)
}
import codeql.ruby.typetracking.TypeTracker as TypeTracker
deprecated import codeql.ruby.typetracking.TypeTracker as TypeTracker
private DataFlow::LocalSourceNode backtrackShellExec(
TypeTracker::TypeBackTracker t, Concepts::SystemCommandExecution shellExec
TypeBackTracker t, Concepts::SystemCommandExecution shellExec
) {
t.start() and
result = any(DataFlow::Node n | shellExec.isShellInterpreted(n)).getALocalSource()
or
exists(TypeTracker::TypeBackTracker t2 |
result = backtrackShellExec(t2, shellExec).backtrack(t2, t)
)
exists(TypeBackTracker t2 | result = backtrackShellExec(t2, shellExec).backtrack(t2, t))
}
/**

View File

@@ -11,6 +11,7 @@ private import codeql.ruby.dataflow.RemoteFlowSources
private import codeql.ruby.dataflow.BarrierGuards
private import codeql.ruby.dataflow.Sanitizers
private import codeql.ruby.frameworks.ActionController
private import codeql.ruby.frameworks.data.internal.ApiGraphModels
/**
* Provides default sources, sinks and sanitizers for detecting
@@ -73,6 +74,10 @@ module UrlRedirect {
}
}
private class ExternalUrlRedirectSink extends Sink {
ExternalUrlRedirectSink() { this = ModelOutput::getASinkNode("url-redirection").asSink() }
}
/**
* A comparison with a constant string, considered as a sanitizer-guard.
*/

View File

@@ -5,8 +5,8 @@
*/
private import codeql.Locations
private import codeql.ruby.typetracking.TypeTracker
private import TypeTrackerSpecific
private import codeql.ruby.DataFlow
private import codeql.ruby.typetracking.internal.TypeTrackingImpl
/**
* The signature to use when instantiating `ApiGraphShared`.
@@ -37,14 +37,14 @@ signature module ApiGraphSharedSig {
*
* This node will have outgoing epsilon edges to its type-tracking successors.
*/
ApiNode getForwardNode(TypeTrackingNode node, TypeTracker t);
ApiNode getForwardNode(DataFlow::LocalSourceNode node, TypeTracker t);
/**
* Gets the backward node with the given type-tracking state.
*
* This node will have outgoing epsilon edges to its type-tracking predecessors.
*/
ApiNode getBackwardNode(TypeTrackingNode node, TypeTracker t);
ApiNode getBackwardNode(DataFlow::LocalSourceNode node, TypeTracker t);
/**
* Gets the sink node corresponding to `node`.
@@ -55,7 +55,7 @@ signature module ApiGraphSharedSig {
*
* Sink nodes have outgoing epsilon edges to the backward nodes corresponding to their local sources.
*/
ApiNode getSinkNode(Node node);
ApiNode getSinkNode(DataFlow::Node node);
/**
* Holds if a language-specific epsilon edge `pred -> succ` should be generated.
@@ -72,7 +72,9 @@ module ApiGraphShared<ApiGraphSharedSig S> {
/** Gets a local source of `node`. */
bindingset[node]
pragma[inline_late]
TypeTrackingNode getALocalSourceStrict(Node node) { result = node.getALocalSource() }
DataFlow::LocalSourceNode getALocalSourceStrict(DataFlow::Node node) {
result = node.getALocalSource()
}
cached
private module Cached {
@@ -85,23 +87,21 @@ module ApiGraphShared<ApiGraphSharedSig S> {
cached
predicate epsilonEdge(ApiNode pred, ApiNode succ) {
exists(
StepSummary summary, TypeTrackingNode predNode, TypeTracker predState,
TypeTrackingNode succNode, TypeTracker succState
StepSummary summary, DataFlow::LocalSourceNode predNode, TypeTracker predState,
DataFlow::LocalSourceNode succNode, TypeTracker succState
|
StepSummary::stepCall(predNode, succNode, summary)
or
StepSummary::stepNoCall(predNode, succNode, summary)
step(predNode, succNode, summary)
|
pred = getForwardNode(predNode, predState) and
succState = StepSummary::append(predState, summary) and
succState = append(predState, summary) and
succ = getForwardNode(succNode, succState)
or
succ = getBackwardNode(predNode, predState) and // swap order for backward flow
succState = StepSummary::append(predState, summary) and
succState = append(predState, summary) and
pred = getBackwardNode(succNode, succState) // swap order for backward flow
)
or
exists(Node sink, TypeTrackingNode localSource |
exists(DataFlow::Node sink, DataFlow::LocalSourceNode localSource |
pred = getSinkNode(sink) and
localSource = getALocalSourceStrict(sink) and
succ = getBackwardStartNode(localSource)
@@ -121,39 +121,39 @@ module ApiGraphShared<ApiGraphSharedSig S> {
/** Gets the API node to use when starting forward flow from `source` */
cached
ApiNode forwardStartNode(TypeTrackingNode source) {
result = getForwardNode(source, TypeTracker::end(false))
ApiNode forwardStartNode(DataFlow::LocalSourceNode source) {
result = getForwardNode(source, noContentTypeTracker(false))
}
/** Gets the API node to use when starting backward flow from `sink` */
cached
ApiNode backwardStartNode(TypeTrackingNode sink) {
ApiNode backwardStartNode(DataFlow::LocalSourceNode sink) {
// There is backward flow A->B iff there is forward flow B->A.
// The starting point of backward flow corresponds to the end of a forward flow, and vice versa.
result = getBackwardNode(sink, TypeTracker::end(_))
result = getBackwardNode(sink, noContentTypeTracker(_))
}
/** Gets `node` as a data flow source. */
cached
TypeTrackingNode asSourceCached(ApiNode node) { node = forwardEndNode(result) }
DataFlow::LocalSourceNode asSourceCached(ApiNode node) { node = forwardEndNode(result) }
/** Gets `node` as a data flow sink. */
cached
Node asSinkCached(ApiNode node) { node = getSinkNode(result) }
DataFlow::Node asSinkCached(ApiNode node) { node = getSinkNode(result) }
}
private import Cached
/** Gets an API node corresponding to the end of forward-tracking to `localSource`. */
pragma[nomagic]
private ApiNode forwardEndNode(TypeTrackingNode localSource) {
result = getForwardNode(localSource, TypeTracker::end(_))
private ApiNode forwardEndNode(DataFlow::LocalSourceNode localSource) {
result = getForwardNode(localSource, noContentTypeTracker(_))
}
/** Gets an API node corresponding to the end of backtracking to `localSource`. */
pragma[nomagic]
private ApiNode backwardEndNode(TypeTrackingNode localSource) {
result = getBackwardNode(localSource, TypeTracker::end(false))
private ApiNode backwardEndNode(DataFlow::LocalSourceNode localSource) {
result = getBackwardNode(localSource, noContentTypeTracker(false))
}
/** Gets a node reachable from `node` by zero or more epsilon edges, including `node` itself. */
@@ -164,18 +164,18 @@ module ApiGraphShared<ApiGraphSharedSig S> {
/** Gets `node` as a data flow sink. */
bindingset[node]
pragma[inline_late]
Node asSinkInline(ApiNode node) { result = asSinkCached(node) }
DataFlow::Node asSinkInline(ApiNode node) { result = asSinkCached(node) }
/** Gets `node` as a data flow source. */
bindingset[node]
pragma[inline_late]
TypeTrackingNode asSourceInline(ApiNode node) { result = asSourceCached(node) }
DataFlow::LocalSourceNode asSourceInline(ApiNode node) { result = asSourceCached(node) }
/** Gets a value reachable from `source`. */
bindingset[source]
pragma[inline_late]
Node getAValueReachableFromSourceInline(ApiNode source) {
exists(TypeTrackingNode src |
DataFlow::Node getAValueReachableFromSourceInline(ApiNode source) {
exists(DataFlow::LocalSourceNode src |
src = asSourceInline(getAnEpsilonSuccessorInline(source)) and
src.flowsTo(pragma[only_bind_into](result))
)
@@ -184,7 +184,7 @@ module ApiGraphShared<ApiGraphSharedSig S> {
/** Gets a value that can reach `sink`. */
bindingset[sink]
pragma[inline_late]
Node getAValueReachingSinkInline(ApiNode sink) {
DataFlow::Node getAValueReachingSinkInline(ApiNode sink) {
backwardStartNode(result) = getAnEpsilonSuccessorInline(sink)
}
@@ -195,7 +195,7 @@ module ApiGraphShared<ApiGraphSharedSig S> {
*/
bindingset[node]
pragma[inline_late]
ApiNode getForwardStartNode(Node node) { result = forwardStartNode(node) }
ApiNode getForwardStartNode(DataFlow::Node node) { result = forwardStartNode(node) }
/**
* Gets the starting point of backtracking from `node`.
@@ -204,7 +204,7 @@ module ApiGraphShared<ApiGraphSharedSig S> {
*/
bindingset[node]
pragma[inline_late]
ApiNode getBackwardStartNode(Node node) { result = backwardStartNode(node) }
ApiNode getBackwardStartNode(DataFlow::Node node) { result = backwardStartNode(node) }
/**
* Gets a possible ending point of forward-tracking at `node`.
@@ -216,7 +216,7 @@ module ApiGraphShared<ApiGraphSharedSig S> {
*/
bindingset[node]
pragma[inline_late]
ApiNode getForwardEndNode(Node node) { result = forwardEndNode(node) }
ApiNode getForwardEndNode(DataFlow::Node node) { result = forwardEndNode(node) }
/**
* Gets a possible ending point backtracking to `node`.
@@ -228,7 +228,7 @@ module ApiGraphShared<ApiGraphSharedSig S> {
*/
bindingset[node]
pragma[inline_late]
ApiNode getBackwardEndNode(Node node) { result = backwardEndNode(node) }
ApiNode getBackwardEndNode(DataFlow::Node node) { result = backwardEndNode(node) }
/**
* Gets a possible eding point of forward or backward tracking at `node`.
@@ -237,19 +237,19 @@ module ApiGraphShared<ApiGraphSharedSig S> {
*/
bindingset[node]
pragma[inline_late]
ApiNode getForwardOrBackwardEndNode(Node node) {
ApiNode getForwardOrBackwardEndNode(DataFlow::Node node) {
result = getForwardEndNode(node) or result = getBackwardEndNode(node)
}
/** Gets an API node for tracking forward starting at `node`. This is the implementation of `DataFlow::LocalSourceNode.track()` */
bindingset[node]
pragma[inline_late]
ApiNode getNodeForForwardTracking(Node node) { result = forwardStartNode(node) }
ApiNode getNodeForForwardTracking(DataFlow::Node node) { result = forwardStartNode(node) }
/** Gets an API node for backtracking starting at `node`. The implementation of `DataFlow::Node.backtrack()`. */
bindingset[node]
pragma[inline_late]
ApiNode getNodeForBacktracking(Node node) {
ApiNode getNodeForBacktracking(DataFlow::Node node) {
result = getBackwardStartNode(getALocalSourceStrict(node))
}

View File

@@ -1,6 +1,11 @@
/** Step Summaries and Type Tracking */
/**
* DEPRECATED: Use `codeql.ruby.typetracking.TypeTracking` instead.
*
* Step Summaries and Type Tracking
*/
private import TypeTrackerSpecific
private import codeql.util.Boolean
cached
private module Cached {
@@ -8,22 +13,22 @@ private module Cached {
* A description of a step on an inter-procedural data flow path.
*/
cached
newtype TStepSummary =
deprecated newtype TStepSummary =
LevelStep() or
CallStep() or
ReturnStep() or
StoreStep(TypeTrackerContent content) { basicStoreStep(_, _, content) } or
LoadStep(TypeTrackerContent content) { basicLoadStep(_, _, content) } or
LoadStoreStep(TypeTrackerContent load, TypeTrackerContent store) {
deprecated StoreStep(TypeTrackerContent content) { basicStoreStep(_, _, content) } or
deprecated LoadStep(TypeTrackerContent content) { basicLoadStep(_, _, content) } or
deprecated LoadStoreStep(TypeTrackerContent load, TypeTrackerContent store) {
basicLoadStoreStep(_, _, load, store)
} or
WithContent(ContentFilter filter) { basicWithContentStep(_, _, filter) } or
WithoutContent(ContentFilter filter) { basicWithoutContentStep(_, _, filter) } or
deprecated WithContent(ContentFilter filter) { basicWithContentStep(_, _, filter) } or
deprecated WithoutContent(ContentFilter filter) { basicWithoutContentStep(_, _, filter) } or
JumpStep()
cached
newtype TTypeTracker =
MkTypeTracker(Boolean hasCall, OptionalTypeTrackerContent content) {
deprecated newtype TTypeTracker =
deprecated MkTypeTracker(Boolean hasCall, OptionalTypeTrackerContent content) {
content = noContent()
or
// Restrict `content` to those that might eventually match a load.
@@ -40,8 +45,8 @@ private module Cached {
}
cached
newtype TTypeBackTracker =
MkTypeBackTracker(Boolean hasReturn, OptionalTypeTrackerContent content) {
deprecated newtype TTypeBackTracker =
deprecated MkTypeBackTracker(Boolean hasReturn, OptionalTypeTrackerContent content) {
content = noContent()
or
// As in MkTypeTracker, restrict `content` to those that might eventually match a store.
@@ -57,11 +62,13 @@ private module Cached {
/** Gets a type tracker with no content and the call bit set to the given value. */
cached
TypeTracker noContentTypeTracker(boolean hasCall) { result = MkTypeTracker(hasCall, noContent()) }
deprecated TypeTracker noContentTypeTracker(boolean hasCall) {
result = MkTypeTracker(hasCall, noContent())
}
/** Gets the summary resulting from appending `step` to type-tracking summary `tt`. */
cached
TypeTracker append(TypeTracker tt, StepSummary step) {
deprecated TypeTracker append(TypeTracker tt, StepSummary step) {
exists(Boolean hasCall, OptionalTypeTrackerContent currentContents |
tt = MkTypeTracker(hasCall, currentContents)
|
@@ -108,13 +115,13 @@ private module Cached {
}
pragma[nomagic]
private TypeBackTracker noContentTypeBackTracker(boolean hasReturn) {
deprecated private TypeBackTracker noContentTypeBackTracker(boolean hasReturn) {
result = MkTypeBackTracker(hasReturn, noContent())
}
/** Gets the summary resulting from prepending `step` to this type-tracking summary. */
cached
TypeBackTracker prepend(TypeBackTracker tbt, StepSummary step) {
deprecated TypeBackTracker prepend(TypeBackTracker tbt, StepSummary step) {
exists(Boolean hasReturn, OptionalTypeTrackerContent content |
tbt = MkTypeBackTracker(hasReturn, content)
|
@@ -167,7 +174,9 @@ private module Cached {
* Steps contained in this predicate should _not_ depend on the call graph.
*/
cached
predicate stepNoCall(TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) {
deprecated predicate stepNoCall(
TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo, StepSummary summary
) {
exists(Node mid | nodeFrom.flowsTo(mid) and smallstepNoCall(mid, nodeTo, summary))
}
@@ -176,12 +185,14 @@ private module Cached {
* inter-procedural step from `nodeFrom` to `nodeTo`.
*/
cached
predicate stepCall(TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) {
deprecated predicate stepCall(
TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo, StepSummary summary
) {
exists(Node mid | nodeFrom.flowsTo(mid) and smallstepCall(mid, nodeTo, summary))
}
cached
predicate smallstepNoCall(Node nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) {
deprecated predicate smallstepNoCall(Node nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) {
jumpStep(nodeFrom, nodeTo) and
summary = JumpStep()
or
@@ -210,7 +221,7 @@ private module Cached {
}
cached
predicate smallstepCall(Node nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) {
deprecated predicate smallstepCall(Node nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) {
callStep(nodeFrom, nodeTo) and summary = CallStep()
or
returnStep(nodeFrom, nodeTo) and
@@ -223,25 +234,27 @@ private module Cached {
private import Cached
private predicate step(TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) {
deprecated private predicate step(
TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo, StepSummary summary
) {
stepNoCall(nodeFrom, nodeTo, summary)
or
stepCall(nodeFrom, nodeTo, summary)
}
pragma[nomagic]
private predicate stepProj(TypeTrackingNode nodeFrom, StepSummary summary) {
deprecated private predicate stepProj(TypeTrackingNode nodeFrom, StepSummary summary) {
step(nodeFrom, _, summary)
}
private predicate smallstep(Node nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) {
deprecated private predicate smallstep(Node nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) {
smallstepNoCall(nodeFrom, nodeTo, summary)
or
smallstepCall(nodeFrom, nodeTo, summary)
}
pragma[nomagic]
private predicate smallstepProj(Node nodeFrom, StepSummary summary) {
deprecated private predicate smallstepProj(Node nodeFrom, StepSummary summary) {
smallstep(nodeFrom, _, summary)
}
@@ -270,7 +283,7 @@ private predicate smallstepProj(Node nodeFrom, StepSummary summary) {
* function. This means we will track the fact that `x.attr` can have the type of `y` into the
* assignment to `z` inside `bar`, even though this attribute write happens _after_ `bar` is called.
*/
private predicate flowsToStoreStep(
deprecated private predicate flowsToStoreStep(
Node nodeFrom, TypeTrackingNode nodeTo, TypeTrackerContent content
) {
exists(Node obj | nodeTo.flowsTo(obj) and basicStoreStep(nodeFrom, obj, content))
@@ -279,7 +292,7 @@ private predicate flowsToStoreStep(
/**
* Holds if `loadContent` is loaded from `nodeFrom` and written to `storeContent` of `nodeTo`.
*/
private predicate flowsToLoadStoreStep(
deprecated private predicate flowsToLoadStoreStep(
Node nodeFrom, TypeTrackingNode nodeTo, TypeTrackerContent loadContent,
TypeTrackerContent storeContent
) {
@@ -293,7 +306,7 @@ private predicate flowsToLoadStoreStep(
*
* A description of a step on an inter-procedural data flow path.
*/
class StepSummary extends TStepSummary {
deprecated class StepSummary extends TStepSummary {
/** Gets a textual representation of this step summary. */
string toString() {
this instanceof LevelStep and result = "level"
@@ -316,7 +329,7 @@ class StepSummary extends TStepSummary {
}
/** Provides predicates for updating step summaries (`StepSummary`s). */
module StepSummary {
deprecated module StepSummary {
predicate append = Cached::append/2;
/**
@@ -411,6 +424,8 @@ module StepSummary {
}
/**
* DEPRECATED: Use `codeql.ruby.typetracking.TypeTracking` instead.
*
* A summary of the steps needed to track a value to a given dataflow node.
*
* This can be used to track objects that implement a certain API in order to
@@ -437,7 +452,7 @@ module StepSummary {
* `t = t2.step(myType(t2), result)`. If you additionally want to track individual
* intra-procedural steps, use `t = t2.smallstep(myCallback(t2), result)`.
*/
class TypeTracker extends TTypeTracker {
deprecated class TypeTracker extends TTypeTracker {
Boolean hasCall;
OptionalTypeTrackerContent content;
@@ -565,7 +580,7 @@ class TypeTracker extends TTypeTracker {
}
/** Provides predicates for implementing custom `TypeTracker`s. */
module TypeTracker {
deprecated module TypeTracker {
/**
* Gets a valid end point of type tracking.
*/
@@ -580,15 +595,17 @@ module TypeTracker {
}
pragma[nomagic]
private predicate backStepProj(TypeTrackingNode nodeTo, StepSummary summary) {
deprecated private predicate backStepProj(TypeTrackingNode nodeTo, StepSummary summary) {
step(_, nodeTo, summary)
}
private predicate backSmallstepProj(TypeTrackingNode nodeTo, StepSummary summary) {
deprecated private predicate backSmallstepProj(TypeTrackingNode nodeTo, StepSummary summary) {
smallstep(_, nodeTo, summary)
}
/**
* DEPRECATED: Use `codeql.ruby.typetracking.TypeTracking` instead.
*
* A summary of the steps needed to back-track a use of a value to a given dataflow node.
*
* This can for example be used to track callbacks that are passed to a certain API,
@@ -618,7 +635,7 @@ private predicate backSmallstepProj(TypeTrackingNode nodeTo, StepSummary summary
* `t2 = t.step(result, myCallback(t2))`. If you additionally want to track individual
* intra-procedural steps, use `t2 = t.smallstep(result, myCallback(t2))`.
*/
class TypeBackTracker extends TTypeBackTracker {
deprecated class TypeBackTracker extends TTypeBackTracker {
Boolean hasReturn;
OptionalTypeTrackerContent content;
@@ -747,7 +764,7 @@ class TypeBackTracker extends TTypeBackTracker {
}
/** Provides predicates for implementing custom `TypeBackTracker`s. */
module TypeBackTracker {
deprecated module TypeBackTracker {
/**
* Gets a valid end point of type back-tracking.
*/
@@ -768,14 +785,14 @@ module TypeBackTracker {
* `stepCall` relation (`stepNoCall` not being recursive, can be join-ordered in the
* same way as in `stepInlineLate`).
*/
module CallGraphConstruction {
deprecated module CallGraphConstruction {
/** The input to call graph construction. */
signature module InputSig {
/** A state to track during type tracking. */
class State;
/** Holds if type tracking should start at `start` in state `state`. */
predicate start(Node start, State state);
deprecated predicate start(Node start, State state);
/**
* Holds if type tracking should use the step from `nodeFrom` to `nodeTo`,
@@ -784,7 +801,7 @@ module CallGraphConstruction {
* Implementing this predicate using `StepSummary::[small]stepNoCall` yields
* standard type tracking.
*/
predicate stepNoCall(Node nodeFrom, Node nodeTo, StepSummary summary);
deprecated predicate stepNoCall(Node nodeFrom, Node nodeTo, StepSummary summary);
/**
* Holds if type tracking should use the step from `nodeFrom` to `nodeTo`,
@@ -793,7 +810,7 @@ module CallGraphConstruction {
* Implementing this predicate using `StepSummary::[small]stepCall` yields
* standard type tracking.
*/
predicate stepCall(Node nodeFrom, Node nodeTo, StepSummary summary);
deprecated predicate stepCall(Node nodeFrom, Node nodeTo, StepSummary summary);
/** A projection of an element from the state space. */
class StateProj;
@@ -802,25 +819,25 @@ module CallGraphConstruction {
StateProj stateProj(State state);
/** Holds if type tracking should stop at `n` when we are tracking projected state `stateProj`. */
predicate filter(Node n, StateProj stateProj);
deprecated predicate filter(Node n, StateProj stateProj);
}
/** Provides the `track` predicate for use in call graph construction. */
module Make<InputSig Input> {
pragma[nomagic]
private predicate stepNoCallProj(Node nodeFrom, StepSummary summary) {
deprecated private predicate stepNoCallProj(Node nodeFrom, StepSummary summary) {
Input::stepNoCall(nodeFrom, _, summary)
}
pragma[nomagic]
private predicate stepCallProj(Node nodeFrom, StepSummary summary) {
deprecated private predicate stepCallProj(Node nodeFrom, StepSummary summary) {
Input::stepCall(nodeFrom, _, summary)
}
bindingset[nodeFrom, t]
pragma[inline_late]
pragma[noopt]
private TypeTracker stepNoCallInlineLate(
deprecated private TypeTracker stepNoCallInlineLate(
TypeTracker t, TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo
) {
exists(StepSummary summary |
@@ -837,7 +854,7 @@ module CallGraphConstruction {
}
pragma[nomagic]
private Node track(Input::State state, TypeTracker t) {
deprecated private Node track(Input::State state, TypeTracker t) {
t.start() and Input::start(result, state)
or
exists(Input::StateProj stateProj |
@@ -855,12 +872,12 @@ module CallGraphConstruction {
bindingset[t, summary]
pragma[inline_late]
private TypeTracker appendInlineLate(TypeTracker t, StepSummary summary) {
deprecated private TypeTracker appendInlineLate(TypeTracker t, StepSummary summary) {
result = t.append(summary)
}
pragma[nomagic]
private Node trackCall(Input::State state, TypeTracker t, StepSummary summary) {
deprecated private Node trackCall(Input::State state, TypeTracker t, StepSummary summary) {
exists(TypeTracker t2 |
// non-linear recursion
result = track(state, t2) and
@@ -871,7 +888,7 @@ module CallGraphConstruction {
/** Gets a node that can be reached from _some_ start node in state `state`. */
pragma[nomagic]
Node track(Input::State state) { result = track(state, TypeTracker::end()) }
deprecated Node track(Input::State state) { result = track(state, TypeTracker::end()) }
}
/** A simple version of `CallGraphConstruction` that uses standard type tracking. */
@@ -882,15 +899,15 @@ module CallGraphConstruction {
class State;
/** Holds if type tracking should start at `start` in state `state`. */
predicate start(Node start, State state);
deprecated predicate start(Node start, State state);
/** Holds if type tracking should stop at `n`. */
predicate filter(Node n);
deprecated predicate filter(Node n);
}
/** Provides the `track` predicate for use in call graph construction. */
module Make<InputSig Input> {
private module I implements CallGraphConstruction::InputSig {
deprecated private module I implements CallGraphConstruction::InputSig {
private import codeql.util.Unit
class State = Input::State;
@@ -915,7 +932,7 @@ module CallGraphConstruction {
}
}
import CallGraphConstruction::Make<I>
deprecated import CallGraphConstruction::Make<I>
}
}
}

View File

@@ -1,29 +1,18 @@
private import codeql.ruby.AST as Ast
private import codeql.ruby.CFG as Cfg
private import Cfg::CfgNodes
private import codeql.ruby.dataflow.FlowSummary
private import codeql.ruby.dataflow.internal.DataFlowImplCommon as DataFlowImplCommon
private import codeql.ruby.dataflow.internal.DataFlowPublic as DataFlowPublic
private import codeql.ruby.dataflow.internal.DataFlowPrivate as DataFlowPrivate
private import codeql.ruby.dataflow.internal.DataFlowDispatch as DataFlowDispatch
private import codeql.ruby.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
private import codeql.ruby.dataflow.internal.FlowSummaryImplSpecific as FlowSummaryImplSpecific
private import codeql.ruby.dataflow.internal.AccessPathSyntax
private import internal.TypeTrackingImpl as TypeTrackingImpl
deprecated import codeql.util.Boolean
class Node = DataFlowPublic::Node;
deprecated class Node = DataFlowPublic::Node;
class TypeTrackingNode = DataFlowPublic::LocalSourceNode;
deprecated class TypeTrackingNode = DataFlowPublic::LocalSourceNode;
class TypeTrackerContent = DataFlowPublic::ContentSet;
private module SCS = SummaryComponentStack;
private module SC = SummaryComponent;
deprecated class TypeTrackerContent = DataFlowPublic::ContentSet;
/**
* An optional content set, that is, a `ContentSet` or the special "no content set" value.
*/
class OptionalTypeTrackerContent extends DataFlowPrivate::TOptionalContentSet {
deprecated class OptionalTypeTrackerContent extends DataFlowPrivate::TOptionalContentSet {
/** Gets a textual representation of this content set. */
string toString() {
this instanceof DataFlowPrivate::TNoContentSet and
@@ -33,178 +22,45 @@ class OptionalTypeTrackerContent extends DataFlowPrivate::TOptionalContentSet {
}
}
private newtype TContentFilter = MkElementFilter()
/**
* A label to use for `WithContent` and `WithoutContent` steps, restricting
* which `ContentSet` may pass through.
*/
class ContentFilter extends TContentFilter {
/** Gets a string representation of this content filter. */
string toString() { this = MkElementFilter() and result = "elements" }
/** Gets the content of a type-tracker that matches this filter. */
TypeTrackerContent getAMatchingContent() {
this = MkElementFilter() and
result.getAReadContent() instanceof DataFlow::Content::ElementContent
}
}
deprecated class ContentFilter = TypeTrackingImpl::TypeTrackingInput::ContentFilter;
/** Module for getting `ContentFilter` values. */
module ContentFilter {
deprecated module ContentFilter {
/** Gets the filter that only allow element contents. */
ContentFilter hasElements() { result = MkElementFilter() }
ContentFilter hasElements() { any() }
}
/**
* Holds if a value stored with `storeContents` can be read back with `loadContents`.
*/
pragma[inline]
predicate compatibleContents(TypeTrackerContent storeContents, TypeTrackerContent loadContents) {
deprecated predicate compatibleContents(
TypeTrackerContent storeContents, TypeTrackerContent loadContents
) {
storeContents.getAStoreContent() = loadContents.getAReadContent()
}
/** Gets the "no content set" value to use for a type tracker not inside any content. */
OptionalTypeTrackerContent noContent() { result = DataFlowPrivate::TNoContentSet() }
deprecated OptionalTypeTrackerContent noContent() { result = DataFlowPrivate::TNoContentSet() }
/** Holds if there is a simple local flow step from `nodeFrom` to `nodeTo` */
predicate simpleLocalFlowStep = DataFlowPrivate::localFlowStepTypeTracker/2;
deprecated predicate simpleLocalFlowStep =
TypeTrackingImpl::TypeTrackingInput::simpleLocalSmallStep/2;
/**
* Holds if data can flow from `node1` to `node2` in a way that discards call contexts.
*/
predicate jumpStep = DataFlowPrivate::jumpStep/2;
/** Holds if there is direct flow from `param` to a return. */
pragma[nomagic]
private predicate flowThrough(DataFlowPublic::ParameterNode param) {
exists(DataFlowPrivate::SourceReturnNode returnNode, DataFlowDispatch::ReturnKind rk |
param.flowsTo(returnNode) and
returnNode.hasKind(rk, param.(DataFlowPrivate::NodeImpl).getCfgScope())
|
rk instanceof DataFlowDispatch::NormalReturnKind
or
rk instanceof DataFlowDispatch::BreakReturnKind
)
}
/** Holds if there is flow from `arg` to `p` via the call `call`, not counting `new -> initialize` call steps. */
pragma[nomagic]
predicate callStepNoInitialize(
ExprNodes::CallCfgNode call, Node arg, DataFlowPrivate::ParameterNodeImpl p
) {
exists(DataFlowDispatch::ParameterPosition pos |
argumentPositionMatch(call, arg, pos) and
p.isSourceParameterOf(DataFlowDispatch::getTarget(call), pos)
)
}
deprecated predicate jumpStep = TypeTrackingImpl::TypeTrackingInput::jumpStep/2;
/** Holds if there is a level step from `nodeFrom` to `nodeTo`, which may depend on the call graph. */
pragma[nomagic]
predicate levelStepCall(Node nodeFrom, Node nodeTo) {
exists(DataFlowPublic::ParameterNode param |
flowThrough(param) and
callStepNoInitialize(nodeTo.asExpr(), nodeFrom, param)
)
}
deprecated predicate levelStepCall = TypeTrackingImpl::TypeTrackingInput::levelStepCall/2;
/** Holds if there is a level step from `nodeFrom` to `nodeTo`, which does not depend on the call graph. */
pragma[nomagic]
predicate levelStepNoCall(Node nodeFrom, Node nodeTo) {
TypeTrackerSummaryFlow::levelStepNoCall(nodeFrom, nodeTo)
or
localFieldStep(nodeFrom, nodeTo)
}
/**
* Gets a method of `mod`, with `instance` indicating if this is an instance method.
*
* Does not take inheritance or the various forms of inclusion into account.
*/
pragma[nomagic]
private MethodBase getAMethod(ModuleBase mod, boolean instance) {
not mod instanceof SingletonClass and
result = mod.getAMethod() and
if result instanceof SingletonMethod then instance = false else instance = true
or
exists(SingletonClass cls |
cls.getValue().(SelfVariableAccess).getVariable().getDeclaringScope() = mod and
result = cls.getAMethod().(Method) and
instance = false
)
}
/**
* Gets a value flowing into `field` in `mod`, with `instance` indicating if it's
* a field on an instance of `mod` (as opposed to the module object itself).
*/
pragma[nomagic]
private Node fieldPredecessor(ModuleBase mod, boolean instance, string field) {
exists(InstanceVariableWriteAccess access, AssignExpr assign |
access.getReceiver().getVariable().getDeclaringScope() = getAMethod(mod, instance) and
field = access.getVariable().getName() and
assign.getLeftOperand() = access and
result.asExpr().getExpr() = assign.getRightOperand()
)
}
/**
* Gets a reference to `field` in `mod`, with `instance` indicating if it's
* a field on an instance of `mod` (as opposed to the module object itself).
*/
pragma[nomagic]
private Node fieldSuccessor(ModuleBase mod, boolean instance, string field) {
exists(InstanceVariableReadAccess access |
access.getReceiver().getVariable().getDeclaringScope() = getAMethod(mod, instance) and
result.asExpr().getExpr() = access and
field = access.getVariable().getName()
)
}
/**
* Holds if `pred -> succ` should be used a level step, from a field assignment to
* a read within the same class.
*/
private predicate localFieldStep(Node pred, Node succ) {
exists(ModuleBase mod, boolean instance, string field |
pred = fieldPredecessor(mod, instance, field) and
succ = fieldSuccessor(mod, instance, field)
)
}
pragma[noinline]
private predicate argumentPositionMatch(
ExprNodes::CallCfgNode call, DataFlowPrivate::ArgumentNode arg,
DataFlowDispatch::ParameterPosition ppos
) {
exists(DataFlowDispatch::ArgumentPosition apos |
arg.sourceArgumentOf(call, apos) and
not apos.isLambdaSelf() and
DataFlowDispatch::parameterMatch(ppos, apos)
)
}
pragma[noinline]
private predicate viableParam(
ExprNodes::CallCfgNode call, DataFlowPrivate::ParameterNodeImpl p,
DataFlowDispatch::ParameterPosition ppos
) {
exists(Cfg::CfgScope callable |
DataFlowDispatch::getTarget(call) = callable or
DataFlowDispatch::getInitializeTarget(call) = callable
|
p.isSourceParameterOf(callable, ppos)
)
}
/** Holds if there is flow from `arg` to `p` via the call `call`. */
pragma[nomagic]
predicate callStep(ExprNodes::CallCfgNode call, Node arg, DataFlowPrivate::ParameterNodeImpl p) {
exists(DataFlowDispatch::ParameterPosition pos |
argumentPositionMatch(call, arg, pos) and
viableParam(call, p, pos)
)
}
deprecated predicate levelStepNoCall = TypeTrackingImpl::TypeTrackingInput::levelStepNoCall/2;
/**
* Holds if `nodeFrom` steps to `nodeTo` by being passed as a parameter in a call.
@@ -213,7 +69,7 @@ predicate callStep(ExprNodes::CallCfgNode call, Node arg, DataFlowPrivate::Param
* recursion (or, at best, terrible performance), since identifying calls to library
* methods is done using API graphs (which uses type tracking).
*/
predicate callStep(Node nodeFrom, Node nodeTo) { callStep(_, nodeFrom, nodeTo) }
deprecated predicate callStep = TypeTrackingImpl::TypeTrackingInput::callStep/2;
/**
* Holds if `nodeFrom` steps to `nodeTo` by being returned from a call.
@@ -222,17 +78,7 @@ predicate callStep(Node nodeFrom, Node nodeTo) { callStep(_, nodeFrom, nodeTo) }
* recursion (or, at best, terrible performance), since identifying calls to library
* methods is done using API graphs (which uses type tracking).
*/
predicate returnStep(Node nodeFrom, Node nodeTo) {
exists(ExprNodes::CallCfgNode call |
nodeFrom instanceof DataFlowPrivate::ReturnNode and
not nodeFrom instanceof DataFlowPrivate::InitializeReturnNode and
nodeFrom.(DataFlowPrivate::NodeImpl).getCfgScope() = DataFlowDispatch::getTarget(call) and
// deliberately do not include `getInitializeTarget`, since calls to `new` should not
// get the return value from `initialize`. Any fields being set in the initializer
// will reach all reads via `callStep` and `localFieldStep`.
nodeTo.asExpr().getAstNode() = call.getAstNode()
)
}
deprecated predicate returnStep = TypeTrackingImpl::TypeTrackingInput::returnStep/2;
/**
* Holds if `nodeFrom` is being written to the `contents` of the object
@@ -265,211 +111,25 @@ predicate returnStep(Node nodeFrom, Node nodeTo) {
* to `z` inside `bar`, even though this content write happens _after_ `bar` is
* called.
*/
predicate basicStoreStep(Node nodeFrom, Node nodeTo, DataFlow::ContentSet contents) {
storeStepIntoSourceNode(nodeFrom, nodeTo, contents)
or
TypeTrackerSummaryFlow::basicStoreStep(nodeFrom, nodeTo, contents)
}
/**
* Holds if a store step `nodeFrom -> nodeTo` with `contents` exists, where the destination node
* is a post-update node that should be treated as a local source node.
*/
private predicate storeStepIntoSourceNode(Node nodeFrom, Node nodeTo, DataFlow::ContentSet contents) {
// TODO: support SetterMethodCall inside TuplePattern
exists(ExprNodes::MethodCallCfgNode call |
contents
.isSingleton(DataFlowPublic::Content::getAttributeName(call.getExpr()
.(Ast::SetterMethodCall)
.getTargetName())) and
nodeTo.(DataFlowPublic::PostUpdateNode).getPreUpdateNode().asExpr() = call.getReceiver() and
call.getExpr() instanceof Ast::SetterMethodCall and
call.getArgument(call.getNumberOfArguments() - 1) =
nodeFrom.(DataFlowPublic::ExprNode).getExprNode()
)
or
DataFlowPrivate::storeStepCommon(nodeFrom, contents, nodeTo)
}
deprecated predicate basicStoreStep = TypeTrackingImpl::TypeTrackingInput::storeStep/3;
/**
* Holds if `nodeTo` is the result of accessing the `content` content of `nodeFrom`.
*/
predicate basicLoadStep(Node nodeFrom, Node nodeTo, DataFlow::ContentSet contents) {
readStepIntoSourceNode(nodeFrom, nodeTo, contents)
or
exists(ExprNodes::MethodCallCfgNode call |
call.getExpr().getNumberOfArguments() = 0 and
contents.isSingleton(DataFlowPublic::Content::getAttributeName(call.getExpr().getMethodName())) and
nodeFrom.asExpr() = call.getReceiver() and
nodeTo.asExpr() = call
)
or
TypeTrackerSummaryFlow::basicLoadStep(nodeFrom, nodeTo, contents)
}
/**
* Holds if a read step `nodeFrom -> nodeTo` with `contents` exists, where the destination node
* should be treated as a local source node.
*/
private predicate readStepIntoSourceNode(Node nodeFrom, Node nodeTo, DataFlow::ContentSet contents) {
DataFlowPrivate::readStepCommon(nodeFrom, contents, nodeTo)
}
deprecated predicate basicLoadStep = TypeTrackingImpl::TypeTrackingInput::loadStep/3;
/**
* Holds if the `loadContent` of `nodeFrom` is stored in the `storeContent` of `nodeTo`.
*/
predicate basicLoadStoreStep(
Node nodeFrom, Node nodeTo, DataFlow::ContentSet loadContent, DataFlow::ContentSet storeContent
) {
readStoreStepIntoSourceNode(nodeFrom, nodeTo, loadContent, storeContent)
or
TypeTrackerSummaryFlow::basicLoadStoreStep(nodeFrom, nodeTo, loadContent, storeContent)
}
/**
* Holds if a read+store step `nodeFrom -> nodeTo` exists, where the destination node
* should be treated as a local source node.
*/
private predicate readStoreStepIntoSourceNode(
Node nodeFrom, Node nodeTo, DataFlow::ContentSet loadContent, DataFlow::ContentSet storeContent
) {
exists(DataFlowPrivate::SynthSplatParameterShiftNode shift |
shift.readFrom(nodeFrom, loadContent) and
shift.storeInto(nodeTo, storeContent)
)
or
exists(DataFlowPrivate::SynthSplatArgumentShiftNode shift |
shift.readFrom(nodeFrom, loadContent) and
shift.storeInto(nodeTo, storeContent)
)
}
deprecated predicate basicLoadStoreStep = TypeTrackingImpl::TypeTrackingInput::loadStoreStep/4;
/**
* Holds if type-tracking should step from `nodeFrom` to `nodeTo` but block flow of contents matched by `filter` through here.
*/
predicate basicWithoutContentStep(Node nodeFrom, Node nodeTo, ContentFilter filter) {
TypeTrackerSummaryFlow::basicWithoutContentStep(nodeFrom, nodeTo, filter)
}
deprecated predicate basicWithoutContentStep =
TypeTrackingImpl::TypeTrackingInput::withoutContentStep/3;
/**
* Holds if type-tracking should step from `nodeFrom` to `nodeTo` if inside a content matched by `filter`.
*/
predicate basicWithContentStep(Node nodeFrom, Node nodeTo, ContentFilter filter) {
TypeTrackerSummaryFlow::basicWithContentStep(nodeFrom, nodeTo, filter)
}
/**
* A utility class that is equivalent to `boolean` but does not require type joining.
*/
class Boolean extends boolean {
Boolean() { this = true or this = false }
}
private import SummaryComponentStack
/**
* Holds if the given component can't be evaluated by `evaluateSummaryComponentStackLocal`.
*/
pragma[nomagic]
predicate isNonLocal(SummaryComponent component) {
component = SC::content(_)
or
component = SC::withContent(_)
}
private import internal.SummaryTypeTracker as SummaryTypeTracker
private import codeql.ruby.dataflow.FlowSummary as FlowSummary
private module SummaryTypeTrackerInput implements SummaryTypeTracker::Input {
// Dataflow nodes
class Node = DataFlow::Node;
// Content
class TypeTrackerContent = DataFlowPublic::ContentSet;
class TypeTrackerContentFilter = ContentFilter;
TypeTrackerContentFilter getFilterFromWithoutContentStep(TypeTrackerContent content) {
(
content.isAnyElement()
or
content.isElementLowerBoundOrUnknown(_)
or
content.isElementOfTypeOrUnknown(_)
or
content.isSingleton(any(DataFlow::Content::UnknownElementContent c))
) and
result = MkElementFilter()
}
TypeTrackerContentFilter getFilterFromWithContentStep(TypeTrackerContent content) {
(
content.isAnyElement()
or
content.isElementLowerBound(_)
or
content.isElementLowerBoundOrUnknown(_)
or
content.isElementOfType(_)
or
content.isElementOfTypeOrUnknown(_)
or
content.isSingleton(any(DataFlow::Content::ElementContent c))
) and
result = MkElementFilter()
}
// Summaries and their stacks
class SummaryComponent = FlowSummary::SummaryComponent;
class SummaryComponentStack = FlowSummary::SummaryComponentStack;
predicate singleton = FlowSummary::SummaryComponentStack::singleton/1;
predicate push = FlowSummary::SummaryComponentStack::push/2;
// Relating content to summaries
predicate content = FlowSummary::SummaryComponent::content/1;
predicate withoutContent = FlowSummary::SummaryComponent::withoutContent/1;
predicate withContent = FlowSummary::SummaryComponent::withContent/1;
predicate return = FlowSummary::SummaryComponent::return/0;
// Callables
class SummarizedCallable = FlowSummary::SummarizedCallable;
// Relating nodes to summaries
Node argumentOf(Node call, SummaryComponent arg, boolean isPostUpdate) {
exists(DataFlowDispatch::ParameterPosition pos, DataFlowPrivate::ArgumentNode n |
arg = SummaryComponent::argument(pos) and
argumentPositionMatch(call.asExpr(), n, pos)
|
isPostUpdate = false and result = n
or
isPostUpdate = true and result.(DataFlowPublic::PostUpdateNode).getPreUpdateNode() = n
)
}
Node parameterOf(Node callable, SummaryComponent param) {
exists(DataFlowDispatch::ArgumentPosition apos, DataFlowDispatch::ParameterPosition ppos |
param = SummaryComponent::parameter(apos) and
DataFlowDispatch::parameterMatch(ppos, apos) and
result
.(DataFlowPrivate::ParameterNodeImpl)
.isSourceParameterOf(callable.asExpr().getExpr(), ppos)
)
}
Node returnOf(Node callable, SummaryComponent return) {
return = SummaryComponent::return() and
result.(DataFlowPrivate::ReturnNode).(DataFlowPrivate::NodeImpl).getCfgScope() =
callable.asExpr().getExpr()
}
// Relating callables to nodes
Node callTo(SummarizedCallable callable) { result.asExpr().getExpr() = callable.getACallSimple() }
}
private module TypeTrackerSummaryFlow = SummaryTypeTracker::SummaryFlow<SummaryTypeTrackerInput>;
deprecated predicate basicWithContentStep = TypeTrackingImpl::TypeTrackingInput::withContentStep/3;

View File

@@ -0,0 +1,7 @@
/**
* Provides classes and predicates for simple data-flow reachability suitable
* for tracking types.
*/
private import codeql.ruby.typetracking.internal.TypeTrackingImpl as Impl
import Impl::Shared::TypeTracking<Impl::TypeTrackingInput>

View File

@@ -1,412 +0,0 @@
/**
* Provides the implementation of type tracking steps through flow summaries.
* To use this, you must implement the `Input` signature. You can then use the predicates in the `Output`
* signature to implement the predicates of the same names inside `TypeTrackerSpecific.qll`.
*/
/** The classes and predicates needed to generate type-tracking steps from summaries. */
signature module Input {
// Dataflow nodes
class Node;
// Content
class TypeTrackerContent;
class TypeTrackerContentFilter;
// Relating content and filters
/**
* Gets a content filter to use for a `WithoutContent[content]` step, (data is not allowed to be stored in `content`)
* or has no result if
* the step should be treated as ordinary flow.
*
* `WithoutContent` is often used to perform strong updates on individual collection elements, but for
* type-tracking this is rarely beneficial and quite expensive. However, `WithoutContent` can be quite useful
* for restricting the type of an object, and in these cases we translate it to a filter.
*/
TypeTrackerContentFilter getFilterFromWithoutContentStep(TypeTrackerContent content);
/**
* Gets a content filter to use for a `WithContent[content]` step, (data must be stored in `content`)
* or has no result if
* the step cannot be handled by type-tracking.
*
* `WithContent` is often used to perform strong updates on individual collection elements (or rather
* to preserve those that didn't get updated). But for type-tracking this is rarely beneficial and quite expensive.
* However, `WithContent` can be quite useful for restricting the type of an object, and in these cases we translate it to a filter.
*/
TypeTrackerContentFilter getFilterFromWithContentStep(TypeTrackerContent content);
// Summaries and their stacks
class SummaryComponent;
class SummaryComponentStack {
SummaryComponent head();
}
/** Gets a singleton stack containing `component`. */
SummaryComponentStack singleton(SummaryComponent component);
/**
* Gets the stack obtained by pushing `head` onto `tail`.
*/
SummaryComponentStack push(SummaryComponent head, SummaryComponentStack tail);
/** Gets a singleton stack representing a return. */
SummaryComponent return();
// Relating content to summaries
/** Gets a summary component for content `c`. */
SummaryComponent content(TypeTrackerContent contents);
/** Gets a summary component where data is not allowed to be stored in `contents`. */
SummaryComponent withoutContent(TypeTrackerContent contents);
/** Gets a summary component where data must be stored in `contents`. */
SummaryComponent withContent(TypeTrackerContent contents);
// Callables
class SummarizedCallable {
predicate propagatesFlow(
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
);
}
// Relating nodes to summaries
/**
* Gets a dataflow node respresenting the argument of `call` indicated by `arg`.
*
* Returns the post-update node of the argument when `isPostUpdate` is true.
*/
Node argumentOf(Node call, SummaryComponent arg, boolean isPostUpdate);
/** Gets a dataflow node respresenting the parameter of `callable` indicated by `param`. */
Node parameterOf(Node callable, SummaryComponent param);
/** Gets a dataflow node respresenting the return of `callable` indicated by `return`. */
Node returnOf(Node callable, SummaryComponent return);
// Relating callables to nodes
/** Gets a dataflow node respresenting a call to `callable`. */
Node callTo(SummarizedCallable callable);
}
/**
* The predicates provided by a summary type tracker.
* These are meant to be used in `TypeTrackerSpecific.qll`
* inside the predicates of the same names.
*/
signature module Output<Input I> {
/**
* Holds if there is a level step from `nodeFrom` to `nodeTo`, which does not depend on the call graph.
*/
predicate levelStepNoCall(I::Node nodeFrom, I::Node nodeTo);
/**
* Holds if `nodeTo` is the result of accessing the `content` content of `nodeFrom`.
*/
predicate basicLoadStep(I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent content);
/**
* Holds if `nodeFrom` is being written to the `content` content of the object in `nodeTo`.
*/
predicate basicStoreStep(I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent content);
/**
* Holds if the `loadContent` of `nodeFrom` is stored in the `storeContent` of `nodeTo`.
*/
predicate basicLoadStoreStep(
I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent loadContent,
I::TypeTrackerContent storeContent
);
/**
* Holds if type-tracking should step from `nodeFrom` to `nodeTo` but block flow of contents matched by `filter` through here.
*/
predicate basicWithoutContentStep(
I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContentFilter filter
);
/**
* Holds if type-tracking should step from `nodeFrom` to `nodeTo` if inside a content matched by `filter`.
*/
predicate basicWithContentStep(
I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContentFilter filter
);
}
/**
* Implementation of the summary type tracker, that is type tracking through flow summaries.
*/
module SummaryFlow<Input I> implements Output<I> {
pragma[nomagic]
private predicate isNonLocal(I::SummaryComponent component) {
component = I::content(_)
or
component = I::withContent(_)
}
pragma[nomagic]
private predicate hasLoadSummary(
I::SummarizedCallable callable, I::TypeTrackerContent contents, I::SummaryComponentStack input,
I::SummaryComponentStack output
) {
callable.propagatesFlow(I::push(I::content(contents), input), output, true) and
not isNonLocal(input.head()) and
not isNonLocal(output.head())
}
pragma[nomagic]
private predicate hasStoreSummary(
I::SummarizedCallable callable, I::TypeTrackerContent contents, I::SummaryComponentStack input,
I::SummaryComponentStack output
) {
not isNonLocal(input.head()) and
not isNonLocal(output.head()) and
(
callable.propagatesFlow(input, I::push(I::content(contents), output), true)
or
// Allow the input to start with an arbitrary WithoutContent[X].
// Since type-tracking only tracks one content deep, and we're about to store into another content,
// we're already preventing the input from being in a content.
callable
.propagatesFlow(I::push(I::withoutContent(_), input),
I::push(I::content(contents), output), true)
)
}
pragma[nomagic]
private predicate hasLoadStoreSummary(
I::SummarizedCallable callable, I::TypeTrackerContent loadContents,
I::TypeTrackerContent storeContents, I::SummaryComponentStack input,
I::SummaryComponentStack output
) {
callable
.propagatesFlow(I::push(I::content(loadContents), input),
I::push(I::content(storeContents), output), true) and
not isNonLocal(input.head()) and
not isNonLocal(output.head())
}
pragma[nomagic]
private predicate hasWithoutContentSummary(
I::SummarizedCallable callable, I::TypeTrackerContentFilter filter,
I::SummaryComponentStack input, I::SummaryComponentStack output
) {
exists(I::TypeTrackerContent content |
callable.propagatesFlow(I::push(I::withoutContent(content), input), output, true) and
filter = I::getFilterFromWithoutContentStep(content) and
not isNonLocal(input.head()) and
not isNonLocal(output.head()) and
input != output
)
}
pragma[nomagic]
private predicate hasWithContentSummary(
I::SummarizedCallable callable, I::TypeTrackerContentFilter filter,
I::SummaryComponentStack input, I::SummaryComponentStack output
) {
exists(I::TypeTrackerContent content |
callable.propagatesFlow(I::push(I::withContent(content), input), output, true) and
filter = I::getFilterFromWithContentStep(content) and
not isNonLocal(input.head()) and
not isNonLocal(output.head()) and
input != output
)
}
private predicate componentLevelStep(I::SummaryComponent component) {
exists(I::TypeTrackerContent content |
component = I::withoutContent(content) and
not exists(I::getFilterFromWithoutContentStep(content))
)
}
/**
* Gets a data flow `I::Node` corresponding an argument or return value of `call`,
* as specified by `component`. `isOutput` indicates whether the node represents
* an output node or an input node.
*/
bindingset[call, component]
private I::Node evaluateSummaryComponentLocal(
I::Node call, I::SummaryComponent component, boolean isOutput
) {
result = I::argumentOf(call, component, isOutput)
or
component = I::return() and
result = call and
isOutput = true
}
/**
* Holds if `callable` is relevant for type-tracking and we therefore want `stack` to
* be evaluated locally at its call sites.
*/
pragma[nomagic]
private predicate dependsOnSummaryComponentStack(
I::SummarizedCallable callable, I::SummaryComponentStack stack
) {
exists(I::callTo(callable)) and
(
callable.propagatesFlow(stack, _, true)
or
callable.propagatesFlow(_, stack, true)
or
// include store summaries as they may skip an initial step at the input
hasStoreSummary(callable, _, stack, _)
)
or
dependsOnSummaryComponentStackCons(callable, _, stack)
}
pragma[nomagic]
private predicate dependsOnSummaryComponentStackCons(
I::SummarizedCallable callable, I::SummaryComponent head, I::SummaryComponentStack tail
) {
dependsOnSummaryComponentStack(callable, I::push(head, tail))
}
pragma[nomagic]
private predicate dependsOnSummaryComponentStackConsLocal(
I::SummarizedCallable callable, I::SummaryComponent head, I::SummaryComponentStack tail
) {
dependsOnSummaryComponentStackCons(callable, head, tail) and
not isNonLocal(head)
}
pragma[nomagic]
private predicate dependsOnSummaryComponentStackLeaf(
I::SummarizedCallable callable, I::SummaryComponent leaf
) {
dependsOnSummaryComponentStack(callable, I::singleton(leaf))
}
/**
* Gets a data flow I::Node corresponding to the local input or output of `call`
* identified by `stack`, if possible.
*/
pragma[nomagic]
private I::Node evaluateSummaryComponentStackLocal(
I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack stack, boolean isOutput
) {
exists(I::SummaryComponent component |
dependsOnSummaryComponentStackLeaf(callable, component) and
stack = I::singleton(component) and
call = I::callTo(callable) and
result = evaluateSummaryComponentLocal(call, component, isOutput)
)
or
exists(
I::Node prev, I::SummaryComponent head, I::SummaryComponentStack tail, boolean isOutput0
|
prev = evaluateSummaryComponentStackLocal(callable, call, tail, isOutput0) and
dependsOnSummaryComponentStackConsLocal(callable, pragma[only_bind_into](head),
pragma[only_bind_out](tail)) and
stack = I::push(pragma[only_bind_out](head), pragma[only_bind_out](tail))
|
// `Parameter[X]` is only allowed in the output of flow summaries (hence `isOutput = true`),
// however the target of the parameter (e.g. `Argument[Y].Parameter[X]`) should be fetched
// not from a post-update argument node (hence `isOutput0 = false`)
result = I::parameterOf(prev, head) and
isOutput0 = false and
isOutput = true
or
// `ReturnValue` is only allowed in the input of flow summaries (hence `isOutput = false`),
// and the target of the return value (e.g. `Argument[X].ReturnValue`) should be fetched not
// from a post-update argument node (hence `isOutput0 = false`)
result = I::returnOf(prev, head) and
isOutput0 = false and
isOutput = false
or
componentLevelStep(head) and
result = prev and
isOutput = isOutput0
)
}
// Implement Output
predicate levelStepNoCall(I::Node nodeFrom, I::Node nodeTo) {
exists(
I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input,
I::SummaryComponentStack output
|
callable.propagatesFlow(input, output, true) and
call = I::callTo(callable) and
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input, false) and
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output, true)
)
}
predicate basicLoadStep(I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent content) {
exists(
I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input,
I::SummaryComponentStack output
|
hasLoadSummary(callable, content, pragma[only_bind_into](input),
pragma[only_bind_into](output)) and
call = I::callTo(callable) and
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input, false) and
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output, true)
)
}
predicate basicStoreStep(I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent content) {
exists(
I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input,
I::SummaryComponentStack output
|
hasStoreSummary(callable, content, pragma[only_bind_into](input),
pragma[only_bind_into](output)) and
call = I::callTo(callable) and
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input, false) and
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output, true)
)
}
predicate basicLoadStoreStep(
I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent loadContent,
I::TypeTrackerContent storeContent
) {
exists(
I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input,
I::SummaryComponentStack output
|
hasLoadStoreSummary(callable, loadContent, storeContent, pragma[only_bind_into](input),
pragma[only_bind_into](output)) and
call = I::callTo(callable) and
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input, false) and
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output, true)
)
}
predicate basicWithoutContentStep(
I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContentFilter filter
) {
exists(
I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input,
I::SummaryComponentStack output
|
hasWithoutContentSummary(callable, filter, pragma[only_bind_into](input),
pragma[only_bind_into](output)) and
call = I::callTo(callable) and
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input, false) and
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output, true)
)
}
predicate basicWithContentStep(
I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContentFilter filter
) {
exists(
I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input,
I::SummaryComponentStack output
|
hasWithContentSummary(callable, filter, pragma[only_bind_into](input),
pragma[only_bind_into](output)) and
call = I::callTo(callable) and
nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input, false) and
nodeTo = evaluateSummaryComponentStackLocal(callable, call, output, true)
)
}
}

View File

@@ -0,0 +1,430 @@
import codeql.typetracking.TypeTracking as Shared
import codeql.typetracking.internal.TypeTrackingImpl as SharedImpl
private import codeql.ruby.AST
private import codeql.ruby.CFG as Cfg
private import Cfg::CfgNodes
private import codeql.typetracking.internal.SummaryTypeTracker as SummaryTypeTracker
private import codeql.ruby.DataFlow
private import codeql.ruby.dataflow.FlowSummary as FlowSummary
private import codeql.ruby.dataflow.internal.DataFlowImplCommon as DataFlowImplCommon
private import codeql.ruby.dataflow.internal.DataFlowPublic as DataFlowPublic
private import codeql.ruby.dataflow.internal.DataFlowPrivate as DataFlowPrivate
private import codeql.ruby.dataflow.internal.DataFlowDispatch as DataFlowDispatch
private import codeql.ruby.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
/** Holds if there is direct flow from `param` to a return. */
pragma[nomagic]
private predicate flowThrough(DataFlowPublic::ParameterNode param) {
exists(DataFlowPrivate::SourceReturnNode returnNode, DataFlowDispatch::ReturnKind rk |
param.flowsTo(returnNode) and
returnNode.hasKind(rk, param.(DataFlowPrivate::NodeImpl).getCfgScope())
|
rk instanceof DataFlowDispatch::NormalReturnKind
or
rk instanceof DataFlowDispatch::BreakReturnKind
)
}
/** Holds if there is flow from `arg` to `p` via the call `call`, not counting `new -> initialize` call steps. */
pragma[nomagic]
private predicate callStepNoInitialize(
ExprNodes::CallCfgNode call, DataFlow::Node arg, DataFlowPrivate::ParameterNodeImpl p
) {
exists(DataFlowDispatch::ParameterPosition pos |
argumentPositionMatch(call, arg, pos) and
p.isSourceParameterOf(DataFlowDispatch::getTarget(call), pos)
)
}
/**
* Gets a method of `mod`, with `instance` indicating if this is an instance method.
*
* Does not take inheritance or the various forms of inclusion into account.
*/
pragma[nomagic]
private MethodBase getAMethod(ModuleBase mod, boolean instance) {
not mod instanceof SingletonClass and
result = mod.getAMethod() and
if result instanceof SingletonMethod then instance = false else instance = true
or
exists(SingletonClass cls |
cls.getValue().(SelfVariableAccess).getVariable().getDeclaringScope() = mod and
result = cls.getAMethod().(Method) and
instance = false
)
}
/**
* Gets a value flowing into `field` in `mod`, with `instance` indicating if it's
* a field on an instance of `mod` (as opposed to the module object itself).
*/
pragma[nomagic]
private DataFlow::Node fieldPredecessor(ModuleBase mod, boolean instance, string field) {
exists(InstanceVariableWriteAccess access, AssignExpr assign |
access.getReceiver().getVariable().getDeclaringScope() = getAMethod(mod, instance) and
field = access.getVariable().getName() and
assign.getLeftOperand() = access and
result.asExpr().getExpr() = assign.getRightOperand()
)
}
/**
* Gets a reference to `field` in `mod`, with `instance` indicating if it's
* a field on an instance of `mod` (as opposed to the module object itself).
*/
pragma[nomagic]
private DataFlow::Node fieldSuccessor(ModuleBase mod, boolean instance, string field) {
exists(InstanceVariableReadAccess access |
access.getReceiver().getVariable().getDeclaringScope() = getAMethod(mod, instance) and
result.asExpr().getExpr() = access and
field = access.getVariable().getName()
)
}
/**
* Holds if `pred -> succ` should be used a level step, from a field assignment to
* a read within the same class.
*/
private predicate localFieldStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(ModuleBase mod, boolean instance, string field |
pred = fieldPredecessor(mod, instance, field) and
succ = fieldSuccessor(mod, instance, field)
)
}
pragma[noinline]
private predicate argumentPositionMatch(
ExprNodes::CallCfgNode call, DataFlowPrivate::ArgumentNode arg,
DataFlowDispatch::ParameterPosition ppos
) {
exists(DataFlowDispatch::ArgumentPosition apos |
arg.sourceArgumentOf(call, apos) and
not apos.isLambdaSelf() and
DataFlowDispatch::parameterMatch(ppos, apos)
)
}
pragma[noinline]
private predicate viableParam(
ExprNodes::CallCfgNode call, DataFlowPrivate::ParameterNodeImpl p,
DataFlowDispatch::ParameterPosition ppos
) {
exists(Cfg::CfgScope callable |
DataFlowDispatch::getTarget(call) = callable or
DataFlowDispatch::getInitializeTarget(call) = callable
|
p.isSourceParameterOf(callable, ppos)
)
}
/** Holds if there is flow from `arg` to `p` via the call `call`. */
pragma[nomagic]
predicate callStep(
ExprNodes::CallCfgNode call, DataFlow::Node arg, DataFlowPrivate::ParameterNodeImpl p
) {
exists(DataFlowDispatch::ParameterPosition pos |
argumentPositionMatch(call, arg, pos) and
viableParam(call, p, pos)
)
}
private module SummaryTypeTrackerInput implements SummaryTypeTracker::Input {
// Dataflow nodes
class Node = DataFlow::Node;
// Content
class Content = DataFlowPublic::ContentSet;
class ContentFilter = TypeTrackingInput::ContentFilter;
ContentFilter getFilterFromWithoutContentStep(Content content) {
(
content.isAnyElement()
or
content.isElementLowerBoundOrUnknown(_)
or
content.isElementOfTypeOrUnknown(_)
or
content.isSingleton(any(DataFlow::Content::UnknownElementContent c))
) and
result = MkElementFilter()
}
ContentFilter getFilterFromWithContentStep(Content content) {
(
content.isAnyElement()
or
content.isElementLowerBound(_)
or
content.isElementLowerBoundOrUnknown(_)
or
content.isElementOfType(_)
or
content.isElementOfTypeOrUnknown(_)
or
content.isSingleton(any(DataFlow::Content::ElementContent c))
) and
result = MkElementFilter()
}
// Summaries and their stacks
class SummaryComponent = FlowSummaryImpl::Private::SummaryComponent;
class SummaryComponentStack = FlowSummaryImpl::Private::SummaryComponentStack;
predicate singleton = FlowSummaryImpl::Private::SummaryComponentStack::singleton/1;
predicate push = FlowSummaryImpl::Private::SummaryComponentStack::push/2;
// Relating content to summaries
predicate content = FlowSummaryImpl::Private::SummaryComponent::content/1;
predicate withoutContent = FlowSummaryImpl::Private::SummaryComponent::withoutContent/1;
predicate withContent = FlowSummaryImpl::Private::SummaryComponent::withContent/1;
predicate return = FlowSummaryImpl::Private::SummaryComponent::return/0;
// Callables
class SummarizedCallable = FlowSummaryImpl::Private::SummarizedCallableImpl;
// Relating nodes to summaries
Node argumentOf(Node call, SummaryComponent arg, boolean isPostUpdate) {
exists(DataFlowDispatch::ParameterPosition pos, DataFlowPrivate::ArgumentNode n |
arg = FlowSummaryImpl::Private::SummaryComponent::argument(pos) and
argumentPositionMatch(call.asExpr(), n, pos)
|
isPostUpdate = false and result = n
or
isPostUpdate = true and result.(DataFlowPublic::PostUpdateNode).getPreUpdateNode() = n
)
}
Node parameterOf(Node callable, SummaryComponent param) {
exists(DataFlowDispatch::ArgumentPosition apos, DataFlowDispatch::ParameterPosition ppos |
param = FlowSummaryImpl::Private::SummaryComponent::parameter(apos) and
DataFlowDispatch::parameterMatch(ppos, apos) and
result
.(DataFlowPrivate::ParameterNodeImpl)
.isSourceParameterOf(callable.asExpr().getExpr(), ppos)
)
}
Node returnOf(Node callable, SummaryComponent return) {
return = FlowSummaryImpl::Private::SummaryComponent::return() and
result.(DataFlowPrivate::ReturnNode).(DataFlowPrivate::NodeImpl).getCfgScope() =
callable.asExpr().getExpr()
}
// Relating callables to nodes
Node callTo(SummarizedCallable callable) {
result.asExpr().getExpr() = callable.(FlowSummary::SummarizedCallable).getACallSimple()
}
}
private module TypeTrackerSummaryFlow = SummaryTypeTracker::SummaryFlow<SummaryTypeTrackerInput>;
private newtype TContentFilter = MkElementFilter()
module TypeTrackingInput implements Shared::TypeTrackingInput {
class Node = DataFlowPublic::Node;
class LocalSourceNode = DataFlowPublic::LocalSourceNode;
class Content = DataFlowPublic::ContentSet;
/**
* A label to use for `WithContent` and `WithoutContent` steps, restricting
* which `ContentSet` may pass through.
*/
class ContentFilter extends TContentFilter {
/** Gets a string representation of this content filter. */
string toString() { this = MkElementFilter() and result = "elements" }
/** Gets the content of a type-tracker that matches this filter. */
Content getAMatchingContent() {
this = MkElementFilter() and
result.getAReadContent() instanceof DataFlow::Content::ElementContent
}
}
/**
* Holds if a value stored with `storeContents` can be read back with `loadContents`.
*/
pragma[inline]
predicate compatibleContents(Content storeContents, Content loadContents) {
storeContents.getAStoreContent() = loadContents.getAReadContent()
}
/** Holds if there is a simple local flow step from `nodeFrom` to `nodeTo` */
predicate simpleLocalSmallStep = DataFlowPrivate::localFlowStepTypeTracker/2;
/** Holds if there is a level step from `nodeFrom` to `nodeTo`, which does not depend on the call graph. */
pragma[nomagic]
predicate levelStepNoCall(Node nodeFrom, LocalSourceNode nodeTo) {
TypeTrackerSummaryFlow::levelStepNoCall(nodeFrom, nodeTo)
or
localFieldStep(nodeFrom, nodeTo)
}
/** Holds if there is a level step from `nodeFrom` to `nodeTo`, which may depend on the call graph. */
pragma[nomagic]
predicate levelStepCall(Node nodeFrom, LocalSourceNode nodeTo) {
exists(DataFlowPublic::ParameterNode param |
flowThrough(param) and
callStepNoInitialize(nodeTo.asExpr(), nodeFrom, param)
)
}
/**
* Holds if `nodeFrom` steps to `nodeTo` by being passed as a parameter in a call.
*
* Flow into summarized library methods is not included, as that will lead to negative
* recursion (or, at best, terrible performance), since identifying calls to library
* methods is done using API graphs (which uses type tracking).
*/
predicate callStep(Node nodeFrom, LocalSourceNode nodeTo) { callStep(_, nodeFrom, nodeTo) }
/**
* Holds if `nodeFrom` steps to `nodeTo` by being returned from a call.
*
* Flow out of summarized library methods is not included, as that will lead to negative
* recursion (or, at best, terrible performance), since identifying calls to library
* methods is done using API graphs (which uses type tracking).
*/
predicate returnStep(Node nodeFrom, LocalSourceNode nodeTo) {
exists(ExprNodes::CallCfgNode call |
nodeFrom instanceof DataFlowPrivate::ReturnNode and
not nodeFrom instanceof DataFlowPrivate::InitializeReturnNode and
nodeFrom.(DataFlowPrivate::NodeImpl).getCfgScope() = DataFlowDispatch::getTarget(call) and
// deliberately do not include `getInitializeTarget`, since calls to `new` should not
// get the return value from `initialize`. Any fields being set in the initializer
// will reach all reads via `callStep` and `localFieldStep`.
nodeTo.asExpr().getAstNode() = call.getAstNode()
)
}
/**
* Holds if `nodeFrom` is being written to the `contents` of the object
* in `nodeTo`.
*
* Note that the choice of `nodeTo` does not have to make sense
* "chronologically". All we care about is whether the `contents` of
* `nodeTo` can have a specific type, and the assumption is that if a specific
* type appears here, then any access of that particular content can yield
* something of that particular type.
*
* Thus, in an example such as
*
* ```rb
* def foo(y)
* x = Foo.new
* bar(x)
* x.content = y
* baz(x)
* end
*
* def bar(x)
* z = x.content
* end
* ```
* for the content write `x.content = y`, we will have `contents` being the
* literal string `"content"`, `nodeFrom` will be `y`, and `nodeTo` will be the
* `Foo` object created on the first line of the function. This means we will
* track the fact that `x.content` can have the type of `y` into the assignment
* to `z` inside `bar`, even though this content write happens _after_ `bar` is
* called.
*/
predicate storeStep(Node nodeFrom, Node nodeTo, Content contents) {
// TODO: support SetterMethodCall inside TuplePattern
exists(ExprNodes::MethodCallCfgNode call |
contents
.isSingleton(DataFlowPublic::Content::getAttributeName(call.getExpr()
.(SetterMethodCall)
.getTargetName())) and
nodeTo.(DataFlowPublic::PostUpdateNode).getPreUpdateNode().asExpr() = call.getReceiver() and
call.getExpr() instanceof SetterMethodCall and
call.getArgument(call.getNumberOfArguments() - 1) =
nodeFrom.(DataFlowPublic::ExprNode).getExprNode()
)
or
DataFlowPrivate::storeStepCommon(nodeFrom, contents, nodeTo)
or
TypeTrackerSummaryFlow::basicStoreStep(nodeFrom, nodeTo, contents)
}
/**
* Holds if `nodeTo` is the result of accessing the `content` content of `nodeFrom`.
*/
predicate loadStep(Node nodeFrom, LocalSourceNode nodeTo, Content contents) {
DataFlowPrivate::readStepCommon(nodeFrom, contents, nodeTo)
or
exists(ExprNodes::MethodCallCfgNode call |
call.getExpr().getNumberOfArguments() = 0 and
contents
.isSingleton(DataFlowPublic::Content::getAttributeName(call.getExpr().getMethodName())) and
nodeFrom.asExpr() = call.getReceiver() and
nodeTo.asExpr() = call
)
or
TypeTrackerSummaryFlow::basicLoadStep(nodeFrom, nodeTo, contents)
}
/**
* Holds if the `loadContent` of `nodeFrom` is stored in the `storeContent` of `nodeTo`.
*/
predicate loadStoreStep(Node nodeFrom, Node nodeTo, Content loadContent, Content storeContent) {
exists(DataFlowPrivate::SynthSplatParameterShiftNode shift |
shift.readFrom(nodeFrom, loadContent) and
shift.storeInto(nodeTo, storeContent)
)
or
exists(DataFlowPrivate::SynthSplatArgumentShiftNode shift |
shift.readFrom(nodeFrom, loadContent) and
shift.storeInto(nodeTo, storeContent)
)
or
TypeTrackerSummaryFlow::basicLoadStoreStep(nodeFrom, nodeTo, loadContent, storeContent)
}
/**
* Same as `withContentStep`, but `nodeTo` has type `Node` instead of `LocalSourceNode`,
* which allows for it by used in the definition of `LocalSourceNode`.
*/
additional predicate withContentStepImpl(Node nodeFrom, Node nodeTo, ContentFilter filter) {
TypeTrackerSummaryFlow::basicWithContentStep(nodeFrom, nodeTo, filter)
}
/**
* Holds if type-tracking should step from `nodeFrom` to `nodeTo` if inside a content matched by `filter`.
*/
predicate withContentStep(Node nodeFrom, LocalSourceNode nodeTo, ContentFilter filter) {
withContentStepImpl(nodeFrom, nodeTo, filter)
}
/**
* Same as `withoutContentStep`, but `nodeTo` has type `Node` instead of `LocalSourceNode`,
* which allows for it by used in the definition of `LocalSourceNode`.
*/
additional predicate withoutContentStepImpl(Node nodeFrom, Node nodeTo, ContentFilter filter) {
TypeTrackerSummaryFlow::basicWithoutContentStep(nodeFrom, nodeTo, filter)
}
/**
* Holds if type-tracking should step from `nodeFrom` to `nodeTo` but block flow of contents matched by `filter` through here.
*/
predicate withoutContentStep(Node nodeFrom, LocalSourceNode nodeTo, ContentFilter filter) {
withoutContentStepImpl(nodeFrom, nodeTo, filter)
}
/**
* Holds if data can flow from `node1` to `node2` in a way that discards call contexts.
*/
predicate jumpStep(Node nodeFrom, LocalSourceNode nodeTo) {
DataFlowPrivate::jumpStep(nodeFrom, nodeTo)
}
predicate hasFeatureBacktrackStoreTarget() { none() }
}
import SharedImpl::TypeTracking<TypeTrackingInput>

View File

@@ -1,3 +1,11 @@
/**
* @name Generate flow models
* @description Queries to generate source, sink, summary and type models.
* @kind table
* @id rb/utils/modeleditor/generate-model
* @tags modeleditor generate-model framework-mode
*/
private import internal.Types
private import internal.Summaries

View File

@@ -0,0 +1,17 @@
/**
* @name Fetch endpoints for use in the model editor (application mode)
* @description A list of 3rd party endpoints (methods and attributes) used in the codebase. Excludes test and generated code.
* @kind table
* @id rb/utils/modeleditor/application-mode-endpoints
* @tags modeleditor endpoints application-mode
*/
import codeql.ruby.AST
// This query is empty as Application Mode is not yet supported for Ruby.
from
Call usage, string package, string type, string name, string parameters, boolean supported,
string namespace, string version, string supportedType, string classification
where none()
select usage, package, namespace, type, name, parameters, supported, namespace, version,
supportedType, classification

View File

@@ -0,0 +1,15 @@
/**
* @name Fetch endpoints for use in the model editor (framework mode)
* @description A list of endpoints accessible (methods and attributes) for consumers of the library. Excludes test and generated code.
* @kind table
* @id rb/utils/modeleditor/framework-mode-endpoints
* @tags modeleditor endpoints framework-mode
*/
import ruby
import ModelEditor
from PublicEndpointFromSource endpoint
select endpoint, endpoint.getNamespace(), endpoint.getTypeName(), endpoint.getName(),
endpoint.getParameterTypes(), endpoint.getSupportedStatus(), endpoint.getFile().getBaseName(),
endpoint.getSupportedType()

View File

@@ -0,0 +1,172 @@
/** Provides classes and predicates related to handling APIs for the VS Code extension. */
private import ruby
private import codeql.ruby.dataflow.FlowSummary
private import codeql.ruby.dataflow.internal.DataFlowPrivate
private import codeql.ruby.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
private import codeql.ruby.frameworks.core.Gem
private import codeql.ruby.frameworks.data.ModelsAsData
private import codeql.ruby.frameworks.data.internal.ApiGraphModelsExtensions
private import queries.modeling.internal.Util as Util
/** Holds if the given callable is not worth supporting. */
private predicate isUninteresting(DataFlow::MethodNode c) {
c.getLocation().getFile() instanceof TestFile
}
private predicate gemFileStep(Gem::GemSpec gem, Folder folder, int n) {
n = 0 and folder.getAFile() = gem.(File)
or
exists(Folder parent, int m |
gemFileStep(gem, parent, m) and
parent.getAFolder() = folder and
n = m + 1
)
}
/**
* A callable method or accessor from either the Ruby Standard Library, a 3rd party library, or from the source.
*/
class Endpoint extends DataFlow::MethodNode {
Endpoint() { this.isPublic() and not isUninteresting(this) }
File getFile() { result = this.getLocation().getFile() }
string getName() { result = this.getMethodName() }
/**
* Gets the namespace of this endpoint.
*/
bindingset[this]
string getNamespace() {
exists(Folder folder | folder = this.getFile().getParentContainer() |
// The nearest gemspec to this endpoint, if one exists
result = min(Gem::GemSpec g, int n | gemFileStep(g, folder, n) | g order by n).getName()
or
not gemFileStep(_, folder, _) and
result = ""
)
}
/**
* Gets the unbound type name of this endpoint.
*/
bindingset[this]
string getTypeName() {
result =
any(DataFlow::ModuleNode m | m.getOwnInstanceMethod(this.getMethodName()) = this)
.getQualifiedName() or
result =
any(DataFlow::ModuleNode m | m.getOwnSingletonMethod(this.getMethodName()) = this)
.getQualifiedName() + "!"
}
/**
* Gets the parameter types of this endpoint.
*/
bindingset[this]
string getParameterTypes() {
// For now, return the names of postional and keyword parameters. We don't always have type information, so we can't return type names.
// We don't yet handle splat params or block params.
result =
"(" +
concat(string key, string value |
value = any(int i | i.toString() = key | this.asCallable().getParameter(i)).getName()
or
exists(DataFlow::ParameterNode param |
param = this.asCallable().getKeywordParameter(key)
|
value = key + ":"
)
|
value, "," order by key
) + ")"
}
/** Holds if this API has a supported summary. */
pragma[nomagic]
predicate hasSummary() { none() }
/** Holds if this API is a known source. */
pragma[nomagic]
abstract predicate isSource();
/** Holds if this API is a known sink. */
pragma[nomagic]
abstract predicate isSink();
/** Holds if this API is a known neutral. */
pragma[nomagic]
predicate isNeutral() { none() }
/**
* Holds if this API is supported by existing CodeQL libraries, that is, it is either a
* recognized source, sink or neutral or it has a flow summary.
*/
predicate isSupported() {
this.hasSummary() or this.isSource() or this.isSink() or this.isNeutral()
}
boolean getSupportedStatus() { if this.isSupported() then result = true else result = false }
string getSupportedType() {
this.isSink() and result = "sink"
or
this.isSource() and result = "source"
or
this.hasSummary() and result = "summary"
or
this.isNeutral() and result = "neutral"
or
not this.isSupported() and result = ""
}
}
string methodClassification(Call method) {
method.getFile() instanceof TestFile and result = "test"
or
not method.getFile() instanceof TestFile and
result = "source"
}
class TestFile extends File {
TestFile() {
this.getRelativePath().regexpMatch(".*(test|spec).+") and
not this.getAbsolutePath().matches("%/ql/test/%") // allows our test cases to work
}
}
/**
* A callable where there exists a MaD sink model that applies to it.
*/
class SinkCallable extends DataFlow::MethodNode {
SinkCallable() {
exists(string type, string path, string method |
method = path.regexpCapture("(Method\\[[^\\]]+\\]).*", 1) and
Util::pathToMethod(this, type, method) and
sinkModel(type, path, _)
)
}
}
/**
* A callable where there exists a MaD source model that applies to it.
*/
class SourceCallable extends DataFlow::CallableNode {
SourceCallable() {
exists(string type, string path, string method |
method = path.regexpCapture("(Method\\[[^\\]]+\\]).*", 1) and
Util::pathToMethod(this, type, method) and
sourceModel(type, path, _)
)
}
}
/**
* A class of effectively public callables from source code.
*/
class PublicEndpointFromSource extends Endpoint {
override predicate isSource() { this instanceof SourceCallable }
override predicate isSink() { this instanceof SinkCallable }
}

View File

@@ -12,6 +12,7 @@ private import internal.InlineExpectationsTestImpl
private module FlowTestImpl implements InputSig<RubyDataFlow> {
import TestUtilities.InlineFlowTestUtil
bindingset[src, sink]
string getArgString(DataFlow::Node src, DataFlow::Node sink) {
(if exists(getSourceArgString(src)) then result = getSourceArgString(src) else result = "") and
exists(sink)

View File

@@ -1,7 +1,7 @@
import ruby
import TestUtilities.InlineExpectationsTest
import TestUtilities.InlineFlowTestUtil
private import codeql.ruby.typetracking.TypeTracker
private import codeql.ruby.typetracking.TypeTracking
private DataFlow::LocalSourceNode track(TypeTracker t, DataFlow::CallNode source) {
t.start() and

View File

@@ -1,16 +1,15 @@
import ruby
import codeql.dataflow.internal.AccessPathSyntax
import codeql.ruby.ast.internal.TreeSitter
import codeql.ruby.dataflow.internal.AccessPathSyntax
import codeql.ruby.frameworks.data.internal.ApiGraphModels
import codeql.ruby.frameworks.data.internal.ApiGraphModels as ApiGraphModels
import codeql.ruby.ApiGraphs
import TestUtilities.InlineExpectationsTest
class AccessPathFromExpectation extends AccessPath::Range {
AccessPathFromExpectation() { hasExpectationWithValue(_, this) }
}
private predicate accessPathRange(string s) { hasExpectationWithValue(_, s) }
import AccessPath<accessPathRange/1>
API::Node evaluatePath(AccessPath path, int n) {
path instanceof AccessPathFromExpectation and
n = 1 and
exists(AccessPathToken token | token = path.getToken(0) |
token.getName() = "Member" and
@@ -23,9 +22,9 @@ API::Node evaluatePath(AccessPath path, int n) {
result = token.getAnArgument().(API::EntryPoint).getANode()
)
or
result = getSuccessorFromNode(evaluatePath(path, n - 1), path.getToken(n - 1))
result = ApiGraphModels::getSuccessorFromNode(evaluatePath(path, n - 1), path.getToken(n - 1))
or
result = getSuccessorFromInvoke(evaluatePath(path, n - 1), path.getToken(n - 1))
result = ApiGraphModels::getSuccessorFromInvoke(evaluatePath(path, n - 1), path.getToken(n - 1))
or
// TODO this is a workaround, support parsing of Method['[]'] instead
path.getToken(n - 1).getName() = "MethodBracket" and

View File

@@ -32,7 +32,6 @@ testFailures
| array_flow.rb:940:18:940:78 | # $ hasValueFlow=91.1 $ hasValueFlow=91.2 $ hasValueFlow=91.3 | Missing result:hasValueFlow=91.3 |
| array_flow.rb:957:28:957:46 | # $ hasValueFlow=93 | Missing result:hasValueFlow=93 |
| array_flow.rb:958:28:958:46 | # $ hasValueFlow=93 | Missing result:hasValueFlow=93 |
| array_flow.rb:1018:16:1018:36 | # $ hasValueFlow=99.2 | Missing result:hasValueFlow=99.2 |
| array_flow.rb:1099:10:1099:13 | ...[...] | Unexpected result: hasValueFlow=105.2 |
| array_flow.rb:1100:10:1100:13 | ...[...] | Unexpected result: hasValueFlow=105.3 |
| array_flow.rb:1110:10:1110:13 | ...[...] | Unexpected result: hasValueFlow=105.2 |

View File

@@ -16,7 +16,7 @@ abstract private class Summary extends SimpleSummarizedCallable {
bindingset[this]
Summary() { any() }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
this.propagates(input, output) and preservesValue = true
}

View File

@@ -9,30 +9,16 @@ testFailures
| hash_flow.rb:219:27:219:47 | # $ hasValueFlow=14.2 | Missing result:hasValueFlow=14.2 |
| hash_flow.rb:291:10:291:14 | ...[...] | Unexpected result: hasValueFlow=19.1 |
| hash_flow.rb:294:10:294:14 | ...[...] | Unexpected result: hasValueFlow=19.3 |
| hash_flow.rb:453:22:453:42 | # $ hasValueFlow=27.3 | Missing result:hasValueFlow=27.3 |
| hash_flow.rb:455:22:455:42 | # $ hasValueFlow=27.4 | Missing result:hasValueFlow=27.4 |
| hash_flow.rb:467:16:467:36 | # $ hasValueFlow=28.1 | Missing result:hasValueFlow=28.1 |
| hash_flow.rb:513:22:513:42 | # $ hasValueFlow=31.1 | Missing result:hasValueFlow=31.1 |
| hash_flow.rb:515:10:515:20 | ( ... ) | Unexpected result: hasValueFlow=31.3 |
| hash_flow.rb:515:22:515:42 | # $ hasValueFlow=31.2 | Missing result:hasValueFlow=31.2 |
| hash_flow.rb:559:17:559:57 | # $ hasValueFlow=34.1 $ hasValueFlow=34.2 | Missing result:hasValueFlow=34.1 |
| hash_flow.rb:559:17:559:57 | # $ hasValueFlow=34.1 $ hasValueFlow=34.2 | Missing result:hasValueFlow=34.2 |
| hash_flow.rb:571:18:571:38 | # $ hasValueFlow=35.1 | Missing result:hasValueFlow=35.1 |
| hash_flow.rb:591:20:591:60 | # $ hasValueFlow=36.1 $ hasValueFlow=36.2 | Missing result:hasValueFlow=36.1 |
| hash_flow.rb:591:20:591:60 | # $ hasValueFlow=36.1 $ hasValueFlow=36.2 | Missing result:hasValueFlow=36.2 |
| hash_flow.rb:673:10:673:19 | ( ... ) | Unexpected result: hasValueFlow=41.1 |
| hash_flow.rb:704:22:704:42 | # $ hasValueFlow=42.3 | Missing result:hasValueFlow=42.3 |
| hash_flow.rb:706:22:706:42 | # $ hasValueFlow=42.4 | Missing result:hasValueFlow=42.4 |
| hash_flow.rb:776:10:776:14 | ...[...] | Unexpected result: hasValueFlow=46.1 |
| hash_flow.rb:779:10:779:14 | ...[...] | Unexpected result: hasValueFlow=46.3 |
| hash_flow.rb:781:10:781:17 | ...[...] | Unexpected result: hasValueFlow=46.1 |
| hash_flow.rb:784:10:784:17 | ...[...] | Unexpected result: hasValueFlow=46.3 |
| hash_flow.rb:841:22:841:42 | # $ hasValueFlow=48.3 | Missing result:hasValueFlow=48.3 |
| hash_flow.rb:843:22:843:42 | # $ hasValueFlow=48.4 | Missing result:hasValueFlow=48.4 |
| hash_flow.rb:903:22:903:42 | # $ hasValueFlow=50.3 | Missing result:hasValueFlow=50.3 |
| hash_flow.rb:905:22:905:42 | # $ hasValueFlow=50.4 | Missing result:hasValueFlow=50.4 |
| hash_flow.rb:933:22:933:42 | # $ hasValueFlow=51.3 | Missing result:hasValueFlow=51.3 |
| hash_flow.rb:935:22:935:42 | # $ hasValueFlow=51.4 | Missing result:hasValueFlow=51.4 |
| hash_flow.rb:963:22:963:42 | # $ hasValueFlow=52.3 | Missing result:hasValueFlow=52.3 |
| hash_flow.rb:965:22:965:42 | # $ hasValueFlow=52.4 | Missing result:hasValueFlow=52.4 |
failures

View File

@@ -2528,6 +2528,8 @@
| local_dataflow.rb:78:12:78:20 | [post] self | local_dataflow.rb:85:22:85:28 | self |
| local_dataflow.rb:78:12:78:20 | [post] self | local_dataflow.rb:86:28:86:34 | self |
| local_dataflow.rb:78:12:78:20 | [post] self | local_dataflow.rb:87:20:87:26 | self |
| local_dataflow.rb:78:12:78:20 | call to source | local_dataflow.rb:79:13:79:13 | b |
| local_dataflow.rb:78:12:78:20 | call to source | local_dataflow.rb:80:8:80:8 | a |
| local_dataflow.rb:78:12:78:20 | self | local_dataflow.rb:79:20:79:26 | self |
| local_dataflow.rb:78:12:78:20 | self | local_dataflow.rb:80:24:80:30 | self |
| local_dataflow.rb:78:12:78:20 | self | local_dataflow.rb:82:7:82:13 | self |

View File

@@ -76,8 +76,8 @@ def test_case x
end
z = case source(1)
in 5 => b then sink(b) # $ hasTaintFlow=1
in a if a > 0 then sink(a) # $ hasTaintFlow=1
in 5 => b then sink(b) # $ hasValueFlow=1
in a if a > 0 then sink(a) # $ hasValueFlow=1
in [c, *d, e ] then [
sink(c), # $ hasTaintFlow=1
sink(d), # $ hasTaintFlow=1

View File

@@ -2660,52 +2660,52 @@ track
| params_flow.rb:120:1:126:3 | destruct | type tracker without call steps | params_flow.rb:120:1:126:3 | destruct |
| params_flow.rb:120:1:126:3 | self in destruct | type tracker with call steps | params_flow.rb:5:1:7:3 | self in sink |
| params_flow.rb:120:1:126:3 | self in destruct | type tracker without call steps | params_flow.rb:120:1:126:3 | self in destruct |
| params_flow.rb:120:15:120:15 | a | type tracker with call steps | params_flow.rb:5:10:5:10 | x |
| params_flow.rb:120:15:120:15 | a | type tracker with call steps with content splat position 0 | params_flow.rb:5:1:7:3 | synthetic splat parameter |
| params_flow.rb:120:15:120:15 | a | type tracker with call steps with content splat position 0 | params_flow.rb:6:5:6:10 | synthetic splat argument |
| params_flow.rb:120:15:120:15 | a | type tracker without call steps | params_flow.rb:120:15:120:15 | a |
| params_flow.rb:120:15:120:15 | a | type tracker without call steps | params_flow.rb:120:15:120:15 | a |
| params_flow.rb:120:15:120:15 | a | type tracker without call steps with content splat position 0 | params_flow.rb:121:5:121:10 | synthetic splat argument |
| params_flow.rb:120:17:120:17 | b | type tracker with call steps | params_flow.rb:5:10:5:10 | x |
| params_flow.rb:120:17:120:17 | b | type tracker with call steps with content splat position 0 | params_flow.rb:5:1:7:3 | synthetic splat parameter |
| params_flow.rb:120:17:120:17 | b | type tracker with call steps with content splat position 0 | params_flow.rb:6:5:6:10 | synthetic splat argument |
| params_flow.rb:120:17:120:17 | b | type tracker without call steps | params_flow.rb:120:17:120:17 | b |
| params_flow.rb:120:17:120:17 | b | type tracker without call steps | params_flow.rb:120:17:120:17 | b |
| params_flow.rb:120:17:120:17 | b | type tracker without call steps with content splat position 0 | params_flow.rb:122:5:122:10 | synthetic splat argument |
| params_flow.rb:120:22:120:22 | c | type tracker with call steps | params_flow.rb:5:10:5:10 | x |
| params_flow.rb:120:22:120:22 | c | type tracker with call steps with content splat position 0 | params_flow.rb:5:1:7:3 | synthetic splat parameter |
| params_flow.rb:120:22:120:22 | c | type tracker with call steps with content splat position 0 | params_flow.rb:6:5:6:10 | synthetic splat argument |
| params_flow.rb:120:22:120:22 | c | type tracker without call steps | params_flow.rb:120:22:120:22 | c |
| params_flow.rb:120:22:120:22 | c | type tracker without call steps | params_flow.rb:120:22:120:22 | c |
| params_flow.rb:120:22:120:22 | c | type tracker without call steps with content splat position 0 | params_flow.rb:123:5:123:10 | synthetic splat argument |
| params_flow.rb:120:25:120:25 | d | type tracker with call steps | params_flow.rb:5:10:5:10 | x |
| params_flow.rb:120:25:120:25 | d | type tracker with call steps with content splat position 0 | params_flow.rb:5:1:7:3 | synthetic splat parameter |
| params_flow.rb:120:25:120:25 | d | type tracker with call steps with content splat position 0 | params_flow.rb:6:5:6:10 | synthetic splat argument |
| params_flow.rb:120:25:120:25 | d | type tracker without call steps | params_flow.rb:120:25:120:25 | d |
| params_flow.rb:120:25:120:25 | d | type tracker without call steps | params_flow.rb:120:25:120:25 | d |
| params_flow.rb:120:25:120:25 | d | type tracker without call steps with content splat position 0 | params_flow.rb:124:5:124:10 | synthetic splat argument |
| params_flow.rb:120:27:120:27 | e | type tracker with call steps | params_flow.rb:5:10:5:10 | x |
| params_flow.rb:120:27:120:27 | e | type tracker with call steps with content splat position 0 | params_flow.rb:5:1:7:3 | synthetic splat parameter |
| params_flow.rb:120:27:120:27 | e | type tracker with call steps with content splat position 0 | params_flow.rb:6:5:6:10 | synthetic splat argument |
| params_flow.rb:120:27:120:27 | e | type tracker without call steps | params_flow.rb:120:27:120:27 | e |
| params_flow.rb:120:27:120:27 | e | type tracker without call steps | params_flow.rb:120:27:120:27 | e |
| params_flow.rb:120:27:120:27 | e | type tracker without call steps with content splat position 0 | params_flow.rb:125:5:125:10 | synthetic splat argument |
| params_flow.rb:121:5:121:10 | call to sink | type tracker without call steps | params_flow.rb:121:5:121:10 | call to sink |
| params_flow.rb:121:5:121:10 | synthetic splat argument | type tracker with call steps | params_flow.rb:5:1:7:3 | synthetic splat parameter |
| params_flow.rb:121:5:121:10 | synthetic splat argument | type tracker without call steps | params_flow.rb:121:5:121:10 | synthetic splat argument |
| params_flow.rb:121:10:121:10 | a | type tracker with call steps | params_flow.rb:5:10:5:10 | x |
| params_flow.rb:121:10:121:10 | a | type tracker with call steps with content splat position 0 | params_flow.rb:5:1:7:3 | synthetic splat parameter |
| params_flow.rb:121:10:121:10 | a | type tracker with call steps with content splat position 0 | params_flow.rb:6:5:6:10 | synthetic splat argument |
| params_flow.rb:121:10:121:10 | a | type tracker without call steps | params_flow.rb:121:10:121:10 | a |
| params_flow.rb:121:10:121:10 | a | type tracker without call steps with content splat position 0 | params_flow.rb:121:5:121:10 | synthetic splat argument |
| params_flow.rb:122:5:122:10 | call to sink | type tracker without call steps | params_flow.rb:122:5:122:10 | call to sink |
| params_flow.rb:122:5:122:10 | synthetic splat argument | type tracker with call steps | params_flow.rb:5:1:7:3 | synthetic splat parameter |
| params_flow.rb:122:5:122:10 | synthetic splat argument | type tracker without call steps | params_flow.rb:122:5:122:10 | synthetic splat argument |
| params_flow.rb:122:10:122:10 | b | type tracker with call steps | params_flow.rb:5:10:5:10 | x |
| params_flow.rb:122:10:122:10 | b | type tracker with call steps with content splat position 0 | params_flow.rb:5:1:7:3 | synthetic splat parameter |
| params_flow.rb:122:10:122:10 | b | type tracker with call steps with content splat position 0 | params_flow.rb:6:5:6:10 | synthetic splat argument |
| params_flow.rb:122:10:122:10 | b | type tracker without call steps | params_flow.rb:122:10:122:10 | b |
| params_flow.rb:122:10:122:10 | b | type tracker without call steps with content splat position 0 | params_flow.rb:122:5:122:10 | synthetic splat argument |
| params_flow.rb:123:5:123:10 | call to sink | type tracker without call steps | params_flow.rb:123:5:123:10 | call to sink |
| params_flow.rb:123:5:123:10 | synthetic splat argument | type tracker with call steps | params_flow.rb:5:1:7:3 | synthetic splat parameter |
| params_flow.rb:123:5:123:10 | synthetic splat argument | type tracker without call steps | params_flow.rb:123:5:123:10 | synthetic splat argument |
| params_flow.rb:123:10:123:10 | c | type tracker with call steps | params_flow.rb:5:10:5:10 | x |
| params_flow.rb:123:10:123:10 | c | type tracker with call steps with content splat position 0 | params_flow.rb:5:1:7:3 | synthetic splat parameter |
| params_flow.rb:123:10:123:10 | c | type tracker with call steps with content splat position 0 | params_flow.rb:6:5:6:10 | synthetic splat argument |
| params_flow.rb:123:10:123:10 | c | type tracker without call steps | params_flow.rb:123:10:123:10 | c |
| params_flow.rb:123:10:123:10 | c | type tracker without call steps with content splat position 0 | params_flow.rb:123:5:123:10 | synthetic splat argument |
| params_flow.rb:124:5:124:10 | call to sink | type tracker without call steps | params_flow.rb:124:5:124:10 | call to sink |
| params_flow.rb:124:5:124:10 | synthetic splat argument | type tracker with call steps | params_flow.rb:5:1:7:3 | synthetic splat parameter |
| params_flow.rb:124:5:124:10 | synthetic splat argument | type tracker without call steps | params_flow.rb:124:5:124:10 | synthetic splat argument |
| params_flow.rb:124:10:124:10 | d | type tracker with call steps | params_flow.rb:5:10:5:10 | x |
| params_flow.rb:124:10:124:10 | d | type tracker with call steps with content splat position 0 | params_flow.rb:5:1:7:3 | synthetic splat parameter |
| params_flow.rb:124:10:124:10 | d | type tracker with call steps with content splat position 0 | params_flow.rb:6:5:6:10 | synthetic splat argument |
| params_flow.rb:124:10:124:10 | d | type tracker without call steps | params_flow.rb:124:10:124:10 | d |
| params_flow.rb:124:10:124:10 | d | type tracker without call steps with content splat position 0 | params_flow.rb:124:5:124:10 | synthetic splat argument |
| params_flow.rb:125:5:125:10 | call to sink | type tracker without call steps | params_flow.rb:125:5:125:10 | call to sink |
| params_flow.rb:125:5:125:10 | call to sink | type tracker without call steps | params_flow.rb:128:1:128:61 | call to destruct |
| params_flow.rb:125:5:125:10 | synthetic splat argument | type tracker with call steps | params_flow.rb:5:1:7:3 | synthetic splat parameter |
| params_flow.rb:125:5:125:10 | synthetic splat argument | type tracker without call steps | params_flow.rb:125:5:125:10 | synthetic splat argument |
| params_flow.rb:125:10:125:10 | e | type tracker with call steps | params_flow.rb:5:10:5:10 | x |
| params_flow.rb:125:10:125:10 | e | type tracker with call steps with content splat position 0 | params_flow.rb:5:1:7:3 | synthetic splat parameter |
| params_flow.rb:125:10:125:10 | e | type tracker with call steps with content splat position 0 | params_flow.rb:6:5:6:10 | synthetic splat argument |
| params_flow.rb:125:10:125:10 | e | type tracker without call steps | params_flow.rb:125:10:125:10 | e |
| params_flow.rb:125:10:125:10 | e | type tracker without call steps with content splat position 0 | params_flow.rb:125:5:125:10 | synthetic splat argument |
| params_flow.rb:128:1:128:61 | call to destruct | type tracker without call steps | params_flow.rb:128:1:128:61 | call to destruct |
| params_flow.rb:128:1:128:61 | synthetic splat argument | type tracker without call steps | params_flow.rb:128:1:128:61 | synthetic splat argument |
| params_flow.rb:128:10:128:31 | Array | type tracker without call steps | params_flow.rb:128:10:128:31 | Array |
@@ -6168,47 +6168,52 @@ trackEnd
| params_flow.rb:120:1:126:3 | self in destruct | params_flow.rb:123:5:123:10 | self |
| params_flow.rb:120:1:126:3 | self in destruct | params_flow.rb:124:5:124:10 | self |
| params_flow.rb:120:1:126:3 | self in destruct | params_flow.rb:125:5:125:10 | self |
| params_flow.rb:120:15:120:15 | a | params_flow.rb:5:10:5:10 | x |
| params_flow.rb:120:15:120:15 | a | params_flow.rb:5:10:5:10 | x |
| params_flow.rb:120:15:120:15 | a | params_flow.rb:6:10:6:10 | x |
| params_flow.rb:120:15:120:15 | a | params_flow.rb:120:15:120:15 | a |
| params_flow.rb:120:15:120:15 | a | params_flow.rb:120:15:120:15 | a |
| params_flow.rb:120:15:120:15 | a | params_flow.rb:121:10:121:10 | a |
| params_flow.rb:120:17:120:17 | b | params_flow.rb:5:10:5:10 | x |
| params_flow.rb:120:17:120:17 | b | params_flow.rb:5:10:5:10 | x |
| params_flow.rb:120:17:120:17 | b | params_flow.rb:6:10:6:10 | x |
| params_flow.rb:120:17:120:17 | b | params_flow.rb:120:17:120:17 | b |
| params_flow.rb:120:17:120:17 | b | params_flow.rb:120:17:120:17 | b |
| params_flow.rb:120:17:120:17 | b | params_flow.rb:122:10:122:10 | b |
| params_flow.rb:120:22:120:22 | c | params_flow.rb:5:10:5:10 | x |
| params_flow.rb:120:22:120:22 | c | params_flow.rb:5:10:5:10 | x |
| params_flow.rb:120:22:120:22 | c | params_flow.rb:6:10:6:10 | x |
| params_flow.rb:120:22:120:22 | c | params_flow.rb:120:22:120:22 | c |
| params_flow.rb:120:22:120:22 | c | params_flow.rb:120:22:120:22 | c |
| params_flow.rb:120:22:120:22 | c | params_flow.rb:123:10:123:10 | c |
| params_flow.rb:120:25:120:25 | d | params_flow.rb:5:10:5:10 | x |
| params_flow.rb:120:25:120:25 | d | params_flow.rb:5:10:5:10 | x |
| params_flow.rb:120:25:120:25 | d | params_flow.rb:6:10:6:10 | x |
| params_flow.rb:120:25:120:25 | d | params_flow.rb:120:25:120:25 | d |
| params_flow.rb:120:25:120:25 | d | params_flow.rb:120:25:120:25 | d |
| params_flow.rb:120:25:120:25 | d | params_flow.rb:124:10:124:10 | d |
| params_flow.rb:120:27:120:27 | e | params_flow.rb:5:10:5:10 | x |
| params_flow.rb:120:27:120:27 | e | params_flow.rb:5:10:5:10 | x |
| params_flow.rb:120:27:120:27 | e | params_flow.rb:6:10:6:10 | x |
| params_flow.rb:120:27:120:27 | e | params_flow.rb:120:27:120:27 | e |
| params_flow.rb:120:27:120:27 | e | params_flow.rb:120:27:120:27 | e |
| params_flow.rb:120:27:120:27 | e | params_flow.rb:125:10:125:10 | e |
| params_flow.rb:121:5:121:10 | call to sink | params_flow.rb:121:5:121:10 | call to sink |
| params_flow.rb:121:5:121:10 | synthetic splat argument | params_flow.rb:5:1:7:3 | synthetic splat parameter |
| params_flow.rb:121:5:121:10 | synthetic splat argument | params_flow.rb:121:5:121:10 | synthetic splat argument |
| params_flow.rb:121:10:121:10 | a | params_flow.rb:5:10:5:10 | x |
| params_flow.rb:121:10:121:10 | a | params_flow.rb:5:10:5:10 | x |
| params_flow.rb:121:10:121:10 | a | params_flow.rb:6:10:6:10 | x |
| params_flow.rb:121:10:121:10 | a | params_flow.rb:121:10:121:10 | a |
| params_flow.rb:122:5:122:10 | call to sink | params_flow.rb:122:5:122:10 | call to sink |
| params_flow.rb:122:5:122:10 | synthetic splat argument | params_flow.rb:5:1:7:3 | synthetic splat parameter |
| params_flow.rb:122:5:122:10 | synthetic splat argument | params_flow.rb:122:5:122:10 | synthetic splat argument |
| params_flow.rb:122:10:122:10 | b | params_flow.rb:5:10:5:10 | x |
| params_flow.rb:122:10:122:10 | b | params_flow.rb:5:10:5:10 | x |
| params_flow.rb:122:10:122:10 | b | params_flow.rb:6:10:6:10 | x |
| params_flow.rb:122:10:122:10 | b | params_flow.rb:122:10:122:10 | b |
| params_flow.rb:123:5:123:10 | call to sink | params_flow.rb:123:5:123:10 | call to sink |
| params_flow.rb:123:5:123:10 | synthetic splat argument | params_flow.rb:5:1:7:3 | synthetic splat parameter |
| params_flow.rb:123:5:123:10 | synthetic splat argument | params_flow.rb:123:5:123:10 | synthetic splat argument |
| params_flow.rb:123:10:123:10 | c | params_flow.rb:5:10:5:10 | x |
| params_flow.rb:123:10:123:10 | c | params_flow.rb:5:10:5:10 | x |
| params_flow.rb:123:10:123:10 | c | params_flow.rb:6:10:6:10 | x |
| params_flow.rb:123:10:123:10 | c | params_flow.rb:123:10:123:10 | c |
| params_flow.rb:124:5:124:10 | call to sink | params_flow.rb:124:5:124:10 | call to sink |
| params_flow.rb:124:5:124:10 | synthetic splat argument | params_flow.rb:5:1:7:3 | synthetic splat parameter |
| params_flow.rb:124:5:124:10 | synthetic splat argument | params_flow.rb:124:5:124:10 | synthetic splat argument |
| params_flow.rb:124:10:124:10 | d | params_flow.rb:5:10:5:10 | x |
| params_flow.rb:124:10:124:10 | d | params_flow.rb:5:10:5:10 | x |
| params_flow.rb:124:10:124:10 | d | params_flow.rb:6:10:6:10 | x |
| params_flow.rb:124:10:124:10 | d | params_flow.rb:124:10:124:10 | d |
| params_flow.rb:125:5:125:10 | call to sink | params_flow.rb:125:5:125:10 | call to sink |
| params_flow.rb:125:5:125:10 | call to sink | params_flow.rb:128:1:128:61 | call to destruct |
| params_flow.rb:125:5:125:10 | synthetic splat argument | params_flow.rb:5:1:7:3 | synthetic splat parameter |
| params_flow.rb:125:5:125:10 | synthetic splat argument | params_flow.rb:125:5:125:10 | synthetic splat argument |
| params_flow.rb:125:10:125:10 | e | params_flow.rb:5:10:5:10 | x |
| params_flow.rb:125:10:125:10 | e | params_flow.rb:5:10:5:10 | x |
| params_flow.rb:125:10:125:10 | e | params_flow.rb:6:10:6:10 | x |
| params_flow.rb:125:10:125:10 | e | params_flow.rb:125:10:125:10 | e |
| params_flow.rb:128:1:128:61 | call to destruct | params_flow.rb:128:1:128:61 | call to destruct |
| params_flow.rb:128:1:128:61 | synthetic splat argument | params_flow.rb:128:1:128:61 | synthetic splat argument |
| params_flow.rb:128:10:128:31 | Array | params_flow.rb:128:10:128:31 | Array |

View File

@@ -7,13 +7,12 @@ import codeql.ruby.ApiGraphs
import codeql.ruby.dataflow.FlowSummary
import codeql.ruby.TaintTracking
import codeql.ruby.dataflow.internal.FlowSummaryImpl
import codeql.ruby.dataflow.internal.AccessPathSyntax
import codeql.ruby.frameworks.data.ModelsAsData
import TestUtilities.InlineFlowTest
import PathGraph
query predicate invalidSpecComponent(SummarizedCallable sc, string s, string c) {
(sc.propagatesFlowExt(s, _, _) or sc.propagatesFlowExt(_, s, _)) and
(sc.propagatesFlow(s, _, _) or sc.propagatesFlow(_, s, _)) and
Private::External::invalidSpecComponent(s, c)
}
@@ -24,7 +23,7 @@ private class SummarizedCallableIdentity extends SummarizedCallable {
override MethodCall getACall() { result.getMethodName() = this }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[0]" and
output = "ReturnValue" and
preservesValue = true
@@ -36,7 +35,7 @@ private class SummarizedCallableApplyBlock extends SummarizedCallable {
override MethodCall getACall() { result.getMethodName() = this }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[0]" and
output = "Argument[block].Parameter[0]" and
preservesValue = true
@@ -52,7 +51,7 @@ private class SummarizedCallableApplyLambda extends SummarizedCallable {
override MethodCall getACall() { result.getMethodName() = this }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
input = "Argument[1]" and
output = "Argument[0].Parameter[0]" and
preservesValue = true

View File

@@ -1,6 +1,6 @@
import codeql.ruby.AST
import codeql.ruby.DataFlow
import codeql.ruby.typetracking.TypeTracker
import codeql.ruby.typetracking.TypeTracking
class LocalSourceNode extends DataFlow::LocalSourceNode {
LocalSourceNode() { this.getLocation().getFile().getExtension() = "rb" }

View File

@@ -0,0 +1,8 @@
| lib/module.rb:2:3:3:5 | foo | mylib | M1 | foo | (x,y) | false | module.rb | |
| lib/module.rb:5:3:6:5 | self_foo | mylib | M1! | self_foo | (x,y) | false | module.rb | |
| lib/mylib.rb:4:3:5:5 | foo | mylib | A | foo | (x,y,key1:) | false | mylib.rb | |
| lib/mylib.rb:7:3:8:5 | bar | mylib | A | bar | (x) | false | mylib.rb | |
| lib/mylib.rb:10:3:11:5 | self_foo | mylib | A! | self_foo | (x,y) | false | mylib.rb | |
| lib/mylib.rb:19:5:20:7 | foo | mylib | A::ANested | foo | (x,y) | false | mylib.rb | |
| lib/other.rb:6:3:7:5 | foo | mylib | B | foo | (x,y) | false | other.rb | |
| other_lib/lib/other_gem.rb:3:9:4:11 | foo | other-lib | OtherLib::A | foo | (x,y) | false | other_gem.rb | |

View File

@@ -0,0 +1 @@
utils/modeleditor/FrameworkModeEndpoints.ql

View File

@@ -0,0 +1,7 @@
module M1
def foo(x, y)
end
def self.self_foo(x, y)
end
end

View File

@@ -0,0 +1,27 @@
require_relative "./other"
class A
def foo(x, y, key1:, **kwargs, &block)
end
def bar(x, *args)
end
def self.self_foo(x, y)
end
private
def private_1(x, y)
end
class ANested
def foo(x, y)
end
private
def private_2(x, y)
end
end
end

View File

@@ -0,0 +1,12 @@
require_relative "./module"
class B
include M1
def foo(x, y)
end
end
class C
extend M1
end

View File

@@ -0,0 +1,3 @@
Gem::Specification.new do |s|
s.name = "mylib"
end

View File

@@ -0,0 +1,6 @@
module OtherLib
class A
def foo(x, y)
end
end
end

View File

@@ -0,0 +1 @@
Gem::Specification.new("other-lib")