Ruby: Adopt shared type tracking library

This commit is contained in:
Tom Hvitved
2023-11-06 13:00:44 +01:00
parent 620e8dcb37
commit 6ce8e0510f
27 changed files with 696 additions and 593 deletions

View File

@@ -462,10 +462,6 @@
"ruby/ql/lib/codeql/ruby/security/internal/SensitiveDataHeuristics.qll", "ruby/ql/lib/codeql/ruby/security/internal/SensitiveDataHeuristics.qll",
"swift/ql/lib/codeql/swift/security/internal/SensitiveDataHeuristics.qll" "swift/ql/lib/codeql/swift/security/internal/SensitiveDataHeuristics.qll"
], ],
"TypeTracker": [
"python/ql/lib/semmle/python/dataflow/new/internal/TypeTracker.qll",
"ruby/ql/lib/codeql/ruby/typetracking/TypeTracker.qll"
],
"SummaryTypeTracker": [ "SummaryTypeTracker": [
"python/ql/lib/semmle/python/dataflow/new/internal/SummaryTypeTracker.qll", "python/ql/lib/semmle/python/dataflow/new/internal/SummaryTypeTracker.qll",
"ruby/ql/lib/codeql/ruby/typetracking/internal/SummaryTypeTracker.qll" "ruby/ql/lib/codeql/ruby/typetracking/internal/SummaryTypeTracker.qll"
@@ -534,4 +530,4 @@
"python/ql/test/experimental/dataflow/model-summaries/InlineTaintTest.ext.yml", "python/ql/test/experimental/dataflow/model-summaries/InlineTaintTest.ext.yml",
"python/ql/test/experimental/dataflow/model-summaries/NormalDataflowTest.ext.yml" "python/ql/test/experimental/dataflow/model-summaries/NormalDataflowTest.ext.yml"
] ]
} }

View File

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

View File

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

View File

@@ -2,7 +2,7 @@
import codeql.ruby.AST import codeql.ruby.AST
private import codeql.ruby.CFG private import codeql.ruby.CFG
private import codeql.ruby.typetracking.TypeTracker private import codeql.ruby.typetracking.TypeTracking
import codeql.ruby.DataFlow import codeql.ruby.DataFlow
private import internal.FlowSummaryImpl as Impl private import internal.FlowSummaryImpl as Impl
private import internal.DataFlowDispatch private import internal.DataFlowDispatch

View File

@@ -1,8 +1,7 @@
private import codeql.ruby.AST private import codeql.ruby.AST
private import codeql.ruby.CFG private import codeql.ruby.CFG
private import DataFlowPrivate private import DataFlowPrivate
private import codeql.ruby.typetracking.TypeTracker private import codeql.ruby.typetracking.internal.TypeTrackingImpl
private import codeql.ruby.typetracking.TypeTrackerSpecific as TypeTrackerSpecific
private import codeql.ruby.ast.internal.Module private import codeql.ruby.ast.internal.Module
private import FlowSummaryImpl as FlowSummaryImpl private import FlowSummaryImpl as FlowSummaryImpl
private import FlowSummaryImplSpecific as FlowSummaryImplSpecific private import FlowSummaryImplSpecific as FlowSummaryImplSpecific
@@ -657,7 +656,7 @@ private module TrackInstanceInput implements CallGraphConstruction::InputSig {
predicate stepNoCall(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, StepSummary summary) { 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 // We exclude steps into `self` parameters. For those, we instead rely on the type of
// the enclosing module // the enclosing module
StepSummary::smallstepNoCall(nodeFrom, nodeTo, summary) and smallStepNoCall(nodeFrom, nodeTo, summary) and
isNotSelf(nodeTo) isNotSelf(nodeTo)
or or
// We exclude steps into type checked variables. For those, we instead rely on the // We exclude steps into type checked variables. For those, we instead rely on the
@@ -667,7 +666,7 @@ private module TrackInstanceInput implements CallGraphConstruction::InputSig {
} }
predicate stepCall(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, StepSummary summary) { predicate stepCall(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, StepSummary summary) {
StepSummary::smallstepCall(nodeFrom, nodeTo, summary) smallStepCall(nodeFrom, nodeTo, summary)
} }
class StateProj = Unit; class StateProj = Unit;
@@ -941,7 +940,7 @@ private module TrackSingletonMethodOnInstanceInput implements CallGraphConstruct
RelevantCall call, DataFlow::Node arg, DataFlow::ParameterNode p, RelevantCall call, DataFlow::Node arg, DataFlow::ParameterNode p,
CfgNodes::ExprCfgNode nodeFromPreExpr CfgNodes::ExprCfgNode nodeFromPreExpr
| |
TypeTrackerSpecific::callStep(call, arg, p) and callStep(call, arg, p) and
nodeTo.getPreUpdateNode() = arg and nodeTo.getPreUpdateNode() = arg and
summary.toString() = "return" and summary.toString() = "return" and
( (
@@ -965,13 +964,13 @@ private module TrackSingletonMethodOnInstanceInput implements CallGraphConstruct
} }
predicate stepNoCall(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, StepSummary summary) { predicate stepNoCall(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, StepSummary summary) {
StepSummary::smallstepNoCall(nodeFrom, nodeTo, summary) smallStepNoCall(nodeFrom, nodeTo, summary)
or or
localFlowStep(nodeFrom, nodeTo, summary) localFlowStep(nodeFrom, nodeTo, summary)
} }
predicate stepCall(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, StepSummary summary) { predicate stepCall(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, StepSummary summary) {
StepSummary::smallstepCall(nodeFrom, nodeTo, summary) smallStepCall(nodeFrom, nodeTo, summary)
or or
paramReturnFlow(nodeFrom, nodeTo, summary) paramReturnFlow(nodeFrom, nodeTo, summary)
} }

View File

@@ -113,7 +113,7 @@ module LocalFlow {
SsaImpl::lastRefBeforeRedefExt(def, bb, i, next.getDefinitionExt()) and SsaImpl::lastRefBeforeRedefExt(def, bb, i, next.getDefinitionExt()) and
exprFrom = bb.getNode(i) and exprFrom = bb.getNode(i) and
exprFrom.getExpr() instanceof VariableReadAccess and exprFrom.getExpr() instanceof VariableReadAccess and
exprFrom = [nodeFrom.asExpr(), nodeFrom.(PostUpdateNode).getPreUpdateNode().asExpr()] exprFrom = [nodeFrom.asExpr(), nodeFrom.(PostUpdateNodeImpl).getPreUpdateNode().asExpr()]
) )
} }
@@ -159,7 +159,7 @@ module LocalFlow {
firstReadExt(def, nodeTo.asExpr()) firstReadExt(def, nodeTo.asExpr())
or or
// Flow from post-update read to next read // Flow from post-update read to next read
localSsaFlowStepUseUse(def, nodeFrom.(PostUpdateNode).getPreUpdateNode(), nodeTo) localSsaFlowStepUseUse(def, nodeFrom.(PostUpdateNodeImpl).getPreUpdateNode(), nodeTo)
or or
// Flow into phi (read) SSA definition node from def // Flow into phi (read) SSA definition node from def
localFlowSsaInputFromDef(nodeFrom, def, nodeTo) localFlowSsaInputFromDef(nodeFrom, def, nodeTo)
@@ -456,7 +456,7 @@ private predicate splatArgumentAt(CfgNodes::ExprNodes::CallCfgNode c, int pos) {
/** A collection of cached types and predicates to be evaluated in the same stage. */ /** A collection of cached types and predicates to be evaluated in the same stage. */
cached cached
private module Cached { private module Cached {
private import codeql.ruby.typetracking.TypeTrackerSpecific as TypeTrackerSpecific private import codeql.ruby.typetracking.internal.TypeTrackingImpl
cached cached
newtype TNode = newtype TNode =
@@ -575,14 +575,10 @@ private module Cached {
VariableCapture::flowInsensitiveStep(nodeFrom, nodeTo) VariableCapture::flowInsensitiveStep(nodeFrom, nodeTo)
} }
/** Holds if `n` wraps an SSA definition without ingoing flow. */
private predicate entrySsaDefinition(SsaDefinitionExtNode n) { private predicate entrySsaDefinition(SsaDefinitionExtNode n) {
n = LocalFlow::getParameterDefNode(_) n.getDefinitionExt() =
or any(SsaImpl::WriteDefinition def | not def.(Ssa::WriteDefinition).assigns(_))
exists(SsaImpl::DefinitionExt def | def = n.getDefinitionExt() |
def instanceof Ssa::SelfDefinition
or
def instanceof Ssa::CapturedEntryDefinition
)
} }
pragma[nomagic] pragma[nomagic]
@@ -599,6 +595,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 cached
predicate isLocalSourceNode(Node n) { predicate isLocalSourceNode(Node n) {
n instanceof TSourceParameterNode n instanceof TSourceParameterNode
@@ -614,11 +620,9 @@ private module Cached {
entrySsaDefinition(n) and entrySsaDefinition(n) and
not LocalFlow::localFlowSsaParamInput(_, n) not LocalFlow::localFlowSsaParamInput(_, n)
or or
TypeTrackerSpecific::basicStoreStep(_, n, _) isStoreTargetNode(n)
or or
TypeTrackerSpecific::basicLoadStep(_, n, _) TypeTrackingInput::loadStep(_, n, _)
or
TypeTrackerSpecific::basicLoadStoreStep(_, n, _, _)
} }
cached cached
@@ -633,7 +637,7 @@ private module Cached {
TElementContentOfTypeContent(string type, Boolean includeUnknown) { TElementContentOfTypeContent(string type, Boolean includeUnknown) {
type = any(Content::KnownElementContent content).getIndex().getValueType() type = any(Content::KnownElementContent content).getIndex().getValueType()
} or } or
TNoContentSet() // Only used by type-tracking deprecated TNoContentSet() // Only used by type-tracking
cached cached
class TContentSet = class TContentSet =

View File

@@ -2,10 +2,9 @@ private import codeql.ruby.AST
private import DataFlowDispatch private import DataFlowDispatch
private import DataFlowPrivate private import DataFlowPrivate
private import codeql.ruby.CFG 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 codeql.ruby.dataflow.SSA
private import FlowSummaryImpl as FlowSummaryImpl private import FlowSummaryImpl as FlowSummaryImpl
private import SsaImpl as SsaImpl
private import codeql.ruby.ApiGraphs private import codeql.ruby.ApiGraphs
/** /**
@@ -245,7 +244,7 @@ class LocalSourceNode extends Node {
/** Holds if this `LocalSourceNode` can flow to `nodeTo` in one or more local flow steps. */ /** Holds if this `LocalSourceNode` can flow to `nodeTo` in one or more local flow steps. */
pragma[inline] 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. * Gets a node that this node may flow to using one heap and/or interprocedural step.
@@ -261,14 +260,14 @@ class LocalSourceNode extends Node {
* See `TypeBackTracker` for more details about how to use this. * See `TypeBackTracker` for more details about how to use this.
*/ */
pragma[inline] 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 * Gets a node to which data may flow from this node in zero or
* more local data-flow steps. * more local data-flow steps.
*/ */
pragma[inline] pragma[inline]
Node getALocalUse() { hasLocalSource(result, this) } Node getALocalUse() { flowsTo(this, result) }
/** Gets a method call where this node flows to the receiver. */ /** Gets a method call where this node flows to the receiver. */
CallNode getAMethodCall() { Cached::hasMethodCall(this, result, _) } CallNode getAMethodCall() { Cached::hasMethodCall(this, result, _) }
@@ -354,21 +353,21 @@ class PostUpdateNode extends Node instanceof PostUpdateNodeImpl {
Node getPreUpdateNode() { result = super.getPreUpdateNode() } Node getPreUpdateNode() { result = super.getPreUpdateNode() }
} }
/** 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 cached
private module 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 cached
predicate hasMethodCall(LocalSourceNode source, CallNode call, string name) { predicate hasMethodCall(LocalSourceNode source, CallNode call, string name) {
source.flowsTo(call.getReceiver()) and source.flowsTo(call.getReceiver()) and

View File

@@ -5,7 +5,7 @@
private import codeql.ruby.Concepts private import codeql.ruby.Concepts
private import codeql.ruby.AST private import codeql.ruby.AST
private import codeql.ruby.DataFlow 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.ApiGraphs
private import codeql.ruby.controlflow.CfgNodes as CfgNodes private import codeql.ruby.controlflow.CfgNodes as CfgNodes

View File

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

View File

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

View File

@@ -17,7 +17,7 @@ private import codeql.ruby.AST as Ast
private import codeql.ruby.CFG private import codeql.ruby.CFG
private import codeql.ruby.DataFlow private import codeql.ruby.DataFlow
private import codeql.ruby.controlflow.CfgNodes 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.ApiGraphs
private import codeql.ruby.Concepts private import codeql.ruby.Concepts
private import codeql.ruby.dataflow.internal.DataFlowPrivate as DataFlowPrivate 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, * Provides a version of type tracking where we first prune for reachable nodes,
* before doing the type tracking computation. * before doing the type tracking computation.
*/ */
private module TypeTrack<TypeTrackInputSig Input> { private module PrunedTypeTrack<TypeTrackInputSig Input> {
private predicate additionalStep( private predicate additionalStep(
DataFlow::LocalSourceNode nodeFrom, DataFlow::LocalSourceNode nodeTo DataFlow::LocalSourceNode nodeFrom, DataFlow::LocalSourceNode nodeTo
) { ) {
@@ -130,10 +130,10 @@ private module TypeTrack<TypeTrackInputSig Input> {
TypeTracker t, DataFlow::LocalSourceNode nodeFrom, DataFlow::LocalSourceNode nodeTo TypeTracker t, DataFlow::LocalSourceNode nodeFrom, DataFlow::LocalSourceNode nodeTo
) { ) {
exists(StepSummary summary | exists(StepSummary summary |
StepSummary::step(nodeFrom, nodeTo, summary) and step(nodeFrom, nodeTo, summary) and
reached(nodeFrom, t) and reached(nodeFrom, t) and
reached(nodeTo, result) and reached(nodeTo, result) and
result = t.append(summary) result = append(t, summary)
) )
or or
additionalStep(nodeFrom, nodeTo) and 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, * 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. * 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`. */ /** Holds if `strConst` flows to a regex compilation (tracked by `t`), where the resulting regular expression is stored in `reg`. */
pragma[nomagic] 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. * 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. * 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. */ /** Gets a node that references a regular expression. */
private DataFlow::LocalSourceNode trackRegexpType(TypeTracker t) { private DataFlow::LocalSourceNode trackRegexpType(TypeTracker t) {

View File

@@ -7,7 +7,7 @@
private import codeql.ruby.AST private import codeql.ruby.AST
private import codeql.ruby.DataFlow private import codeql.ruby.DataFlow
private import codeql.ruby.Concepts 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.Files
private import codeql.ruby.frameworks.core.IO private import codeql.ruby.frameworks.core.IO

View File

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

View File

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

View File

@@ -8,6 +8,7 @@ private import ruby
private import codeql.ruby.ApiGraphs private import codeql.ruby.ApiGraphs
private import codeql.ruby.frameworks.core.Gem::Gem as Gem private import codeql.ruby.frameworks.core.Gem::Gem as Gem
private import codeql.ruby.Concepts as Concepts 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. * 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`. */ /** Gets a node that is eventually executed as code at `codeExec`. */
DataFlow::Node getANodeExecutedAsCode(Concepts::CodeExecution 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`. */ /** Gets a node that is eventually executed as code at `codeExec`, type-tracked with `t`. */
private DataFlow::LocalSourceNode getANodeExecutedAsCode( private DataFlow::LocalSourceNode getANodeExecutedAsCode(
TypeTracker::TypeBackTracker t, Concepts::CodeExecution codeExec TypeBackTracker t, Concepts::CodeExecution codeExec
) { ) {
t.start() and t.start() and
result = codeExec.getCode().getALocalSource() 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 codeExec.runsArbitraryCode() // methods like `Object.send` is benign here, because of the string-construction the attacker cannot control the entire method name
or or
exists(TypeTracker::TypeBackTracker t2 | exists(TypeBackTracker t2 | result = getANodeExecutedAsCode(t2, codeExec).backtrack(t2, t))
result = getANodeExecutedAsCode(t2, codeExec).backtrack(t2, t)
)
} }
/** /**

View File

@@ -7,6 +7,7 @@
private import ruby private import ruby
private import codeql.ruby.ApiGraphs private import codeql.ruby.ApiGraphs
private import codeql.ruby.frameworks.core.Gem::Gem as Gem 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. * 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`. */ /** Gets a node that eventually ends up in the XSS `sink`. */
private DataFlow::Node getANodeThatEndsInXssSink(ReflectedXss::Sink 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`. */ /** Gets a node that is eventually ends up in the XSS `sink`, type-tracked with `t`. */
private DataFlow::LocalSourceNode getANodeThatEndsInXssSink( private DataFlow::LocalSourceNode getANodeThatEndsInXssSink(
TypeTracker::TypeBackTracker t, ReflectedXss::Sink sink TypeBackTracker t, ReflectedXss::Sink sink
) { ) {
t.start() and t.start() and
result = sink.getALocalSource() result = sink.getALocalSource()
or or
exists(TypeTracker::TypeBackTracker t2 | exists(TypeBackTracker t2 | result = getANodeThatEndsInXssSink(t2, sink).backtrack(t2, t))
result = getANodeThatEndsInXssSink(t2, sink).backtrack(t2, t)
)
} }
/** /**

View File

@@ -9,6 +9,7 @@ private import codeql.ruby.DataFlow
private import codeql.ruby.ApiGraphs private import codeql.ruby.ApiGraphs
private import codeql.ruby.frameworks.core.Gem::Gem as Gem private import codeql.ruby.frameworks.core.Gem::Gem as Gem
private import codeql.ruby.Concepts as Concepts 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. * 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` */ /** Holds if the string constructed at `source` is executed at `shellExec` */
predicate isUsedAsShellCommand(DataFlow::Node source, Concepts::SystemCommandExecution 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( private DataFlow::LocalSourceNode backtrackShellExec(
TypeTracker::TypeBackTracker t, Concepts::SystemCommandExecution shellExec TypeBackTracker t, Concepts::SystemCommandExecution shellExec
) { ) {
t.start() and t.start() and
result = any(DataFlow::Node n | shellExec.isShellInterpreted(n)).getALocalSource() result = any(DataFlow::Node n | shellExec.isShellInterpreted(n)).getALocalSource()
or or
exists(TypeTracker::TypeBackTracker t2 | exists(TypeBackTracker t2 | result = backtrackShellExec(t2, shellExec).backtrack(t2, t))
result = backtrackShellExec(t2, shellExec).backtrack(t2, t)
)
} }
/** /**

View File

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

View File

@@ -8,22 +8,22 @@ private module Cached {
* A description of a step on an inter-procedural data flow path. * A description of a step on an inter-procedural data flow path.
*/ */
cached cached
newtype TStepSummary = deprecated newtype TStepSummary =
LevelStep() or LevelStep() or
CallStep() or CallStep() or
ReturnStep() or ReturnStep() or
StoreStep(TypeTrackerContent content) { basicStoreStep(_, _, content) } or deprecated StoreStep(TypeTrackerContent content) { basicStoreStep(_, _, content) } or
LoadStep(TypeTrackerContent content) { basicLoadStep(_, _, content) } or deprecated LoadStep(TypeTrackerContent content) { basicLoadStep(_, _, content) } or
LoadStoreStep(TypeTrackerContent load, TypeTrackerContent store) { deprecated LoadStoreStep(TypeTrackerContent load, TypeTrackerContent store) {
basicLoadStoreStep(_, _, load, store) basicLoadStoreStep(_, _, load, store)
} or } or
WithContent(ContentFilter filter) { basicWithContentStep(_, _, filter) } or deprecated WithContent(ContentFilter filter) { basicWithContentStep(_, _, filter) } or
WithoutContent(ContentFilter filter) { basicWithoutContentStep(_, _, filter) } or deprecated WithoutContent(ContentFilter filter) { basicWithoutContentStep(_, _, filter) } or
JumpStep() JumpStep()
cached cached
newtype TTypeTracker = deprecated newtype TTypeTracker =
MkTypeTracker(Boolean hasCall, OptionalTypeTrackerContent content) { deprecated MkTypeTracker(Boolean hasCall, OptionalTypeTrackerContent content) {
content = noContent() content = noContent()
or or
// Restrict `content` to those that might eventually match a load. // Restrict `content` to those that might eventually match a load.
@@ -40,8 +40,8 @@ private module Cached {
} }
cached cached
newtype TTypeBackTracker = deprecated newtype TTypeBackTracker =
MkTypeBackTracker(Boolean hasReturn, OptionalTypeTrackerContent content) { deprecated MkTypeBackTracker(Boolean hasReturn, OptionalTypeTrackerContent content) {
content = noContent() content = noContent()
or or
// As in MkTypeTracker, restrict `content` to those that might eventually match a store. // As in MkTypeTracker, restrict `content` to those that might eventually match a store.
@@ -57,11 +57,13 @@ private module Cached {
/** Gets a type tracker with no content and the call bit set to the given value. */ /** Gets a type tracker with no content and the call bit set to the given value. */
cached 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`. */ /** Gets the summary resulting from appending `step` to type-tracking summary `tt`. */
cached cached
TypeTracker append(TypeTracker tt, StepSummary step) { deprecated TypeTracker append(TypeTracker tt, StepSummary step) {
exists(Boolean hasCall, OptionalTypeTrackerContent currentContents | exists(Boolean hasCall, OptionalTypeTrackerContent currentContents |
tt = MkTypeTracker(hasCall, currentContents) tt = MkTypeTracker(hasCall, currentContents)
| |
@@ -108,13 +110,13 @@ private module Cached {
} }
pragma[nomagic] pragma[nomagic]
private TypeBackTracker noContentTypeBackTracker(boolean hasReturn) { deprecated private TypeBackTracker noContentTypeBackTracker(boolean hasReturn) {
result = MkTypeBackTracker(hasReturn, noContent()) result = MkTypeBackTracker(hasReturn, noContent())
} }
/** Gets the summary resulting from prepending `step` to this type-tracking summary. */ /** Gets the summary resulting from prepending `step` to this type-tracking summary. */
cached cached
TypeBackTracker prepend(TypeBackTracker tbt, StepSummary step) { deprecated TypeBackTracker prepend(TypeBackTracker tbt, StepSummary step) {
exists(Boolean hasReturn, OptionalTypeTrackerContent content | exists(Boolean hasReturn, OptionalTypeTrackerContent content |
tbt = MkTypeBackTracker(hasReturn, content) tbt = MkTypeBackTracker(hasReturn, content)
| |
@@ -167,7 +169,9 @@ private module Cached {
* Steps contained in this predicate should _not_ depend on the call graph. * Steps contained in this predicate should _not_ depend on the call graph.
*/ */
cached 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)) exists(Node mid | nodeFrom.flowsTo(mid) and smallstepNoCall(mid, nodeTo, summary))
} }
@@ -176,12 +180,14 @@ private module Cached {
* inter-procedural step from `nodeFrom` to `nodeTo`. * inter-procedural step from `nodeFrom` to `nodeTo`.
*/ */
cached 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)) exists(Node mid | nodeFrom.flowsTo(mid) and smallstepCall(mid, nodeTo, summary))
} }
cached cached
predicate smallstepNoCall(Node nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) { deprecated predicate smallstepNoCall(Node nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) {
jumpStep(nodeFrom, nodeTo) and jumpStep(nodeFrom, nodeTo) and
summary = JumpStep() summary = JumpStep()
or or
@@ -210,7 +216,7 @@ private module Cached {
} }
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() callStep(nodeFrom, nodeTo) and summary = CallStep()
or or
returnStep(nodeFrom, nodeTo) and returnStep(nodeFrom, nodeTo) and
@@ -223,25 +229,27 @@ private module Cached {
private import 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) stepNoCall(nodeFrom, nodeTo, summary)
or or
stepCall(nodeFrom, nodeTo, summary) stepCall(nodeFrom, nodeTo, summary)
} }
pragma[nomagic] pragma[nomagic]
private predicate stepProj(TypeTrackingNode nodeFrom, StepSummary summary) { deprecated private predicate stepProj(TypeTrackingNode nodeFrom, StepSummary summary) {
step(nodeFrom, _, 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) smallstepNoCall(nodeFrom, nodeTo, summary)
or or
smallstepCall(nodeFrom, nodeTo, summary) smallstepCall(nodeFrom, nodeTo, summary)
} }
pragma[nomagic] pragma[nomagic]
private predicate smallstepProj(Node nodeFrom, StepSummary summary) { deprecated private predicate smallstepProj(Node nodeFrom, StepSummary summary) {
smallstep(nodeFrom, _, summary) smallstep(nodeFrom, _, summary)
} }
@@ -270,7 +278,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 * 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. * 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 Node nodeFrom, TypeTrackingNode nodeTo, TypeTrackerContent content
) { ) {
exists(Node obj | nodeTo.flowsTo(obj) and basicStoreStep(nodeFrom, obj, content)) exists(Node obj | nodeTo.flowsTo(obj) and basicStoreStep(nodeFrom, obj, content))
@@ -279,7 +287,7 @@ private predicate flowsToStoreStep(
/** /**
* Holds if `loadContent` is loaded from `nodeFrom` and written to `storeContent` of `nodeTo`. * 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, Node nodeFrom, TypeTrackingNode nodeTo, TypeTrackerContent loadContent,
TypeTrackerContent storeContent TypeTrackerContent storeContent
) { ) {
@@ -293,7 +301,7 @@ private predicate flowsToLoadStoreStep(
* *
* A description of a step on an inter-procedural data flow path. * 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. */ /** Gets a textual representation of this step summary. */
string toString() { string toString() {
this instanceof LevelStep and result = "level" this instanceof LevelStep and result = "level"
@@ -316,7 +324,7 @@ class StepSummary extends TStepSummary {
} }
/** Provides predicates for updating step summaries (`StepSummary`s). */ /** Provides predicates for updating step summaries (`StepSummary`s). */
module StepSummary { deprecated module StepSummary {
predicate append = Cached::append/2; predicate append = Cached::append/2;
/** /**
@@ -437,7 +445,7 @@ module StepSummary {
* `t = t2.step(myType(t2), result)`. If you additionally want to track individual * `t = t2.step(myType(t2), result)`. If you additionally want to track individual
* intra-procedural steps, use `t = t2.smallstep(myCallback(t2), result)`. * intra-procedural steps, use `t = t2.smallstep(myCallback(t2), result)`.
*/ */
class TypeTracker extends TTypeTracker { deprecated class TypeTracker extends TTypeTracker {
Boolean hasCall; Boolean hasCall;
OptionalTypeTrackerContent content; OptionalTypeTrackerContent content;
@@ -565,7 +573,7 @@ class TypeTracker extends TTypeTracker {
} }
/** Provides predicates for implementing custom `TypeTracker`s. */ /** Provides predicates for implementing custom `TypeTracker`s. */
module TypeTracker { deprecated module TypeTracker {
/** /**
* Gets a valid end point of type tracking. * Gets a valid end point of type tracking.
*/ */
@@ -580,11 +588,11 @@ module TypeTracker {
} }
pragma[nomagic] pragma[nomagic]
private predicate backStepProj(TypeTrackingNode nodeTo, StepSummary summary) { deprecated private predicate backStepProj(TypeTrackingNode nodeTo, StepSummary summary) {
step(_, nodeTo, summary) step(_, nodeTo, summary)
} }
private predicate backSmallstepProj(TypeTrackingNode nodeTo, StepSummary summary) { deprecated private predicate backSmallstepProj(TypeTrackingNode nodeTo, StepSummary summary) {
smallstep(_, nodeTo, summary) smallstep(_, nodeTo, summary)
} }
@@ -618,7 +626,7 @@ private predicate backSmallstepProj(TypeTrackingNode nodeTo, StepSummary summary
* `t2 = t.step(result, myCallback(t2))`. If you additionally want to track individual * `t2 = t.step(result, myCallback(t2))`. If you additionally want to track individual
* intra-procedural steps, use `t2 = t.smallstep(result, myCallback(t2))`. * intra-procedural steps, use `t2 = t.smallstep(result, myCallback(t2))`.
*/ */
class TypeBackTracker extends TTypeBackTracker { deprecated class TypeBackTracker extends TTypeBackTracker {
Boolean hasReturn; Boolean hasReturn;
OptionalTypeTrackerContent content; OptionalTypeTrackerContent content;
@@ -747,7 +755,7 @@ class TypeBackTracker extends TTypeBackTracker {
} }
/** Provides predicates for implementing custom `TypeBackTracker`s. */ /** Provides predicates for implementing custom `TypeBackTracker`s. */
module TypeBackTracker { deprecated module TypeBackTracker {
/** /**
* Gets a valid end point of type back-tracking. * Gets a valid end point of type back-tracking.
*/ */
@@ -768,14 +776,14 @@ module TypeBackTracker {
* `stepCall` relation (`stepNoCall` not being recursive, can be join-ordered in the * `stepCall` relation (`stepNoCall` not being recursive, can be join-ordered in the
* same way as in `stepInlineLate`). * same way as in `stepInlineLate`).
*/ */
module CallGraphConstruction { deprecated module CallGraphConstruction {
/** The input to call graph construction. */ /** The input to call graph construction. */
signature module InputSig { signature module InputSig {
/** A state to track during type tracking. */ /** A state to track during type tracking. */
class State; class State;
/** Holds if type tracking should start at `start` in state `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`, * Holds if type tracking should use the step from `nodeFrom` to `nodeTo`,
@@ -784,7 +792,7 @@ module CallGraphConstruction {
* Implementing this predicate using `StepSummary::[small]stepNoCall` yields * Implementing this predicate using `StepSummary::[small]stepNoCall` yields
* standard type tracking. * 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`, * Holds if type tracking should use the step from `nodeFrom` to `nodeTo`,
@@ -793,7 +801,7 @@ module CallGraphConstruction {
* Implementing this predicate using `StepSummary::[small]stepCall` yields * Implementing this predicate using `StepSummary::[small]stepCall` yields
* standard type tracking. * 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. */ /** A projection of an element from the state space. */
class StateProj; class StateProj;
@@ -802,25 +810,25 @@ module CallGraphConstruction {
StateProj stateProj(State state); StateProj stateProj(State state);
/** Holds if type tracking should stop at `n` when we are tracking projected state `stateProj`. */ /** 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. */ /** Provides the `track` predicate for use in call graph construction. */
module Make<InputSig Input> { module Make<InputSig Input> {
pragma[nomagic] pragma[nomagic]
private predicate stepNoCallProj(Node nodeFrom, StepSummary summary) { deprecated private predicate stepNoCallProj(Node nodeFrom, StepSummary summary) {
Input::stepNoCall(nodeFrom, _, summary) Input::stepNoCall(nodeFrom, _, summary)
} }
pragma[nomagic] pragma[nomagic]
private predicate stepCallProj(Node nodeFrom, StepSummary summary) { deprecated private predicate stepCallProj(Node nodeFrom, StepSummary summary) {
Input::stepCall(nodeFrom, _, summary) Input::stepCall(nodeFrom, _, summary)
} }
bindingset[nodeFrom, t] bindingset[nodeFrom, t]
pragma[inline_late] pragma[inline_late]
pragma[noopt] pragma[noopt]
private TypeTracker stepNoCallInlineLate( deprecated private TypeTracker stepNoCallInlineLate(
TypeTracker t, TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo TypeTracker t, TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo
) { ) {
exists(StepSummary summary | exists(StepSummary summary |
@@ -837,7 +845,7 @@ module CallGraphConstruction {
} }
pragma[nomagic] 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) t.start() and Input::start(result, state)
or or
exists(Input::StateProj stateProj | exists(Input::StateProj stateProj |
@@ -855,12 +863,12 @@ module CallGraphConstruction {
bindingset[t, summary] bindingset[t, summary]
pragma[inline_late] pragma[inline_late]
private TypeTracker appendInlineLate(TypeTracker t, StepSummary summary) { deprecated private TypeTracker appendInlineLate(TypeTracker t, StepSummary summary) {
result = t.append(summary) result = t.append(summary)
} }
pragma[nomagic] 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 | exists(TypeTracker t2 |
// non-linear recursion // non-linear recursion
result = track(state, t2) and result = track(state, t2) and
@@ -871,7 +879,7 @@ module CallGraphConstruction {
/** Gets a node that can be reached from _some_ start node in state `state`. */ /** Gets a node that can be reached from _some_ start node in state `state`. */
pragma[nomagic] 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. */ /** A simple version of `CallGraphConstruction` that uses standard type tracking. */
@@ -882,15 +890,15 @@ module CallGraphConstruction {
class State; class State;
/** Holds if type tracking should start at `start` in state `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`. */ /** 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. */ /** Provides the `track` predicate for use in call graph construction. */
module Make<InputSig Input> { module Make<InputSig Input> {
private module I implements CallGraphConstruction::InputSig { deprecated private module I implements CallGraphConstruction::InputSig {
private import codeql.util.Unit private import codeql.util.Unit
class State = Input::State; class State = Input::State;
@@ -915,7 +923,7 @@ module CallGraphConstruction {
} }
} }
import CallGraphConstruction::Make<I> deprecated import CallGraphConstruction::Make<I>
} }
} }
} }

View File

@@ -9,21 +9,19 @@ private import codeql.ruby.dataflow.internal.DataFlowDispatch as DataFlowDispatc
private import codeql.ruby.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl private import codeql.ruby.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
private import codeql.ruby.dataflow.internal.FlowSummaryImplSpecific as FlowSummaryImplSpecific private import codeql.ruby.dataflow.internal.FlowSummaryImplSpecific as FlowSummaryImplSpecific
private import codeql.ruby.dataflow.internal.AccessPathSyntax private import codeql.ruby.dataflow.internal.AccessPathSyntax
private import internal.TypeTrackingImpl as TypeTrackingImpl
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; deprecated class TypeTrackerContent = DataFlowPublic::ContentSet;
private module SCS = SummaryComponentStack;
private module SC = SummaryComponent;
/** /**
* An optional content set, that is, a `ContentSet` or the special "no content set" value. * 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. */ /** Gets a textual representation of this content set. */
string toString() { string toString() {
this instanceof DataFlowPrivate::TNoContentSet and this instanceof DataFlowPrivate::TNoContentSet and
@@ -33,178 +31,45 @@ class OptionalTypeTrackerContent extends DataFlowPrivate::TOptionalContentSet {
} }
} }
private newtype TContentFilter = MkElementFilter()
/** /**
* A label to use for `WithContent` and `WithoutContent` steps, restricting * A label to use for `WithContent` and `WithoutContent` steps, restricting
* which `ContentSet` may pass through. * which `ContentSet` may pass through.
*/ */
class ContentFilter extends TContentFilter { deprecated class ContentFilter = TypeTrackingImpl::TypeTrackingInput::ContentFilter;
/** 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
}
}
/** Module for getting `ContentFilter` values. */ /** Module for getting `ContentFilter` values. */
module ContentFilter { deprecated module ContentFilter {
/** Gets the filter that only allow element contents. */ /** 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`. * Holds if a value stored with `storeContents` can be read back with `loadContents`.
*/ */
pragma[inline] pragma[inline]
predicate compatibleContents(TypeTrackerContent storeContents, TypeTrackerContent loadContents) { deprecated predicate compatibleContents(
TypeTrackerContent storeContents, TypeTrackerContent loadContents
) {
storeContents.getAStoreContent() = loadContents.getAReadContent() storeContents.getAStoreContent() = loadContents.getAReadContent()
} }
/** Gets the "no content set" value to use for a type tracker not inside any content. */ /** 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` */ /** 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. * Holds if data can flow from `node1` to `node2` in a way that discards call contexts.
*/ */
predicate jumpStep = DataFlowPrivate::jumpStep/2; deprecated predicate jumpStep = TypeTrackingImpl::TypeTrackingInput::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)
)
}
/** Holds if there is a level step from `nodeFrom` to `nodeTo`, which may depend on the call graph. */ /** Holds if there is a level step from `nodeFrom` to `nodeTo`, which may depend on the call graph. */
pragma[nomagic] deprecated predicate levelStepCall = TypeTrackingImpl::TypeTrackingInput::levelStepCall/2;
predicate levelStepCall(Node nodeFrom, Node nodeTo) {
exists(DataFlowPublic::ParameterNode param |
flowThrough(param) and
callStepNoInitialize(nodeTo.asExpr(), nodeFrom, param)
)
}
/** Holds if there is a level step from `nodeFrom` to `nodeTo`, which does not depend on the call graph. */ /** Holds if there is a level step from `nodeFrom` to `nodeTo`, which does not depend on the call graph. */
pragma[nomagic] deprecated predicate levelStepNoCall = TypeTrackingImpl::TypeTrackingInput::levelStepNoCall/2;
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)
)
}
/** /**
* Holds if `nodeFrom` steps to `nodeTo` by being passed as a parameter in a call. * Holds if `nodeFrom` steps to `nodeTo` by being passed as a parameter in a call.
@@ -213,7 +78,7 @@ predicate callStep(ExprNodes::CallCfgNode call, Node arg, DataFlowPrivate::Param
* recursion (or, at best, terrible performance), since identifying calls to library * recursion (or, at best, terrible performance), since identifying calls to library
* methods is done using API graphs (which uses type tracking). * 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. * Holds if `nodeFrom` steps to `nodeTo` by being returned from a call.
@@ -222,17 +87,7 @@ predicate callStep(Node nodeFrom, Node nodeTo) { callStep(_, nodeFrom, nodeTo) }
* recursion (or, at best, terrible performance), since identifying calls to library * recursion (or, at best, terrible performance), since identifying calls to library
* methods is done using API graphs (which uses type tracking). * methods is done using API graphs (which uses type tracking).
*/ */
predicate returnStep(Node nodeFrom, Node nodeTo) { deprecated predicate returnStep = TypeTrackingImpl::TypeTrackingInput::returnStep/2;
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 * Holds if `nodeFrom` is being written to the `contents` of the object
@@ -265,211 +120,25 @@ predicate returnStep(Node nodeFrom, Node nodeTo) {
* to `z` inside `bar`, even though this content write happens _after_ `bar` is * to `z` inside `bar`, even though this content write happens _after_ `bar` is
* called. * called.
*/ */
predicate basicStoreStep(Node nodeFrom, Node nodeTo, DataFlow::ContentSet contents) { deprecated predicate basicStoreStep = TypeTrackingImpl::TypeTrackingInput::storeStep/3;
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)
}
/** /**
* Holds if `nodeTo` is the result of accessing the `content` content of `nodeFrom`. * Holds if `nodeTo` is the result of accessing the `content` content of `nodeFrom`.
*/ */
predicate basicLoadStep(Node nodeFrom, Node nodeTo, DataFlow::ContentSet contents) { deprecated predicate basicLoadStep = TypeTrackingImpl::TypeTrackingInput::loadStep/3;
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)
}
/** /**
* Holds if the `loadContent` of `nodeFrom` is stored in the `storeContent` of `nodeTo`. * Holds if the `loadContent` of `nodeFrom` is stored in the `storeContent` of `nodeTo`.
*/ */
predicate basicLoadStoreStep( deprecated predicate basicLoadStoreStep = TypeTrackingImpl::TypeTrackingInput::loadStoreStep/4;
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)
)
}
/** /**
* Holds if type-tracking should step from `nodeFrom` to `nodeTo` but block flow of contents matched by `filter` through here. * 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) { deprecated predicate basicWithoutContentStep =
TypeTrackerSummaryFlow::basicWithoutContentStep(nodeFrom, nodeTo, filter) TypeTrackingImpl::TypeTrackingInput::withoutContentStep/3;
}
/** /**
* Holds if type-tracking should step from `nodeFrom` to `nodeTo` if inside a content matched by `filter`. * 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) { deprecated predicate basicWithContentStep = TypeTrackingImpl::TypeTrackingInput::withContentStep/3;
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>;

View File

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

View File

@@ -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 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
private import codeql.ruby.dataflow.internal.FlowSummaryImplSpecific as FlowSummaryImplSpecific
private import codeql.ruby.dataflow.internal.AccessPathSyntax
/** 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 TypeTrackerContent = DataFlowPublic::ContentSet;
class TypeTrackerContentFilter = TypeTrackingInput::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 = FlowSummary::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 = FlowSummary::SummaryComponent::parameter(apos) and
DataFlowDispatch::parameterMatch(ppos, apos) and
result
.(DataFlowPrivate::ParameterNodeImpl)
.isSourceParameterOf(callable.asExpr().getExpr(), ppos)
)
}
Node returnOf(Node callable, SummaryComponent return) {
return = FlowSummary::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>;
private newtype TContentFilter = MkElementFilter()
module TypeTrackingInput implements Shared::TypeTrackingInput {
class Node = DataFlowPublic::Node;
class LocalSourceNode = DataFlowPublic::LocalSourceNode;
class Content = DataFlowPublic::ContentSet;
/**
* A label to use for `WithContent` and `WithoutContent` steps, restricting
* which `ContentSet` may pass through.
*/
class ContentFilter extends TContentFilter {
/** Gets a string representation of this content filter. */
string toString() { this = MkElementFilter() and result = "elements" }
/** Gets the content of a type-tracker that matches this filter. */
Content getAMatchingContent() {
this = MkElementFilter() and
result.getAReadContent() instanceof DataFlow::Content::ElementContent
}
}
/**
* Holds if a value stored with `storeContents` can be read back with `loadContents`.
*/
pragma[inline]
predicate compatibleContents(Content storeContents, Content loadContents) {
storeContents.getAStoreContent() = loadContents.getAReadContent()
}
/** Holds if there is a simple local flow step from `nodeFrom` to `nodeTo` */
predicate simpleLocalSmallStep = DataFlowPrivate::localFlowStepTypeTracker/2;
/** Holds if there is a level step from `nodeFrom` to `nodeTo`, which does not depend on the call graph. */
pragma[nomagic]
predicate levelStepNoCall(Node nodeFrom, LocalSourceNode nodeTo) {
TypeTrackerSummaryFlow::levelStepNoCall(nodeFrom, nodeTo)
or
localFieldStep(nodeFrom, nodeTo)
}
/** Holds if there is a level step from `nodeFrom` to `nodeTo`, which may depend on the call graph. */
pragma[nomagic]
predicate levelStepCall(Node nodeFrom, LocalSourceNode nodeTo) {
exists(DataFlowPublic::ParameterNode param |
flowThrough(param) and
callStepNoInitialize(nodeTo.asExpr(), nodeFrom, param)
)
}
/**
* Holds if `nodeFrom` steps to `nodeTo` by being passed as a parameter in a call.
*
* Flow into summarized library methods is not included, as that will lead to negative
* recursion (or, at best, terrible performance), since identifying calls to library
* methods is done using API graphs (which uses type tracking).
*/
predicate callStep(Node nodeFrom, LocalSourceNode nodeTo) { callStep(_, nodeFrom, nodeTo) }
/**
* Holds if `nodeFrom` steps to `nodeTo` by being returned from a call.
*
* Flow out of summarized library methods is not included, as that will lead to negative
* recursion (or, at best, terrible performance), since identifying calls to library
* methods is done using API graphs (which uses type tracking).
*/
predicate returnStep(Node nodeFrom, LocalSourceNode nodeTo) {
exists(ExprNodes::CallCfgNode call |
nodeFrom instanceof DataFlowPrivate::ReturnNode and
not nodeFrom instanceof DataFlowPrivate::InitializeReturnNode and
nodeFrom.(DataFlowPrivate::NodeImpl).getCfgScope() = DataFlowDispatch::getTarget(call) and
// deliberately do not include `getInitializeTarget`, since calls to `new` should not
// get the return value from `initialize`. Any fields being set in the initializer
// will reach all reads via `callStep` and `localFieldStep`.
nodeTo.asExpr().getAstNode() = call.getAstNode()
)
}
/**
* Holds if `nodeFrom` is being written to the `contents` of the object
* in `nodeTo`.
*
* Note that the choice of `nodeTo` does not have to make sense
* "chronologically". All we care about is whether the `contents` of
* `nodeTo` can have a specific type, and the assumption is that if a specific
* type appears here, then any access of that particular content can yield
* something of that particular type.
*
* Thus, in an example such as
*
* ```rb
* def foo(y)
* x = Foo.new
* bar(x)
* x.content = y
* baz(x)
* end
*
* def bar(x)
* z = x.content
* end
* ```
* for the content write `x.content = y`, we will have `contents` being the
* literal string `"content"`, `nodeFrom` will be `y`, and `nodeTo` will be the
* `Foo` object created on the first line of the function. This means we will
* track the fact that `x.content` can have the type of `y` into the assignment
* to `z` inside `bar`, even though this content write happens _after_ `bar` is
* called.
*/
predicate storeStep(Node nodeFrom, Node nodeTo, Content contents) {
// TODO: support SetterMethodCall inside TuplePattern
exists(ExprNodes::MethodCallCfgNode call |
contents
.isSingleton(DataFlowPublic::Content::getAttributeName(call.getExpr()
.(SetterMethodCall)
.getTargetName())) and
nodeTo.(DataFlowPublic::PostUpdateNode).getPreUpdateNode().asExpr() = call.getReceiver() and
call.getExpr() instanceof SetterMethodCall and
call.getArgument(call.getNumberOfArguments() - 1) =
nodeFrom.(DataFlowPublic::ExprNode).getExprNode()
)
or
DataFlowPrivate::storeStepCommon(nodeFrom, contents, nodeTo)
or
TypeTrackerSummaryFlow::basicStoreStep(nodeFrom, nodeTo, contents)
}
/**
* Holds if `nodeTo` is the result of accessing the `content` content of `nodeFrom`.
*/
predicate loadStep(Node nodeFrom, LocalSourceNode nodeTo, Content contents) {
DataFlowPrivate::readStepCommon(nodeFrom, contents, nodeTo)
or
exists(ExprNodes::MethodCallCfgNode call |
call.getExpr().getNumberOfArguments() = 0 and
contents
.isSingleton(DataFlowPublic::Content::getAttributeName(call.getExpr().getMethodName())) and
nodeFrom.asExpr() = call.getReceiver() and
nodeTo.asExpr() = call
)
or
TypeTrackerSummaryFlow::basicLoadStep(nodeFrom, nodeTo, contents)
}
/**
* Holds if the `loadContent` of `nodeFrom` is stored in the `storeContent` of `nodeTo`.
*/
predicate loadStoreStep(Node nodeFrom, Node nodeTo, Content loadContent, Content storeContent) {
exists(DataFlowPrivate::SynthSplatParameterShiftNode shift |
shift.readFrom(nodeFrom, loadContent) and
shift.storeInto(nodeTo, storeContent)
)
or
exists(DataFlowPrivate::SynthSplatArgumentShiftNode shift |
shift.readFrom(nodeFrom, loadContent) and
shift.storeInto(nodeTo, storeContent)
)
or
TypeTrackerSummaryFlow::basicLoadStoreStep(nodeFrom, nodeTo, loadContent, storeContent)
}
/**
* Same as `withContentStep`, but `nodeTo` has type `Node` instead of `LocalSourceNode`,
* which allows for it by used in the definition of `LocalSourceNode`.
*/
additional predicate withContentStepImpl(Node nodeFrom, Node nodeTo, ContentFilter filter) {
TypeTrackerSummaryFlow::basicWithContentStep(nodeFrom, nodeTo, filter)
}
/**
* Holds if type-tracking should step from `nodeFrom` to `nodeTo` if inside a content matched by `filter`.
*/
predicate withContentStep(Node nodeFrom, LocalSourceNode nodeTo, ContentFilter filter) {
withContentStepImpl(nodeFrom, nodeTo, filter)
}
/**
* Same as `withoutContentStep`, but `nodeTo` has type `Node` instead of `LocalSourceNode`,
* which allows for it by used in the definition of `LocalSourceNode`.
*/
additional predicate withoutContentStepImpl(Node nodeFrom, Node nodeTo, ContentFilter filter) {
TypeTrackerSummaryFlow::basicWithoutContentStep(nodeFrom, nodeTo, filter)
}
/**
* Holds if type-tracking should step from `nodeFrom` to `nodeTo` but block flow of contents matched by `filter` through here.
*/
predicate withoutContentStep(Node nodeFrom, LocalSourceNode nodeTo, ContentFilter filter) {
withoutContentStepImpl(nodeFrom, nodeTo, filter)
}
/**
* Holds if data can flow from `node1` to `node2` in a way that discards call contexts.
*/
predicate jumpStep(Node nodeFrom, LocalSourceNode nodeTo) {
DataFlowPrivate::jumpStep(nodeFrom, nodeTo)
}
predicate hasFeatureBacktrackStoreTarget() { none() }
}
import SharedImpl::TypeTracking<TypeTrackingInput>

View File

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

View File

@@ -32,7 +32,6 @@ testFailures
| array_flow.rb:940:18:940:78 | # $ hasValueFlow=91.1 $ hasValueFlow=91.2 $ hasValueFlow=91.3 | Missing result:hasValueFlow=91.3 | | array_flow.rb: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: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: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:1099:10:1099:13 | ...[...] | Unexpected result: hasValueFlow=105.2 |
| array_flow.rb:1100:10:1100:13 | ...[...] | Unexpected result: hasValueFlow=105.3 | | array_flow.rb:1100:10:1100:13 | ...[...] | Unexpected result: hasValueFlow=105.3 |
| array_flow.rb:1110:10:1110:13 | ...[...] | Unexpected result: hasValueFlow=105.2 | | array_flow.rb:1110:10:1110:13 | ...[...] | Unexpected result: hasValueFlow=105.2 |

View File

@@ -9,30 +9,16 @@ testFailures
| hash_flow.rb:219:27:219:47 | # $ hasValueFlow=14.2 | Missing result:hasValueFlow=14.2 | | hash_flow.rb: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:291:10:291:14 | ...[...] | Unexpected result: hasValueFlow=19.1 |
| hash_flow.rb:294:10:294:14 | ...[...] | Unexpected result: hasValueFlow=19.3 | | 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: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: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.1 |
| hash_flow.rb:559:17:559:57 | # $ hasValueFlow=34.1 $ hasValueFlow=34.2 | Missing result:hasValueFlow=34.2 | | 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: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.1 |
| hash_flow.rb:591:20:591:60 | # $ hasValueFlow=36.1 $ hasValueFlow=36.2 | Missing result:hasValueFlow=36.2 | | 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: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:776:10:776:14 | ...[...] | Unexpected result: hasValueFlow=46.1 |
| hash_flow.rb:779:10:779:14 | ...[...] | Unexpected result: hasValueFlow=46.3 | | 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:781:10:781:17 | ...[...] | Unexpected result: hasValueFlow=46.1 |
| hash_flow.rb:784:10:784:17 | ...[...] | Unexpected result: hasValueFlow=46.3 | | 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 failures

View File

@@ -2580,52 +2580,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 | 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 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: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 | 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 | 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 | 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 | 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 | 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 | 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 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: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 | 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 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: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 | 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 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: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 | 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 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: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: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 | 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 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: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 | 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: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 | | params_flow.rb:128:10:128:31 | Array | type tracker without call steps | params_flow.rb:128:10:128:31 | Array |
@@ -5297,47 +5297,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: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: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: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: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: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: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: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: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 | 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: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: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 | 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: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: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 | 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: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: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 | 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: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: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: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 | 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: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: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 | 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: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 | | params_flow.rb:128:10:128:31 | Array | params_flow.rb:128:10:128:31 | Array |

View File

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