mirror of
https://github.com/github/codeql.git
synced 2026-04-30 11:15:13 +02:00
Merge rc/3.12 into main
This commit is contained in:
8
ruby/ql/consistency-queries/TypeTrackingConsistency.ql
Normal file
8
ruby/ql/consistency-queries/TypeTrackingConsistency.ql
Normal 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>
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
@@ -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)
|
||||
|
||||
@@ -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]"
|
||||
|
||||
@@ -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))) }
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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
@@ -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()
|
||||
}
|
||||
@@ -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`
|
||||
|
||||
@@ -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() }
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
/**
|
||||
|
||||
@@ -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`. */
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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).
|
||||
*/
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"]
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
7
ruby/ql/lib/codeql/ruby/typetracking/TypeTracking.qll
Normal file
7
ruby/ql/lib/codeql/ruby/typetracking/TypeTracking.qll
Normal 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>
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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
|
||||
|
||||
|
||||
17
ruby/ql/src/utils/modeleditor/ApplicationModeEndpoints.ql
Normal file
17
ruby/ql/src/utils/modeleditor/ApplicationModeEndpoints.ql
Normal 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
|
||||
15
ruby/ql/src/utils/modeleditor/FrameworkModeEndpoints.ql
Normal file
15
ruby/ql/src/utils/modeleditor/FrameworkModeEndpoints.ql
Normal 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()
|
||||
172
ruby/ql/src/utils/modeleditor/ModelEditor.qll
Normal file
172
ruby/ql/src/utils/modeleditor/ModelEditor.qll
Normal 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 }
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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 | |
|
||||
@@ -0,0 +1 @@
|
||||
utils/modeleditor/FrameworkModeEndpoints.ql
|
||||
7
ruby/ql/test/query-tests/utils/modeleditor/lib/module.rb
Normal file
7
ruby/ql/test/query-tests/utils/modeleditor/lib/module.rb
Normal file
@@ -0,0 +1,7 @@
|
||||
module M1
|
||||
def foo(x, y)
|
||||
end
|
||||
|
||||
def self.self_foo(x, y)
|
||||
end
|
||||
end
|
||||
27
ruby/ql/test/query-tests/utils/modeleditor/lib/mylib.rb
Normal file
27
ruby/ql/test/query-tests/utils/modeleditor/lib/mylib.rb
Normal 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
|
||||
12
ruby/ql/test/query-tests/utils/modeleditor/lib/other.rb
Normal file
12
ruby/ql/test/query-tests/utils/modeleditor/lib/other.rb
Normal file
@@ -0,0 +1,12 @@
|
||||
require_relative "./module"
|
||||
|
||||
class B
|
||||
include M1
|
||||
|
||||
def foo(x, y)
|
||||
end
|
||||
end
|
||||
|
||||
class C
|
||||
extend M1
|
||||
end
|
||||
3
ruby/ql/test/query-tests/utils/modeleditor/mylib.gemspec
Normal file
3
ruby/ql/test/query-tests/utils/modeleditor/mylib.gemspec
Normal file
@@ -0,0 +1,3 @@
|
||||
Gem::Specification.new do |s|
|
||||
s.name = "mylib"
|
||||
end
|
||||
@@ -0,0 +1,6 @@
|
||||
module OtherLib
|
||||
class A
|
||||
def foo(x, y)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1 @@
|
||||
Gem::Specification.new("other-lib")
|
||||
Reference in New Issue
Block a user