mirror of
https://github.com/github/codeql.git
synced 2026-04-29 02:35:15 +02:00
Merge pull request #10375 from asgerf/rb/summarize-loads-v2
Ruby: type-tracking and API edges through simple library callables
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
private import codeql.ruby.AST
|
||||
private import codeql.ruby.DataFlow
|
||||
private import codeql.ruby.typetracking.TypeTracker
|
||||
private import codeql.ruby.typetracking.TypeTrackerSpecific as TypeTrackerSpecific
|
||||
private import codeql.ruby.ast.internal.Module
|
||||
private import codeql.ruby.controlflow.CfgNodes
|
||||
private import codeql.ruby.dataflow.internal.DataFlowPrivate as DataFlowPrivate
|
||||
@@ -257,6 +258,30 @@ module API {
|
||||
*/
|
||||
Node getAnImmediateSubclass() { result = this.getASuccessor(Label::subclass()) }
|
||||
|
||||
/**
|
||||
* Gets a node representing the `content` stored on the base object.
|
||||
*/
|
||||
Node getContent(DataFlow::Content content) {
|
||||
result = this.getASuccessor(Label::content(content))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a node representing the `contents` stored on the base object.
|
||||
*/
|
||||
pragma[inline]
|
||||
Node getContents(DataFlow::ContentSet contents) {
|
||||
// We always use getAStoreContent when generating the graph, and we always use getAReadContent when querying the graph.
|
||||
result = this.getContent(contents.getAReadContent())
|
||||
}
|
||||
|
||||
/** Gets a node representing the instance field of the given `name`, which must include the `@` character. */
|
||||
Node getField(string name) { result = this.getContent(DataFlowPrivate::TFieldContent(name)) }
|
||||
|
||||
/** Gets a node representing an element of this collection (known or unknown). */
|
||||
Node getAnElement() {
|
||||
result = this.getContents(any(DataFlow::ContentSet set | set.isAnyElement()))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string representation of the lexicographically least among all shortest access paths
|
||||
* from the root to this node.
|
||||
@@ -495,9 +520,25 @@ module API {
|
||||
ref.asExpr() = c and
|
||||
read = c.getExpr()
|
||||
)
|
||||
or
|
||||
exists(TypeTrackerSpecific::TypeTrackerContent c |
|
||||
TypeTrackerSpecific::basicLoadStep(node, ref, c) and
|
||||
lbl = Label::content(c.getAStoreContent())
|
||||
)
|
||||
// note: method calls are not handled here as there is no DataFlow::Node for the intermediate MkMethodAccessNode API node
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `rhs` is a definition of a node that should have an incoming edge labeled `lbl`,
|
||||
* from a def node that is reachable from `node`.
|
||||
*/
|
||||
private predicate defStep(Label::ApiLabel lbl, DataFlow::Node node, DataFlow::Node rhs) {
|
||||
exists(TypeTrackerSpecific::TypeTrackerContent c |
|
||||
TypeTrackerSpecific::basicStoreStep(rhs, node, c) and
|
||||
lbl = Label::content(c.getAStoreContent())
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate isUse(DataFlow::Node nd) {
|
||||
useRoot(_, nd)
|
||||
@@ -539,27 +580,12 @@ module API {
|
||||
/** Gets a node reachable from a use-node. */
|
||||
private DataFlow::LocalSourceNode useCandFwd() { result = useCandFwd(TypeTracker::end()) }
|
||||
|
||||
private DataFlow::Node useCandRev(TypeBackTracker tb) {
|
||||
result = useCandFwd() and
|
||||
tb.start()
|
||||
or
|
||||
exists(TypeBackTracker tb2, DataFlow::LocalSourceNode mid, TypeTracker t |
|
||||
mid = useCandRev(tb2) and
|
||||
result = mid.backtrack(tb2, tb) and
|
||||
pragma[only_bind_out](result) = useCandFwd(t) and
|
||||
pragma[only_bind_out](t) = pragma[only_bind_out](tb).getACompatibleTypeTracker()
|
||||
)
|
||||
}
|
||||
|
||||
private DataFlow::LocalSourceNode useCandRev() {
|
||||
result = useCandRev(TypeBackTracker::end()) and
|
||||
isUse(result)
|
||||
}
|
||||
|
||||
private predicate isDef(DataFlow::Node rhs) {
|
||||
// If a call node is relevant as a use-node, treat its arguments as def-nodes
|
||||
argumentStep(_, useCandFwd(), rhs)
|
||||
or
|
||||
defStep(_, trackDefNode(_), rhs)
|
||||
or
|
||||
rhs = any(EntryPoint entry).getASink()
|
||||
}
|
||||
|
||||
@@ -608,26 +634,12 @@ module API {
|
||||
*
|
||||
* The flow from `src` to the returned node may be inter-procedural.
|
||||
*/
|
||||
private DataFlow::Node trackUseNode(DataFlow::LocalSourceNode src, TypeTracker t) {
|
||||
private DataFlow::LocalSourceNode trackUseNode(DataFlow::LocalSourceNode src, TypeTracker t) {
|
||||
result = src and
|
||||
result = useCandRev() and
|
||||
isUse(src) and
|
||||
t.start()
|
||||
or
|
||||
exists(TypeTracker t2, DataFlow::LocalSourceNode mid |
|
||||
mid = trackUseNode(src, t2) and
|
||||
result = useNodeStep(mid, t2, t)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private DataFlow::Node useNodeStep(
|
||||
DataFlow::LocalSourceNode mid, TypeTracker tmid, TypeTracker t
|
||||
) {
|
||||
exists(TypeBackTracker tb |
|
||||
result = mid.track(tmid, t) and
|
||||
pragma[only_bind_into](result) = useCandRev(pragma[only_bind_into](tb)) and
|
||||
pragma[only_bind_out](t) = pragma[only_bind_into](tb).getACompatibleTypeTracker()
|
||||
)
|
||||
exists(TypeTracker t2 | result = trackUseNode(src, t2).track(t2, t))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -682,6 +694,12 @@ module API {
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(DataFlow::Node predNode, DataFlow::Node succNode |
|
||||
def(pred, predNode) and
|
||||
def(succ, succNode) and
|
||||
defStep(lbl, trackDefNode(predNode), succNode)
|
||||
)
|
||||
or
|
||||
// `pred` is a use of class A
|
||||
// `succ` is a use of class B
|
||||
// there exists a class declaration B < A
|
||||
@@ -754,7 +772,8 @@ module API {
|
||||
any(DataFlowDispatch::ParameterPosition c).isPositional(n)
|
||||
} or
|
||||
MkLabelBlockParameter() or
|
||||
MkLabelEntryPoint(EntryPoint name)
|
||||
MkLabelEntryPoint(EntryPoint name) or
|
||||
MkLabelContent(DataFlow::Content content)
|
||||
}
|
||||
|
||||
/** Provides classes modeling the various edges (labels) in the API graph. */
|
||||
@@ -844,6 +863,20 @@ module API {
|
||||
/** Gets the name of the entry point. */
|
||||
API::EntryPoint getName() { result = name }
|
||||
}
|
||||
|
||||
/** A label representing contents of an object. */
|
||||
class LabelContent extends ApiLabel, MkLabelContent {
|
||||
private DataFlow::Content content;
|
||||
|
||||
LabelContent() { this = MkLabelContent(content) }
|
||||
|
||||
override string toString() {
|
||||
result = "getContent(" + content.toString().replaceAll(" ", "_") + ")"
|
||||
}
|
||||
|
||||
/** Gets the content represented by this label. */
|
||||
DataFlow::Content getContent() { result = content }
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the `member` edge label for member `m`. */
|
||||
@@ -870,6 +903,9 @@ module API {
|
||||
/** Gets the label for the edge from the root node to a custom entry point of the given name. */
|
||||
LabelEntryPoint entryPoint(API::EntryPoint name) { result.getName() = name }
|
||||
|
||||
/** Gets a label representing the given content. */
|
||||
LabelContent content(DataFlow::Content content) { result.getContent() = content }
|
||||
|
||||
/** Gets the API graph label corresponding to the given argument position. */
|
||||
Label::ApiLabel getLabelFromArgumentPosition(DataFlowDispatch::ArgumentPosition pos) {
|
||||
exists(int n |
|
||||
|
||||
@@ -150,7 +150,7 @@ abstract class SimpleSummarizedCallable extends SummarizedCallable {
|
||||
bindingset[this]
|
||||
SimpleSummarizedCallable() { mc.getMethodName() = this }
|
||||
|
||||
final override MethodCall getACall() { result = mc }
|
||||
final override MethodCall getACallSimple() { result = mc }
|
||||
}
|
||||
|
||||
class RequiredSummaryComponentStack = Impl::Public::RequiredSummaryComponentStack;
|
||||
|
||||
@@ -49,7 +49,10 @@ abstract class LibraryCallable extends string {
|
||||
LibraryCallable() { any() }
|
||||
|
||||
/** Gets a call to this library callable. */
|
||||
abstract Call getACall();
|
||||
Call getACall() { none() }
|
||||
|
||||
/** Same as `getACall()` except this does not depend on the call graph or API graph. */
|
||||
Call getACallSimple() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -287,7 +290,7 @@ private DataFlowCallable viableSourceCallable(DataFlowCall call) {
|
||||
private DataFlowCallable viableLibraryCallable(DataFlowCall call) {
|
||||
exists(LibraryCallable callable |
|
||||
result = TLibraryCallable(callable) and
|
||||
call.asCall().getExpr() = callable.getACall()
|
||||
call.asCall().getExpr() = [callable.getACall(), callable.getACallSimple()]
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -373,18 +373,24 @@ private module Cached {
|
||||
n instanceof SynthReturnNode
|
||||
or
|
||||
// Needed for stores in type tracking
|
||||
TypeTrackerSpecific::basicStoreStep(_, n, _)
|
||||
TypeTrackerSpecific::postUpdateStoreStep(_, n, _)
|
||||
}
|
||||
|
||||
cached
|
||||
newtype TContentSet =
|
||||
newtype TOptionalContentSet =
|
||||
TSingletonContent(Content c) or
|
||||
TAnyElementContent() or
|
||||
TKnownOrUnknownElementContent(Content::KnownElementContent c) or
|
||||
TElementLowerBoundContent(int lower, boolean includeUnknown) {
|
||||
FlowSummaryImplSpecific::ParsePositions::isParsedElementLowerBoundPosition(_, includeUnknown,
|
||||
lower)
|
||||
}
|
||||
} or
|
||||
TNoContentSet() // Only used by type-tracking
|
||||
|
||||
cached
|
||||
class TContentSet =
|
||||
TSingletonContent or TAnyElementContent or TKnownOrUnknownElementContent or
|
||||
TElementLowerBoundContent;
|
||||
|
||||
cached
|
||||
newtype TContent =
|
||||
@@ -410,7 +416,9 @@ private module Cached {
|
||||
|
|
||||
name = [input, output].regexpFind("(?<=(^|\\.)Field\\[)[^\\]]+(?=\\])", _, _).trim()
|
||||
)
|
||||
}
|
||||
} or
|
||||
// Only used by type-tracking
|
||||
TAttributeName(string name) { name = any(SetterMethodCall c).getTargetName() }
|
||||
|
||||
/**
|
||||
* Holds if `e` is an `ExprNode` that may be returned by a call to `c`.
|
||||
|
||||
@@ -314,6 +314,26 @@ module Content {
|
||||
class UnknownPairValueContent extends PairValueContent, TUnknownPairValueContent {
|
||||
override string toString() { result = "pair" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A value stored behind a getter/setter pair.
|
||||
*
|
||||
* This is used (only) by type-tracking, as a heuristic since getter/setter pairs tend to operate
|
||||
* on similar types of objects (i.e. the type flowing into a setter will likely flow out of the getter).
|
||||
*/
|
||||
class AttributeNameContent extends Content, TAttributeName {
|
||||
private string name;
|
||||
|
||||
AttributeNameContent() { this = TAttributeName(name) }
|
||||
|
||||
override string toString() { result = "attribute " + name }
|
||||
|
||||
/** Gets the attribute name. */
|
||||
string getName() { result = name }
|
||||
}
|
||||
|
||||
/** Gets `AttributeNameContent` of the given name. */
|
||||
AttributeNameContent getAttributeName(string name) { result.getName() = name }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -246,24 +246,15 @@ module ParsePositions {
|
||||
private import FlowSummaryImpl
|
||||
|
||||
private predicate isParamBody(string body) {
|
||||
exists(AccessPathToken tok |
|
||||
tok.getName() = "Parameter" and
|
||||
body = tok.getAnArgument()
|
||||
)
|
||||
body = any(AccessPathToken tok).getAnArgument("Parameter")
|
||||
}
|
||||
|
||||
private predicate isArgBody(string body) {
|
||||
exists(AccessPathToken tok |
|
||||
tok.getName() = "Argument" and
|
||||
body = tok.getAnArgument()
|
||||
)
|
||||
body = any(AccessPathToken tok).getAnArgument("Argument")
|
||||
}
|
||||
|
||||
private predicate isElementBody(string body) {
|
||||
exists(AccessPathToken tok |
|
||||
tok.getName() = "Element" and
|
||||
body = tok.getAnArgument()
|
||||
)
|
||||
body = any(AccessPathToken tok).getAnArgument(["Element", "WithElement", "WithoutElement"])
|
||||
}
|
||||
|
||||
predicate isParsedParameterPosition(string c, int i) {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
private import codeql.ruby.AST
|
||||
private import codeql.ruby.ApiGraphs
|
||||
private import codeql.ruby.DataFlow
|
||||
private import codeql.ruby.ast.internal.Module
|
||||
private import codeql.ruby.dataflow.FlowSummary
|
||||
private import codeql.ruby.dataflow.internal.DataFlowDispatch
|
||||
|
||||
@@ -17,6 +18,15 @@ private string lastBlockParam(MethodCall mc, string name, int lastBlockParam) {
|
||||
lastBlockParam = mc.getBlock().getNumberOfParameters() - 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a call to the method `name` invoked on the `Array` object
|
||||
* (not on an array instance).
|
||||
*/
|
||||
private MethodCall getAStaticArrayCall(string name) {
|
||||
result.getMethodName() = name and
|
||||
resolveConstantReadAccess(result.getReceiver()) = TResolved("Array")
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides flow summaries for the `Array` class.
|
||||
*
|
||||
@@ -34,9 +44,7 @@ module Array {
|
||||
private class ArrayLiteralSummary extends SummarizedCallable {
|
||||
ArrayLiteralSummary() { this = "Array.[]" }
|
||||
|
||||
override MethodCall getACall() {
|
||||
result = API::getTopLevelMember("Array").getAMethodCall("[]").getExprNode().getExpr()
|
||||
}
|
||||
override MethodCall getACallSimple() { result = getAStaticArrayCall("[]") }
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
exists(ArrayIndex i |
|
||||
@@ -50,9 +58,7 @@ module Array {
|
||||
private class NewSummary extends SummarizedCallable {
|
||||
NewSummary() { this = "Array.new" }
|
||||
|
||||
override MethodCall getACall() {
|
||||
result = API::getTopLevelMember("Array").getAnInstantiation().getExprNode().getExpr()
|
||||
}
|
||||
override MethodCall getACallSimple() { result = getAStaticArrayCall("new") }
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
(
|
||||
@@ -72,9 +78,7 @@ module Array {
|
||||
private class TryConvertSummary extends SummarizedCallable {
|
||||
TryConvertSummary() { this = "Array.try_convert" }
|
||||
|
||||
override MethodCall getACall() {
|
||||
result = API::getTopLevelMember("Array").getAMethodCall("try_convert").getExprNode().getExpr()
|
||||
}
|
||||
override MethodCall getACallSimple() { result = getAStaticArrayCall("try_convert") }
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
input = "Argument[0].WithElement[any]" and
|
||||
@@ -86,7 +90,7 @@ module Array {
|
||||
private class SetIntersectionSummary extends SummarizedCallable {
|
||||
SetIntersectionSummary() { this = "&" }
|
||||
|
||||
override BitwiseAndExpr getACall() { any() }
|
||||
override BitwiseAndExpr getACallSimple() { any() }
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
input = ["Argument[self].Element[any]", "Argument[0].Element[any]"] and
|
||||
@@ -98,7 +102,7 @@ module Array {
|
||||
private class SetUnionSummary extends SummarizedCallable {
|
||||
SetUnionSummary() { this = "|" }
|
||||
|
||||
override BitwiseOrExpr getACall() { any() }
|
||||
override BitwiseOrExpr getACallSimple() { any() }
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
input = ["Argument[self].Element[any]", "Argument[0].Element[any]"] and
|
||||
@@ -110,7 +114,7 @@ module Array {
|
||||
private class RepetitionSummary extends SummarizedCallable {
|
||||
RepetitionSummary() { this = "*" }
|
||||
|
||||
override MulExpr getACall() { any() }
|
||||
override MulExpr getACallSimple() { any() }
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
input = "Argument[self].Element[any]" and
|
||||
@@ -122,7 +126,7 @@ module Array {
|
||||
private class ConcatenationSummary extends SummarizedCallable {
|
||||
ConcatenationSummary() { this = "+" }
|
||||
|
||||
override AddExpr getACall() { any() }
|
||||
override AddExpr getACallSimple() { any() }
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
(
|
||||
@@ -139,7 +143,7 @@ module Array {
|
||||
private class SetDifferenceSummary extends SummarizedCallable {
|
||||
SetDifferenceSummary() { this = "-" }
|
||||
|
||||
override SubExpr getACall() { any() }
|
||||
override SubExpr getACallSimple() { any() }
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
input = "Argument[self].Element[any]" and
|
||||
@@ -152,7 +156,7 @@ module Array {
|
||||
private class AppendOperatorSummary extends SummarizedCallable {
|
||||
AppendOperatorSummary() { this = "<<" }
|
||||
|
||||
override LShiftExpr getACall() { any() }
|
||||
override LShiftExpr getACallSimple() { any() }
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
(
|
||||
@@ -176,9 +180,11 @@ module Array {
|
||||
ElementReferenceReadMethodName methodName; // adding this as a field helps give a better join order
|
||||
|
||||
bindingset[this]
|
||||
ElementReferenceReadSummary() { mc.getMethodName() = methodName }
|
||||
ElementReferenceReadSummary() {
|
||||
mc.getMethodName() = methodName and not mc = getAStaticArrayCall(methodName)
|
||||
}
|
||||
|
||||
override MethodCall getACall() { result = mc }
|
||||
override MethodCall getACallSimple() { result = mc }
|
||||
}
|
||||
|
||||
/** A call to `[]` with a known index. */
|
||||
@@ -303,7 +309,7 @@ module Array {
|
||||
bindingset[this]
|
||||
ElementReferenceStoreSummary() { mc.getMethodName() = "[]=" }
|
||||
|
||||
final override MethodCall getACall() { result = mc }
|
||||
final override MethodCall getACallSimple() { result = mc }
|
||||
}
|
||||
|
||||
/** A call to `[]=` with a known index. */
|
||||
|
||||
@@ -5,6 +5,7 @@ private import codeql.ruby.ApiGraphs
|
||||
private import codeql.ruby.DataFlow
|
||||
private import codeql.ruby.dataflow.FlowSummary
|
||||
private import codeql.ruby.dataflow.internal.DataFlowDispatch
|
||||
private import codeql.ruby.ast.internal.Module
|
||||
|
||||
/**
|
||||
* Provides flow summaries for the `Hash` class.
|
||||
@@ -35,12 +36,19 @@ module Hash {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a call to the method `name` invoked on the `Hash` object
|
||||
* (not on a hash instance).
|
||||
*/
|
||||
private MethodCall getAStaticHashCall(string name) {
|
||||
result.getMethodName() = name and
|
||||
resolveConstantReadAccess(result.getReceiver()) = TResolved("Hash")
|
||||
}
|
||||
|
||||
private class HashLiteralSummary extends SummarizedCallable {
|
||||
HashLiteralSummary() { this = "Hash.[]" }
|
||||
|
||||
final override MethodCall getACall() {
|
||||
result = API::getTopLevelMember("Hash").getAMethodCall("[]").getExprNode().getExpr()
|
||||
}
|
||||
final override MethodCall getACallSimple() { result = getAStaticHashCall("[]") }
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
// { 'nonsymbol' => x }
|
||||
@@ -77,9 +85,8 @@ module Hash {
|
||||
private class HashNewSummary extends SummarizedCallable {
|
||||
HashNewSummary() { this = "Hash[]" }
|
||||
|
||||
final override ElementReference getACall() {
|
||||
result.getReceiver() =
|
||||
API::getTopLevelMember("Hash").getAValueReachableFromSource().asExpr().getExpr() and
|
||||
final override MethodCall getACallSimple() {
|
||||
result = getAStaticHashCall("[]") and
|
||||
result.getNumberOfArguments() = 1
|
||||
}
|
||||
|
||||
@@ -117,9 +124,8 @@ module Hash {
|
||||
)
|
||||
}
|
||||
|
||||
final override ElementReference getACall() {
|
||||
result.getReceiver() =
|
||||
API::getTopLevelMember("Hash").getAValueReachableFromSource().asExpr().getExpr() and
|
||||
final override MethodCall getACallSimple() {
|
||||
result = getAStaticHashCall("[]") and
|
||||
key = result.getArgument(i - 1).getConstantValue() and
|
||||
exists(result.getArgument(i))
|
||||
}
|
||||
@@ -135,9 +141,7 @@ module Hash {
|
||||
private class TryConvertSummary extends SummarizedCallable {
|
||||
TryConvertSummary() { this = "Hash.try_convert" }
|
||||
|
||||
override MethodCall getACall() {
|
||||
result = API::getTopLevelMember("Hash").getAMethodCall("try_convert").getExprNode().getExpr()
|
||||
}
|
||||
override MethodCall getACallSimple() { result = getAStaticHashCall("try_convert") }
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
input = "Argument[0].WithElement[any]" and
|
||||
@@ -152,7 +156,7 @@ module Hash {
|
||||
bindingset[this]
|
||||
StoreSummary() { mc.getMethodName() = "store" and mc.getNumberOfArguments() = 2 }
|
||||
|
||||
final override MethodCall getACall() { result = mc }
|
||||
final override MethodCall getACallSimple() { result = mc }
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
input = "Argument[1]" and
|
||||
|
||||
@@ -31,6 +31,7 @@ import codeql.ruby.dataflow.internal.AccessPathSyntax as AccessPathSyntax
|
||||
import codeql.ruby.DataFlow::DataFlow as DataFlow
|
||||
private import AccessPathSyntax
|
||||
private import codeql.ruby.dataflow.internal.FlowSummaryImplSpecific as FlowSummaryImplSpecific
|
||||
private import codeql.ruby.dataflow.internal.FlowSummaryImpl::Public
|
||||
private import codeql.ruby.dataflow.internal.DataFlowDispatch as DataFlowDispatch
|
||||
|
||||
/**
|
||||
@@ -118,9 +119,11 @@ API::Node getExtraSuccessorFromNode(API::Node node, AccessPathToken token) {
|
||||
result =
|
||||
node.getASuccessor(API::Label::getLabelFromParameterPosition(FlowSummaryImplSpecific::parseArgBody(token
|
||||
.getAnArgument())))
|
||||
// Note: The "Element" token is not implemented yet, as it ultimately requires type-tracking and
|
||||
// API graphs to be aware of the steps involving Element contributed by the standard library model.
|
||||
// Type-tracking cannot summarize function calls on its own, so it doesn't benefit from synthesized callables.
|
||||
or
|
||||
exists(DataFlow::ContentSet contents |
|
||||
SummaryComponent::content(contents) = FlowSummaryImplSpecific::interpretComponentSpecific(token) and
|
||||
result = node.getContents(contents)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -160,7 +163,7 @@ InvokeNode getAnInvocationOf(API::Node node) { result = node }
|
||||
*/
|
||||
bindingset[name]
|
||||
predicate isExtraValidTokenNameInIdentifyingAccessPath(string name) {
|
||||
name = ["Member", "Method", "Instance", "WithBlock", "WithoutBlock"]
|
||||
name = ["Member", "Method", "Instance", "WithBlock", "WithoutBlock", "Element", "Field"]
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -177,7 +180,7 @@ predicate isExtraValidNoArgumentTokenInIdentifyingAccessPath(string name) {
|
||||
*/
|
||||
bindingset[name, argument]
|
||||
predicate isExtraValidTokenArgumentInIdentifyingAccessPath(string name, string argument) {
|
||||
name = ["Member", "Method"] and
|
||||
name = ["Member", "Method", "Element", "Field"] and
|
||||
exists(argument)
|
||||
or
|
||||
name = ["Argument", "Parameter"] and
|
||||
|
||||
@@ -2,27 +2,6 @@
|
||||
|
||||
private import TypeTrackerSpecific
|
||||
|
||||
/**
|
||||
* A string that may appear as the name of a piece of content. This will usually include things like:
|
||||
* - Attribute names (in Python)
|
||||
* - Property names (in JavaScript)
|
||||
*
|
||||
* In general, this can also be used to model things like stores to specific list indices. To ensure
|
||||
* correctness, it is important that
|
||||
*
|
||||
* - different types of content do not have overlapping names, and
|
||||
* - the empty string `""` is not a valid piece of content, as it is used to indicate the absence of
|
||||
* content instead.
|
||||
*/
|
||||
class ContentName extends string {
|
||||
ContentName() { this = getPossibleContentName() }
|
||||
}
|
||||
|
||||
/** A content name, or the empty string (representing no content). */
|
||||
class OptionalContentName extends string {
|
||||
OptionalContentName() { this instanceof ContentName or this = "" }
|
||||
}
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
/**
|
||||
@@ -33,48 +12,78 @@ private module Cached {
|
||||
LevelStep() or
|
||||
CallStep() or
|
||||
ReturnStep() or
|
||||
StoreStep(ContentName content) or
|
||||
LoadStep(ContentName content) or
|
||||
StoreStep(TypeTrackerContent content) { basicStoreStep(_, _, content) } or
|
||||
LoadStep(TypeTrackerContent content) { basicLoadStep(_, _, content) } or
|
||||
JumpStep()
|
||||
|
||||
pragma[nomagic]
|
||||
private TypeTracker noContentTypeTracker(boolean hasCall) {
|
||||
result = MkTypeTracker(hasCall, noContent())
|
||||
}
|
||||
|
||||
/** Gets the summary resulting from appending `step` to type-tracking summary `tt`. */
|
||||
cached
|
||||
TypeTracker append(TypeTracker tt, StepSummary step) {
|
||||
exists(Boolean hasCall, OptionalContentName content | tt = MkTypeTracker(hasCall, content) |
|
||||
exists(Boolean hasCall, OptionalTypeTrackerContent currentContents |
|
||||
tt = MkTypeTracker(hasCall, currentContents)
|
||||
|
|
||||
step = LevelStep() and result = tt
|
||||
or
|
||||
step = CallStep() and result = MkTypeTracker(true, content)
|
||||
step = CallStep() and result = MkTypeTracker(true, currentContents)
|
||||
or
|
||||
step = ReturnStep() and hasCall = false and result = tt
|
||||
or
|
||||
step = LoadStep(content) and result = MkTypeTracker(hasCall, "")
|
||||
or
|
||||
exists(string p | step = StoreStep(p) and content = "" and result = MkTypeTracker(hasCall, p))
|
||||
or
|
||||
step = JumpStep() and
|
||||
result = MkTypeTracker(false, content)
|
||||
result = MkTypeTracker(false, currentContents)
|
||||
)
|
||||
or
|
||||
exists(TypeTrackerContent storeContents, boolean hasCall |
|
||||
exists(TypeTrackerContent loadContents |
|
||||
step = LoadStep(pragma[only_bind_into](loadContents)) and
|
||||
tt = MkTypeTracker(hasCall, storeContents) and
|
||||
compatibleContents(storeContents, loadContents) and
|
||||
result = noContentTypeTracker(hasCall)
|
||||
)
|
||||
or
|
||||
step = StoreStep(pragma[only_bind_into](storeContents)) and
|
||||
tt = noContentTypeTracker(hasCall) and
|
||||
result = MkTypeTracker(hasCall, storeContents)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private TypeBackTracker noContentTypeBackTracker(boolean hasReturn) {
|
||||
result = MkTypeBackTracker(hasReturn, noContent())
|
||||
}
|
||||
|
||||
/** Gets the summary resulting from prepending `step` to this type-tracking summary. */
|
||||
cached
|
||||
TypeBackTracker prepend(TypeBackTracker tbt, StepSummary step) {
|
||||
exists(Boolean hasReturn, string content | tbt = MkTypeBackTracker(hasReturn, content) |
|
||||
exists(Boolean hasReturn, OptionalTypeTrackerContent content |
|
||||
tbt = MkTypeBackTracker(hasReturn, content)
|
||||
|
|
||||
step = LevelStep() and result = tbt
|
||||
or
|
||||
step = CallStep() and hasReturn = false and result = tbt
|
||||
or
|
||||
step = ReturnStep() and result = MkTypeBackTracker(true, content)
|
||||
or
|
||||
exists(string p |
|
||||
step = LoadStep(p) and content = "" and result = MkTypeBackTracker(hasReturn, p)
|
||||
)
|
||||
or
|
||||
step = StoreStep(content) and result = MkTypeBackTracker(hasReturn, "")
|
||||
or
|
||||
step = JumpStep() and
|
||||
result = MkTypeBackTracker(false, content)
|
||||
)
|
||||
or
|
||||
exists(TypeTrackerContent loadContents, boolean hasReturn |
|
||||
exists(TypeTrackerContent storeContents |
|
||||
step = StoreStep(pragma[only_bind_into](storeContents)) and
|
||||
tbt = MkTypeBackTracker(hasReturn, loadContents) and
|
||||
compatibleContents(storeContents, loadContents) and
|
||||
result = noContentTypeBackTracker(hasReturn)
|
||||
)
|
||||
or
|
||||
step = LoadStep(pragma[only_bind_into](loadContents)) and
|
||||
tbt = noContentTypeBackTracker(hasReturn) and
|
||||
result = MkTypeBackTracker(hasReturn, loadContents)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -114,9 +123,9 @@ class StepSummary extends TStepSummary {
|
||||
or
|
||||
this instanceof ReturnStep and result = "return"
|
||||
or
|
||||
exists(string content | this = StoreStep(content) | result = "store " + content)
|
||||
exists(TypeTrackerContent content | this = StoreStep(content) | result = "store " + content)
|
||||
or
|
||||
exists(string content | this = LoadStep(content) | result = "load " + content)
|
||||
exists(TypeTrackerContent content | this = LoadStep(content) | result = "load " + content)
|
||||
or
|
||||
this instanceof JumpStep and result = "jump"
|
||||
}
|
||||
@@ -130,7 +139,7 @@ private predicate smallstepNoCall(Node nodeFrom, TypeTrackingNode nodeTo, StepSu
|
||||
levelStep(nodeFrom, nodeTo) and
|
||||
summary = LevelStep()
|
||||
or
|
||||
exists(string content |
|
||||
exists(TypeTrackerContent content |
|
||||
StepSummary::localSourceStoreStep(nodeFrom, nodeTo, content) and
|
||||
summary = StoreStep(content)
|
||||
or
|
||||
@@ -180,7 +189,7 @@ module StepSummary {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `nodeFrom` is being written to the `content` content of the object in `nodeTo`.
|
||||
* Holds if `nodeFrom` is being written to the `content` of the object in `nodeTo`.
|
||||
*
|
||||
* Note that `nodeTo` will always be a local source node that flows to the place where the content
|
||||
* is written in `basicStoreStep`. This may lead to the flow of information going "back in time"
|
||||
@@ -204,12 +213,23 @@ module StepSummary {
|
||||
* 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.
|
||||
*/
|
||||
predicate localSourceStoreStep(Node nodeFrom, TypeTrackingNode nodeTo, string content) {
|
||||
predicate localSourceStoreStep(Node nodeFrom, TypeTrackingNode nodeTo, TypeTrackerContent content) {
|
||||
exists(Node obj | nodeTo.flowsTo(obj) and basicStoreStep(nodeFrom, obj, content))
|
||||
}
|
||||
}
|
||||
|
||||
private newtype TTypeTracker = MkTypeTracker(Boolean hasCall, OptionalContentName content)
|
||||
private newtype TTypeTracker =
|
||||
MkTypeTracker(Boolean hasCall, OptionalTypeTrackerContent content) {
|
||||
content = noContent()
|
||||
or
|
||||
// Restrict `content` to those that might eventually match a load.
|
||||
// We can't rely on `basicStoreStep` since `startInContent` might be used with
|
||||
// a content that has no corresponding store.
|
||||
exists(TypeTrackerContent loadContents |
|
||||
basicLoadStep(_, _, loadContents) and
|
||||
compatibleContents(content, loadContents)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A summary of the steps needed to track a value to a given dataflow node.
|
||||
@@ -240,7 +260,7 @@ private newtype TTypeTracker = MkTypeTracker(Boolean hasCall, OptionalContentNam
|
||||
*/
|
||||
class TypeTracker extends TTypeTracker {
|
||||
Boolean hasCall;
|
||||
OptionalContentName content;
|
||||
OptionalTypeTrackerContent content;
|
||||
|
||||
TypeTracker() { this = MkTypeTracker(hasCall, content) }
|
||||
|
||||
@@ -251,7 +271,11 @@ class TypeTracker extends TTypeTracker {
|
||||
string toString() {
|
||||
exists(string withCall, string withContent |
|
||||
(if hasCall = true then withCall = "with" else withCall = "without") and
|
||||
(if content != "" then withContent = " with content " + content else withContent = "") and
|
||||
(
|
||||
if content != noContent()
|
||||
then withContent = " with content " + content
|
||||
else withContent = ""
|
||||
) and
|
||||
result = "type tracker " + withCall + " call steps" + withContent
|
||||
)
|
||||
}
|
||||
@@ -259,24 +283,26 @@ class TypeTracker extends TTypeTracker {
|
||||
/**
|
||||
* Holds if this is the starting point of type tracking.
|
||||
*/
|
||||
predicate start() { hasCall = false and content = "" }
|
||||
predicate start() { hasCall = false and content = noContent() }
|
||||
|
||||
/**
|
||||
* Holds if this is the starting point of type tracking, and the value starts in the content named `contentName`.
|
||||
* The type tracking only ends after the content has been loaded.
|
||||
*/
|
||||
predicate startInContent(ContentName contentName) { hasCall = false and content = contentName }
|
||||
predicate startInContent(TypeTrackerContent contentName) {
|
||||
hasCall = false and content = contentName
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this is the starting point of type tracking
|
||||
* when tracking a parameter into a call, but not out of it.
|
||||
*/
|
||||
predicate call() { hasCall = true and content = "" }
|
||||
predicate call() { hasCall = true and content = noContent() }
|
||||
|
||||
/**
|
||||
* Holds if this is the end point of type tracking.
|
||||
*/
|
||||
predicate end() { content = "" }
|
||||
predicate end() { content = noContent() }
|
||||
|
||||
/**
|
||||
* INTERNAL. DO NOT USE.
|
||||
@@ -290,7 +316,7 @@ class TypeTracker extends TTypeTracker {
|
||||
*
|
||||
* Gets the content associated with this type tracker.
|
||||
*/
|
||||
string getContent() { result = content }
|
||||
OptionalTypeTrackerContent getContent() { result = content }
|
||||
|
||||
/**
|
||||
* Gets a type tracker that starts where this one has left off to allow continued
|
||||
@@ -298,7 +324,7 @@ class TypeTracker extends TTypeTracker {
|
||||
*
|
||||
* This predicate is only defined if the type is not associated to a piece of content.
|
||||
*/
|
||||
TypeTracker continue() { content = "" and result = this }
|
||||
TypeTracker continue() { content = noContent() and result = this }
|
||||
|
||||
/**
|
||||
* Gets the summary that corresponds to having taken a forwards
|
||||
@@ -356,7 +382,16 @@ module TypeTracker {
|
||||
TypeTracker end() { result.end() }
|
||||
}
|
||||
|
||||
private newtype TTypeBackTracker = MkTypeBackTracker(Boolean hasReturn, OptionalContentName content)
|
||||
private newtype TTypeBackTracker =
|
||||
MkTypeBackTracker(Boolean hasReturn, OptionalTypeTrackerContent content) {
|
||||
content = noContent()
|
||||
or
|
||||
// As in MkTypeTracker, restrict `content` to those that might eventually match a store.
|
||||
exists(TypeTrackerContent storeContent |
|
||||
basicStoreStep(_, _, storeContent) and
|
||||
compatibleContents(storeContent, content)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A summary of the steps needed to back-track a use of a value to a given dataflow node.
|
||||
@@ -390,7 +425,7 @@ private newtype TTypeBackTracker = MkTypeBackTracker(Boolean hasReturn, Optional
|
||||
*/
|
||||
class TypeBackTracker extends TTypeBackTracker {
|
||||
Boolean hasReturn;
|
||||
string content;
|
||||
OptionalTypeTrackerContent content;
|
||||
|
||||
TypeBackTracker() { this = MkTypeBackTracker(hasReturn, content) }
|
||||
|
||||
@@ -401,7 +436,11 @@ class TypeBackTracker extends TTypeBackTracker {
|
||||
string toString() {
|
||||
exists(string withReturn, string withContent |
|
||||
(if hasReturn = true then withReturn = "with" else withReturn = "without") and
|
||||
(if content != "" then withContent = " with content " + content else withContent = "") and
|
||||
(
|
||||
if content != noContent()
|
||||
then withContent = " with content " + content
|
||||
else withContent = ""
|
||||
) and
|
||||
result = "type back-tracker " + withReturn + " return steps" + withContent
|
||||
)
|
||||
}
|
||||
@@ -409,12 +448,12 @@ class TypeBackTracker extends TTypeBackTracker {
|
||||
/**
|
||||
* Holds if this is the starting point of type tracking.
|
||||
*/
|
||||
predicate start() { hasReturn = false and content = "" }
|
||||
predicate start() { hasReturn = false and content = noContent() }
|
||||
|
||||
/**
|
||||
* Holds if this is the end point of type tracking.
|
||||
*/
|
||||
predicate end() { content = "" }
|
||||
predicate end() { content = noContent() }
|
||||
|
||||
/**
|
||||
* INTERNAL. DO NOT USE.
|
||||
@@ -429,7 +468,7 @@ class TypeBackTracker extends TTypeBackTracker {
|
||||
*
|
||||
* This predicate is only defined if the type has not been tracked into a piece of content.
|
||||
*/
|
||||
TypeBackTracker continue() { content = "" and result = this }
|
||||
TypeBackTracker continue() { content = noContent() and result = this }
|
||||
|
||||
/**
|
||||
* Gets the summary that corresponds to having taken a backwards
|
||||
|
||||
@@ -1,16 +1,46 @@
|
||||
private import codeql.ruby.AST as Ast
|
||||
private import codeql.ruby.CFG as Cfg
|
||||
private import Cfg::CfgNodes
|
||||
private import codeql.ruby.dataflow.FlowSummary
|
||||
private import codeql.ruby.dataflow.internal.DataFlowImplCommon as DataFlowImplCommon
|
||||
private import codeql.ruby.dataflow.internal.DataFlowPublic as DataFlowPublic
|
||||
private import codeql.ruby.dataflow.internal.DataFlowPrivate as DataFlowPrivate
|
||||
private import codeql.ruby.dataflow.internal.DataFlowDispatch as DataFlowDispatch
|
||||
private import codeql.ruby.dataflow.internal.SsaImpl as SsaImpl
|
||||
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
|
||||
|
||||
class Node = DataFlowPublic::Node;
|
||||
|
||||
class TypeTrackingNode = DataFlowPublic::LocalSourceNode;
|
||||
|
||||
class TypeTrackerContent = DataFlowPublic::ContentSet;
|
||||
|
||||
/**
|
||||
* An optional content set, that is, a `ContentSet` or the special "no content set" value.
|
||||
*/
|
||||
class OptionalTypeTrackerContent extends DataFlowPrivate::TOptionalContentSet {
|
||||
/** Gets a textual representation of this content set. */
|
||||
string toString() {
|
||||
this instanceof DataFlowPrivate::TNoContentSet and
|
||||
result = "no content"
|
||||
or
|
||||
result = this.(DataFlowPublic::ContentSet).toString()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a value stored with `storeContents` can be read back with `loadContents`.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate compatibleContents(TypeTrackerContent storeContents, TypeTrackerContent loadContents) {
|
||||
storeContents.getAStoreContent() = loadContents.getAReadContent()
|
||||
}
|
||||
|
||||
/** Gets the "no content set" value to use for a type tracker not inside any content. */
|
||||
OptionalTypeTrackerContent noContent() { result = DataFlowPrivate::TNoContentSet() }
|
||||
|
||||
/** Holds if there is a simple local flow step from `nodeFrom` to `nodeTo` */
|
||||
predicate simpleLocalFlowStep = DataFlowPrivate::localFlowStepTypeTracker/2;
|
||||
|
||||
@@ -37,14 +67,6 @@ private predicate summarizedLocalStep(Node nodeFrom, Node nodeTo) {
|
||||
/** Holds if there is a level step from `nodeFrom` to `nodeTo`. */
|
||||
predicate levelStep(Node nodeFrom, Node nodeTo) { summarizedLocalStep(nodeFrom, nodeTo) }
|
||||
|
||||
/**
|
||||
* Gets the name of a possible piece of content. This will usually include things like
|
||||
*
|
||||
* - Attribute names (in Python)
|
||||
* - Property names (in JavaScript)
|
||||
*/
|
||||
string getPossibleContentName() { result = getSetterCallAttributeName(_) }
|
||||
|
||||
pragma[noinline]
|
||||
private predicate argumentPositionMatch(
|
||||
ExprNodes::CallCfgNode call, DataFlowPrivate::ArgumentNode arg,
|
||||
@@ -115,11 +137,11 @@ predicate returnStep(Node nodeFrom, Node nodeTo) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `nodeFrom` is being written to the `content` content of the object
|
||||
* 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 `content` content of
|
||||
* "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.
|
||||
@@ -138,17 +160,38 @@ predicate returnStep(Node nodeFrom, Node nodeTo) {
|
||||
* z = x.content
|
||||
* end
|
||||
* ```
|
||||
* for the content write `x.content = y`, we will have `content` being the
|
||||
* 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 basicStoreStep(Node nodeFrom, Node nodeTo, string content) {
|
||||
predicate basicStoreStep(Node nodeFrom, Node nodeTo, DataFlow::ContentSet contents) {
|
||||
postUpdateStoreStep(nodeFrom, nodeTo, contents)
|
||||
or
|
||||
exists(
|
||||
SummarizedCallable callable, DataFlowPublic::CallNode call, SummaryComponent input,
|
||||
SummaryComponent output
|
||||
|
|
||||
hasStoreSummary(callable, contents, input, output) and
|
||||
call.asExpr().getExpr() = callable.getACallSimple() and
|
||||
nodeFrom = evaluateSummaryComponentLocal(call, input) and
|
||||
nodeTo = evaluateSummaryComponentLocal(call, output)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
predicate postUpdateStoreStep(Node nodeFrom, Node nodeTo, DataFlow::ContentSet contents) {
|
||||
// TODO: support SetterMethodCall inside TuplePattern
|
||||
exists(ExprNodes::MethodCallCfgNode call |
|
||||
content = getSetterCallAttributeName(call.getExpr()) and
|
||||
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) =
|
||||
@@ -156,32 +199,26 @@ predicate basicStoreStep(Node nodeFrom, Node nodeTo, string content) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the attribute being set by the setter method call, i.e.
|
||||
* the name of the setter method without the trailing `=`. In the following
|
||||
* example, the result is `"bar"`.
|
||||
*
|
||||
* ```rb
|
||||
* foo.bar = 1
|
||||
* ```
|
||||
*/
|
||||
private string getSetterCallAttributeName(Ast::SetterMethodCall call) {
|
||||
// TODO: this should be exposed in `SetterMethodCall`
|
||||
exists(string setterName |
|
||||
setterName = call.getMethodName() and result = setterName.prefix(setterName.length() - 1)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `nodeTo` is the result of accessing the `content` content of `nodeFrom`.
|
||||
*/
|
||||
predicate basicLoadStep(Node nodeFrom, Node nodeTo, string content) {
|
||||
predicate basicLoadStep(Node nodeFrom, Node nodeTo, DataFlow::ContentSet contents) {
|
||||
exists(ExprNodes::MethodCallCfgNode call |
|
||||
call.getExpr().getNumberOfArguments() = 0 and
|
||||
content = call.getExpr().getMethodName() and
|
||||
contents.isSingleton(DataFlowPublic::Content::getAttributeName(call.getExpr().getMethodName())) and
|
||||
nodeFrom.asExpr() = call.getReceiver() and
|
||||
nodeTo.asExpr() = call
|
||||
)
|
||||
or
|
||||
exists(
|
||||
SummarizedCallable callable, DataFlowPublic::CallNode call, SummaryComponent input,
|
||||
SummaryComponent output
|
||||
|
|
||||
hasLoadSummary(callable, contents, input, output) and
|
||||
call.asExpr().getExpr() = callable.getACallSimple() and
|
||||
nodeFrom = evaluateSummaryComponentLocal(call, input) and
|
||||
nodeTo = evaluateSummaryComponentLocal(call, output)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -190,3 +227,40 @@ predicate basicLoadStep(Node nodeFrom, Node nodeTo, string content) {
|
||||
class Boolean extends boolean {
|
||||
Boolean() { this = true or this = false }
|
||||
}
|
||||
|
||||
private import SummaryComponentStack
|
||||
|
||||
private predicate hasStoreSummary(
|
||||
SummarizedCallable callable, DataFlow::ContentSet contents, SummaryComponent input,
|
||||
SummaryComponent output
|
||||
) {
|
||||
callable
|
||||
.propagatesFlow(singleton(input),
|
||||
push(SummaryComponent::content(contents), singleton(output)), true)
|
||||
}
|
||||
|
||||
private predicate hasLoadSummary(
|
||||
SummarizedCallable callable, DataFlow::ContentSet contents, SummaryComponent input,
|
||||
SummaryComponent output
|
||||
) {
|
||||
callable
|
||||
.propagatesFlow(push(SummaryComponent::content(contents), singleton(input)),
|
||||
singleton(output), true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a data flow node corresponding an argument or return value of `call`,
|
||||
* as specified by `component`.
|
||||
*/
|
||||
bindingset[call, component]
|
||||
private DataFlowPublic::Node evaluateSummaryComponentLocal(
|
||||
DataFlowPublic::CallNode call, SummaryComponent component
|
||||
) {
|
||||
exists(DataFlowDispatch::ParameterPosition pos |
|
||||
component = SummaryComponent::argument(pos) and
|
||||
argumentPositionMatch(call.asExpr(), result, pos)
|
||||
)
|
||||
or
|
||||
component = SummaryComponent::return() and
|
||||
result = call
|
||||
}
|
||||
|
||||
@@ -4,3 +4,5 @@ classMethodCalls
|
||||
instanceMethodCalls
|
||||
| test1.rb:61:1:61:12 | Use getMember("M1").getMember("C1").getMethod("new").getReturn().getMethod("m").getReturn() |
|
||||
| test1.rb:62:1:62:12 | Use getMember("M2").getMember("C3").getMethod("new").getReturn().getMethod("m").getReturn() |
|
||||
flowThroughArray
|
||||
| test1.rb:73:1:73:10 | call to m |
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Tests of the public API of API Graphs
|
||||
*/
|
||||
|
||||
import ruby
|
||||
import codeql.ruby.ApiGraphs
|
||||
|
||||
query predicate classMethodCalls(API::Node node) {
|
||||
@@ -11,3 +12,8 @@ query predicate classMethodCalls(API::Node node) {
|
||||
query predicate instanceMethodCalls(API::Node node) {
|
||||
node = API::getTopLevelMember("M1").getMember("C1").getInstance().getReturn("m")
|
||||
}
|
||||
|
||||
query predicate flowThroughArray(DataFlow::Node node) {
|
||||
node =
|
||||
API::getTopLevelMember("A").getMember("B").getMember("C").getMethod("m").getReturn().asSource()
|
||||
}
|
||||
|
||||
@@ -68,3 +68,8 @@ def userDefinedFunction(x, y)
|
||||
x.customEntryPointCall(y) #$ call=entryPoint("CustomEntryPointCall") use=entryPoint("CustomEntryPointCall").getReturn() rhs=entryPoint("CustomEntryPointCall").getParameter(0)
|
||||
x.customEntryPointUse(y) #$ use=entryPoint("CustomEntryPointUse")
|
||||
end
|
||||
|
||||
array = [A::B::C] #$ use=getMember("Array").getMethod("[]").getReturn()
|
||||
array[0].m #$ use=getMember("A").getMember("B").getMember("C").getMethod("m").getReturn()
|
||||
|
||||
A::B::C[0] #$ use=getMember("A").getMember("B").getMember("C").getContent(element_0)
|
||||
|
||||
@@ -38,6 +38,12 @@ edges
|
||||
| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:133:19:133:25 | tainted |
|
||||
| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:134:19:134:25 | tainted |
|
||||
| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:134:19:134:25 | tainted |
|
||||
| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:138:26:138:32 | tainted |
|
||||
| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:138:26:138:32 | tainted |
|
||||
| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:140:16:140:22 | tainted |
|
||||
| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:140:16:140:22 | tainted |
|
||||
| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:143:39:143:45 | tainted |
|
||||
| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:143:39:143:45 | tainted |
|
||||
| summaries.rb:1:20:1:36 | call to source : | summaries.rb:1:11:1:36 | call to identity : |
|
||||
| summaries.rb:1:20:1:36 | call to source : | summaries.rb:1:11:1:36 | call to identity : |
|
||||
| summaries.rb:4:12:7:3 | call to apply_block : | summaries.rb:9:6:9:13 | tainted2 |
|
||||
@@ -192,6 +198,9 @@ edges
|
||||
| summaries.rb:115:16:115:22 | [post] tainted : | summaries.rb:130:23:130:29 | tainted |
|
||||
| summaries.rb:115:16:115:22 | [post] tainted : | summaries.rb:133:19:133:25 | tainted |
|
||||
| summaries.rb:115:16:115:22 | [post] tainted : | summaries.rb:134:19:134:25 | tainted |
|
||||
| summaries.rb:115:16:115:22 | [post] tainted : | summaries.rb:138:26:138:32 | tainted |
|
||||
| summaries.rb:115:16:115:22 | [post] tainted : | summaries.rb:140:16:140:22 | tainted |
|
||||
| summaries.rb:115:16:115:22 | [post] tainted : | summaries.rb:143:39:143:45 | tainted |
|
||||
| summaries.rb:115:16:115:22 | tainted : | summaries.rb:115:16:115:22 | [post] tainted : |
|
||||
| summaries.rb:115:16:115:22 | tainted : | summaries.rb:115:25:115:25 | [post] y : |
|
||||
| summaries.rb:115:16:115:22 | tainted : | summaries.rb:115:33:115:33 | [post] z : |
|
||||
@@ -397,6 +406,12 @@ nodes
|
||||
| summaries.rb:133:19:133:25 | tainted | semmle.label | tainted |
|
||||
| summaries.rb:134:19:134:25 | tainted | semmle.label | tainted |
|
||||
| summaries.rb:134:19:134:25 | tainted | semmle.label | tainted |
|
||||
| summaries.rb:138:26:138:32 | tainted | semmle.label | tainted |
|
||||
| summaries.rb:138:26:138:32 | tainted | semmle.label | tainted |
|
||||
| summaries.rb:140:16:140:22 | tainted | semmle.label | tainted |
|
||||
| summaries.rb:140:16:140:22 | tainted | semmle.label | tainted |
|
||||
| summaries.rb:143:39:143:45 | tainted | semmle.label | tainted |
|
||||
| summaries.rb:143:39:143:45 | tainted | semmle.label | tainted |
|
||||
subpaths
|
||||
invalidSpecComponent
|
||||
#select
|
||||
@@ -488,6 +503,12 @@ invalidSpecComponent
|
||||
| summaries.rb:133:19:133:25 | tainted | summaries.rb:1:20:1:36 | call to source : | summaries.rb:133:19:133:25 | tainted | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : |
|
||||
| summaries.rb:134:19:134:25 | tainted | summaries.rb:1:20:1:36 | call to source : | summaries.rb:134:19:134:25 | tainted | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : |
|
||||
| summaries.rb:134:19:134:25 | tainted | summaries.rb:1:20:1:36 | call to source : | summaries.rb:134:19:134:25 | tainted | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : |
|
||||
| summaries.rb:138:26:138:32 | tainted | summaries.rb:1:20:1:36 | call to source : | summaries.rb:138:26:138:32 | tainted | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : |
|
||||
| summaries.rb:138:26:138:32 | tainted | summaries.rb:1:20:1:36 | call to source : | summaries.rb:138:26:138:32 | tainted | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : |
|
||||
| summaries.rb:140:16:140:22 | tainted | summaries.rb:1:20:1:36 | call to source : | summaries.rb:140:16:140:22 | tainted | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : |
|
||||
| summaries.rb:140:16:140:22 | tainted | summaries.rb:1:20:1:36 | call to source : | summaries.rb:140:16:140:22 | tainted | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : |
|
||||
| summaries.rb:143:39:143:45 | tainted | summaries.rb:1:20:1:36 | call to source : | summaries.rb:143:39:143:45 | tainted | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : |
|
||||
| summaries.rb:143:39:143:45 | tainted | summaries.rb:1:20:1:36 | call to source : | summaries.rb:143:39:143:45 | tainted | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : |
|
||||
warning
|
||||
| CSV type row should have 5 columns but has 2: test;TooFewColumns |
|
||||
| CSV type row should have 5 columns but has 8: test;TooManyColumns;;;Member[Foo].Instance;too;many;columns |
|
||||
|
||||
@@ -143,6 +143,9 @@ private class SinkFromModel extends ModelInput::SinkModelCsv {
|
||||
"test;FooOrBar;Method[method].Argument[0];test-sink", //
|
||||
";;Member[Foo].Method[sinkAnyArg].Argument[any];test-sink", //
|
||||
";;Member[Foo].Method[sinkAnyNamedArg].Argument[any-named];test-sink", //
|
||||
";;Member[Foo].Method[getSinks].ReturnValue.Element[any].Method[mySink].Argument[0];test-sink", //
|
||||
";;Member[Foo].Method[arraySink].Argument[0].Element[any];test-sink", //
|
||||
";;Member[Foo].Method[secondArrayElementIsSink].Argument[0].Element[1];test-sink", //
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,3 +134,12 @@ Alias::Foo.method(tainted) # $ hasValueFlow=tainted
|
||||
Alias::Bar.method(tainted) # $ hasValueFlow=tainted
|
||||
Something::Foo.method(tainted)
|
||||
Alias::Something.method(tainted)
|
||||
|
||||
Foo.getSinks()[0].mySink(tainted) # $ hasValueFlow=tainted
|
||||
Foo.arraySink(tainted)
|
||||
Foo.arraySink([tainted]) # $ hasValueFlow=tainted
|
||||
|
||||
Foo.secondArrayElementIsSink([tainted, "safe", "safe"])
|
||||
Foo.secondArrayElementIsSink(["safe", tainted, "safe"]) # $ hasValueFlow=tainted
|
||||
Foo.secondArrayElementIsSink(["safe", "safe", tainted])
|
||||
Foo.secondArrayElementIsSink([tainted] * 10) # $ MISSING: hasValueFlow=tainted
|
||||
|
||||
@@ -55,12 +55,12 @@ track
|
||||
| type_tracker.rb:14:5:14:13 | call to field= | type tracker without call steps | type_tracker.rb:14:5:14:13 | call to field= |
|
||||
| type_tracker.rb:14:17:14:23 | "hello" | type tracker with call steps | type_tracker.rb:2:16:2:18 | val |
|
||||
| type_tracker.rb:14:17:14:23 | "hello" | type tracker with call steps | type_tracker.rb:2:16:2:18 | val |
|
||||
| type_tracker.rb:14:17:14:23 | "hello" | type tracker with call steps with content field | type_tracker.rb:7:5:9:7 | self (field) |
|
||||
| type_tracker.rb:14:17:14:23 | "hello" | type tracker with call steps with content field | type_tracker.rb:7:5:9:7 | self in field |
|
||||
| type_tracker.rb:14:17:14:23 | "hello" | type tracker with call steps with content attribute field | type_tracker.rb:7:5:9:7 | self (field) |
|
||||
| type_tracker.rb:14:17:14:23 | "hello" | type tracker with call steps with content attribute field | type_tracker.rb:7:5:9:7 | self in field |
|
||||
| type_tracker.rb:14:17:14:23 | "hello" | type tracker without call steps | type_tracker.rb:14:5:14:13 | call to field= |
|
||||
| type_tracker.rb:14:17:14:23 | "hello" | type tracker without call steps | type_tracker.rb:14:17:14:23 | "hello" |
|
||||
| type_tracker.rb:14:17:14:23 | "hello" | type tracker without call steps | type_tracker.rb:15:10:15:18 | call to field |
|
||||
| type_tracker.rb:14:17:14:23 | "hello" | type tracker without call steps with content field | type_tracker.rb:14:5:14:7 | [post] var |
|
||||
| type_tracker.rb:14:17:14:23 | "hello" | type tracker without call steps with content attribute field | type_tracker.rb:14:5:14:7 | [post] var |
|
||||
| type_tracker.rb:14:17:14:23 | __synth__0 | type tracker without call steps | type_tracker.rb:14:17:14:23 | __synth__0 |
|
||||
| type_tracker.rb:15:5:15:18 | call to puts | type tracker without call steps | type_tracker.rb:12:1:16:3 | return return in m |
|
||||
| type_tracker.rb:15:5:15:18 | call to puts | type tracker without call steps | type_tracker.rb:15:5:15:18 | call to puts |
|
||||
@@ -147,6 +147,147 @@ track
|
||||
| type_tracker.rb:32:26:32:26 | 8 | type tracker with call steps | type_tracker.rb:25:13:25:14 | p1 |
|
||||
| type_tracker.rb:32:26:32:26 | 8 | type tracker with call steps | type_tracker.rb:25:13:25:14 | p1 |
|
||||
| type_tracker.rb:32:26:32:26 | 8 | type tracker without call steps | type_tracker.rb:32:26:32:26 | 8 |
|
||||
| type_tracker.rb:34:1:53:3 | &block | type tracker without call steps | type_tracker.rb:34:1:53:3 | &block |
|
||||
| type_tracker.rb:34:1:53:3 | return return in throughArray | type tracker without call steps | type_tracker.rb:34:1:53:3 | return return in throughArray |
|
||||
| type_tracker.rb:34:1:53:3 | self in throughArray | type tracker without call steps | type_tracker.rb:34:1:53:3 | self in throughArray |
|
||||
| type_tracker.rb:34:1:53:3 | throughArray | type tracker without call steps | type_tracker.rb:34:1:53:3 | throughArray |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type tracker with call steps | type_tracker.rb:34:18:34:20 | obj |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type tracker with call steps | type_tracker.rb:36:5:36:10 | ...[...] |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type tracker with call steps | type_tracker.rb:40:5:40:12 | ...[...] |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type tracker with call steps | type_tracker.rb:44:5:44:13 | ...[...] |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type tracker with call steps | type_tracker.rb:52:5:52:13 | ...[...] |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type tracker with call steps with content element | type_tracker.rb:38:13:38:25 | call to [] |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type tracker with call steps with content element | type_tracker.rb:50:14:50:26 | call to [] |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type tracker with call steps with content element 0 or unknown | type_tracker.rb:35:11:35:15 | call to [] |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type tracker with call steps with content element 0 or unknown | type_tracker.rb:42:14:42:26 | call to [] |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type tracker with call steps with content element 0 or unknown | type_tracker.rb:46:14:46:26 | call to [] |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:34:1:53:3 | return return in throughArray |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:34:18:34:20 | obj |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:34:18:34:20 | obj |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:34:18:34:20 | obj |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:36:5:36:10 | ...[...] |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:40:5:40:12 | ...[...] |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:44:5:44:13 | ...[...] |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:52:5:52:13 | ...[...] |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps with content element | type_tracker.rb:38:13:38:25 | call to [] |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps with content element | type_tracker.rb:50:14:50:26 | call to [] |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps with content element 0 or unknown | type_tracker.rb:35:11:35:15 | call to [] |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps with content element 0 or unknown | type_tracker.rb:42:14:42:26 | call to [] |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps with content element 0 or unknown | type_tracker.rb:46:14:46:26 | call to [] |
|
||||
| type_tracker.rb:34:23:34:23 | y | type tracker with call steps | type_tracker.rb:34:23:34:23 | y |
|
||||
| type_tracker.rb:34:23:34:23 | y | type tracker without call steps | type_tracker.rb:34:23:34:23 | y |
|
||||
| type_tracker.rb:34:23:34:23 | y | type tracker without call steps | type_tracker.rb:34:23:34:23 | y |
|
||||
| type_tracker.rb:34:23:34:23 | y | type tracker without call steps | type_tracker.rb:34:23:34:23 | y |
|
||||
| type_tracker.rb:34:26:34:26 | z | type tracker with call steps | type_tracker.rb:34:26:34:26 | z |
|
||||
| type_tracker.rb:34:26:34:26 | z | type tracker without call steps | type_tracker.rb:34:26:34:26 | z |
|
||||
| type_tracker.rb:34:26:34:26 | z | type tracker without call steps | type_tracker.rb:34:26:34:26 | z |
|
||||
| type_tracker.rb:34:26:34:26 | z | type tracker without call steps | type_tracker.rb:34:26:34:26 | z |
|
||||
| type_tracker.rb:35:5:35:7 | tmp | type tracker without call steps | type_tracker.rb:35:5:35:7 | tmp |
|
||||
| type_tracker.rb:35:11:35:15 | Array | type tracker without call steps | type_tracker.rb:35:11:35:15 | Array |
|
||||
| type_tracker.rb:35:11:35:15 | call to [] | type tracker without call steps | type_tracker.rb:35:11:35:15 | call to [] |
|
||||
| type_tracker.rb:36:5:36:10 | ...[...] | type tracker without call steps | type_tracker.rb:36:5:36:10 | ...[...] |
|
||||
| type_tracker.rb:36:9:36:9 | 0 | type tracker without call steps | type_tracker.rb:36:9:36:9 | 0 |
|
||||
| type_tracker.rb:38:5:38:9 | array | type tracker without call steps | type_tracker.rb:38:5:38:9 | array |
|
||||
| type_tracker.rb:38:13:38:25 | Array | type tracker without call steps | type_tracker.rb:38:13:38:25 | Array |
|
||||
| type_tracker.rb:38:13:38:25 | call to [] | type tracker without call steps | type_tracker.rb:38:13:38:25 | call to [] |
|
||||
| type_tracker.rb:38:14:38:14 | 1 | type tracker without call steps | type_tracker.rb:38:14:38:14 | 1 |
|
||||
| type_tracker.rb:38:14:38:14 | 1 | type tracker without call steps | type_tracker.rb:40:5:40:12 | ...[...] |
|
||||
| type_tracker.rb:38:14:38:14 | 1 | type tracker without call steps with content element 0 or unknown | type_tracker.rb:38:13:38:25 | call to [] |
|
||||
| type_tracker.rb:38:16:38:16 | 2 | type tracker without call steps | type_tracker.rb:38:16:38:16 | 2 |
|
||||
| type_tracker.rb:38:16:38:16 | 2 | type tracker without call steps with content element 1 or unknown | type_tracker.rb:38:13:38:25 | call to [] |
|
||||
| type_tracker.rb:38:18:38:18 | 3 | type tracker without call steps | type_tracker.rb:38:18:38:18 | 3 |
|
||||
| type_tracker.rb:38:18:38:18 | 3 | type tracker without call steps with content element 2 or unknown | type_tracker.rb:38:13:38:25 | call to [] |
|
||||
| type_tracker.rb:38:20:38:20 | 4 | type tracker without call steps | type_tracker.rb:38:20:38:20 | 4 |
|
||||
| type_tracker.rb:38:20:38:20 | 4 | type tracker without call steps with content element 3 or unknown | type_tracker.rb:38:13:38:25 | call to [] |
|
||||
| type_tracker.rb:38:22:38:22 | 5 | type tracker without call steps | type_tracker.rb:38:22:38:22 | 5 |
|
||||
| type_tracker.rb:38:22:38:22 | 5 | type tracker without call steps with content element 4 or unknown | type_tracker.rb:38:13:38:25 | call to [] |
|
||||
| type_tracker.rb:38:24:38:24 | 6 | type tracker without call steps | type_tracker.rb:38:24:38:24 | 6 |
|
||||
| type_tracker.rb:38:24:38:24 | 6 | type tracker without call steps with content element 5 or unknown | type_tracker.rb:38:13:38:25 | call to [] |
|
||||
| type_tracker.rb:39:5:39:9 | [post] array | type tracker without call steps | type_tracker.rb:39:5:39:9 | [post] array |
|
||||
| type_tracker.rb:39:5:39:12 | call to []= | type tracker without call steps | type_tracker.rb:39:5:39:12 | call to []= |
|
||||
| type_tracker.rb:39:16:39:18 | __synth__0 | type tracker without call steps | type_tracker.rb:39:16:39:18 | __synth__0 |
|
||||
| type_tracker.rb:40:5:40:12 | ...[...] | type tracker without call steps | type_tracker.rb:40:5:40:12 | ...[...] |
|
||||
| type_tracker.rb:40:11:40:11 | 0 | type tracker without call steps | type_tracker.rb:40:11:40:11 | 0 |
|
||||
| type_tracker.rb:42:5:42:10 | array2 | type tracker without call steps | type_tracker.rb:42:5:42:10 | array2 |
|
||||
| type_tracker.rb:42:14:42:26 | Array | type tracker without call steps | type_tracker.rb:42:14:42:26 | Array |
|
||||
| type_tracker.rb:42:14:42:26 | call to [] | type tracker without call steps | type_tracker.rb:42:14:42:26 | call to [] |
|
||||
| type_tracker.rb:42:15:42:15 | 1 | type tracker without call steps | type_tracker.rb:42:15:42:15 | 1 |
|
||||
| type_tracker.rb:42:15:42:15 | 1 | type tracker without call steps | type_tracker.rb:44:5:44:13 | ...[...] |
|
||||
| type_tracker.rb:42:15:42:15 | 1 | type tracker without call steps with content element 0 or unknown | type_tracker.rb:42:14:42:26 | call to [] |
|
||||
| type_tracker.rb:42:17:42:17 | 2 | type tracker without call steps | type_tracker.rb:42:17:42:17 | 2 |
|
||||
| type_tracker.rb:42:17:42:17 | 2 | type tracker without call steps | type_tracker.rb:44:5:44:13 | ...[...] |
|
||||
| type_tracker.rb:42:17:42:17 | 2 | type tracker without call steps with content element 1 or unknown | type_tracker.rb:42:14:42:26 | call to [] |
|
||||
| type_tracker.rb:42:19:42:19 | 3 | type tracker without call steps | type_tracker.rb:42:19:42:19 | 3 |
|
||||
| type_tracker.rb:42:19:42:19 | 3 | type tracker without call steps | type_tracker.rb:44:5:44:13 | ...[...] |
|
||||
| type_tracker.rb:42:19:42:19 | 3 | type tracker without call steps with content element 2 or unknown | type_tracker.rb:42:14:42:26 | call to [] |
|
||||
| type_tracker.rb:42:21:42:21 | 4 | type tracker without call steps | type_tracker.rb:42:21:42:21 | 4 |
|
||||
| type_tracker.rb:42:21:42:21 | 4 | type tracker without call steps | type_tracker.rb:44:5:44:13 | ...[...] |
|
||||
| type_tracker.rb:42:21:42:21 | 4 | type tracker without call steps with content element 3 or unknown | type_tracker.rb:42:14:42:26 | call to [] |
|
||||
| type_tracker.rb:42:23:42:23 | 5 | type tracker without call steps | type_tracker.rb:42:23:42:23 | 5 |
|
||||
| type_tracker.rb:42:23:42:23 | 5 | type tracker without call steps | type_tracker.rb:44:5:44:13 | ...[...] |
|
||||
| type_tracker.rb:42:23:42:23 | 5 | type tracker without call steps with content element 4 or unknown | type_tracker.rb:42:14:42:26 | call to [] |
|
||||
| type_tracker.rb:42:25:42:25 | 6 | type tracker without call steps | type_tracker.rb:42:25:42:25 | 6 |
|
||||
| type_tracker.rb:42:25:42:25 | 6 | type tracker without call steps | type_tracker.rb:44:5:44:13 | ...[...] |
|
||||
| type_tracker.rb:42:25:42:25 | 6 | type tracker without call steps with content element 5 or unknown | type_tracker.rb:42:14:42:26 | call to [] |
|
||||
| type_tracker.rb:43:5:43:10 | [post] array2 | type tracker without call steps | type_tracker.rb:43:5:43:10 | [post] array2 |
|
||||
| type_tracker.rb:43:5:43:13 | call to []= | type tracker without call steps | type_tracker.rb:43:5:43:13 | call to []= |
|
||||
| type_tracker.rb:43:12:43:12 | 0 | type tracker without call steps | type_tracker.rb:43:12:43:12 | 0 |
|
||||
| type_tracker.rb:43:17:43:19 | __synth__0 | type tracker without call steps | type_tracker.rb:43:17:43:19 | __synth__0 |
|
||||
| type_tracker.rb:44:5:44:13 | ...[...] | type tracker without call steps | type_tracker.rb:44:5:44:13 | ...[...] |
|
||||
| type_tracker.rb:46:5:46:10 | array3 | type tracker without call steps | type_tracker.rb:46:5:46:10 | array3 |
|
||||
| type_tracker.rb:46:14:46:26 | Array | type tracker without call steps | type_tracker.rb:46:14:46:26 | Array |
|
||||
| type_tracker.rb:46:14:46:26 | call to [] | type tracker without call steps | type_tracker.rb:46:14:46:26 | call to [] |
|
||||
| type_tracker.rb:46:15:46:15 | 1 | type tracker without call steps | type_tracker.rb:46:15:46:15 | 1 |
|
||||
| type_tracker.rb:46:15:46:15 | 1 | type tracker without call steps with content element 0 or unknown | type_tracker.rb:46:14:46:26 | call to [] |
|
||||
| type_tracker.rb:46:17:46:17 | 2 | type tracker without call steps | type_tracker.rb:46:17:46:17 | 2 |
|
||||
| type_tracker.rb:46:17:46:17 | 2 | type tracker without call steps | type_tracker.rb:48:5:48:13 | ...[...] |
|
||||
| type_tracker.rb:46:17:46:17 | 2 | type tracker without call steps with content element 1 or unknown | type_tracker.rb:46:14:46:26 | call to [] |
|
||||
| type_tracker.rb:46:19:46:19 | 3 | type tracker without call steps | type_tracker.rb:46:19:46:19 | 3 |
|
||||
| type_tracker.rb:46:19:46:19 | 3 | type tracker without call steps with content element 2 or unknown | type_tracker.rb:46:14:46:26 | call to [] |
|
||||
| type_tracker.rb:46:21:46:21 | 4 | type tracker without call steps | type_tracker.rb:46:21:46:21 | 4 |
|
||||
| type_tracker.rb:46:21:46:21 | 4 | type tracker without call steps with content element 3 or unknown | type_tracker.rb:46:14:46:26 | call to [] |
|
||||
| type_tracker.rb:46:23:46:23 | 5 | type tracker without call steps | type_tracker.rb:46:23:46:23 | 5 |
|
||||
| type_tracker.rb:46:23:46:23 | 5 | type tracker without call steps with content element 4 or unknown | type_tracker.rb:46:14:46:26 | call to [] |
|
||||
| type_tracker.rb:46:25:46:25 | 6 | type tracker without call steps | type_tracker.rb:46:25:46:25 | 6 |
|
||||
| type_tracker.rb:46:25:46:25 | 6 | type tracker without call steps with content element 5 or unknown | type_tracker.rb:46:14:46:26 | call to [] |
|
||||
| type_tracker.rb:47:5:47:10 | [post] array3 | type tracker without call steps | type_tracker.rb:47:5:47:10 | [post] array3 |
|
||||
| type_tracker.rb:47:5:47:13 | call to []= | type tracker without call steps | type_tracker.rb:47:5:47:13 | call to []= |
|
||||
| type_tracker.rb:47:12:47:12 | 0 | type tracker without call steps | type_tracker.rb:47:12:47:12 | 0 |
|
||||
| type_tracker.rb:47:17:47:19 | __synth__0 | type tracker without call steps | type_tracker.rb:47:17:47:19 | __synth__0 |
|
||||
| type_tracker.rb:48:5:48:13 | ...[...] | type tracker without call steps | type_tracker.rb:48:5:48:13 | ...[...] |
|
||||
| type_tracker.rb:48:12:48:12 | 1 | type tracker without call steps | type_tracker.rb:48:12:48:12 | 1 |
|
||||
| type_tracker.rb:50:5:50:10 | array4 | type tracker without call steps | type_tracker.rb:50:5:50:10 | array4 |
|
||||
| type_tracker.rb:50:14:50:26 | Array | type tracker without call steps | type_tracker.rb:50:14:50:26 | Array |
|
||||
| type_tracker.rb:50:14:50:26 | call to [] | type tracker without call steps | type_tracker.rb:50:14:50:26 | call to [] |
|
||||
| type_tracker.rb:50:15:50:15 | 1 | type tracker without call steps | type_tracker.rb:34:1:53:3 | return return in throughArray |
|
||||
| type_tracker.rb:50:15:50:15 | 1 | type tracker without call steps | type_tracker.rb:50:15:50:15 | 1 |
|
||||
| type_tracker.rb:50:15:50:15 | 1 | type tracker without call steps | type_tracker.rb:52:5:52:13 | ...[...] |
|
||||
| type_tracker.rb:50:15:50:15 | 1 | type tracker without call steps with content element 0 or unknown | type_tracker.rb:50:14:50:26 | call to [] |
|
||||
| type_tracker.rb:50:17:50:17 | 2 | type tracker without call steps | type_tracker.rb:34:1:53:3 | return return in throughArray |
|
||||
| type_tracker.rb:50:17:50:17 | 2 | type tracker without call steps | type_tracker.rb:50:17:50:17 | 2 |
|
||||
| type_tracker.rb:50:17:50:17 | 2 | type tracker without call steps | type_tracker.rb:52:5:52:13 | ...[...] |
|
||||
| type_tracker.rb:50:17:50:17 | 2 | type tracker without call steps with content element 1 or unknown | type_tracker.rb:50:14:50:26 | call to [] |
|
||||
| type_tracker.rb:50:19:50:19 | 3 | type tracker without call steps | type_tracker.rb:34:1:53:3 | return return in throughArray |
|
||||
| type_tracker.rb:50:19:50:19 | 3 | type tracker without call steps | type_tracker.rb:50:19:50:19 | 3 |
|
||||
| type_tracker.rb:50:19:50:19 | 3 | type tracker without call steps | type_tracker.rb:52:5:52:13 | ...[...] |
|
||||
| type_tracker.rb:50:19:50:19 | 3 | type tracker without call steps with content element 2 or unknown | type_tracker.rb:50:14:50:26 | call to [] |
|
||||
| type_tracker.rb:50:21:50:21 | 4 | type tracker without call steps | type_tracker.rb:34:1:53:3 | return return in throughArray |
|
||||
| type_tracker.rb:50:21:50:21 | 4 | type tracker without call steps | type_tracker.rb:50:21:50:21 | 4 |
|
||||
| type_tracker.rb:50:21:50:21 | 4 | type tracker without call steps | type_tracker.rb:52:5:52:13 | ...[...] |
|
||||
| type_tracker.rb:50:21:50:21 | 4 | type tracker without call steps with content element 3 or unknown | type_tracker.rb:50:14:50:26 | call to [] |
|
||||
| type_tracker.rb:50:23:50:23 | 5 | type tracker without call steps | type_tracker.rb:34:1:53:3 | return return in throughArray |
|
||||
| type_tracker.rb:50:23:50:23 | 5 | type tracker without call steps | type_tracker.rb:50:23:50:23 | 5 |
|
||||
| type_tracker.rb:50:23:50:23 | 5 | type tracker without call steps | type_tracker.rb:52:5:52:13 | ...[...] |
|
||||
| type_tracker.rb:50:23:50:23 | 5 | type tracker without call steps with content element 4 or unknown | type_tracker.rb:50:14:50:26 | call to [] |
|
||||
| type_tracker.rb:50:25:50:25 | 6 | type tracker without call steps | type_tracker.rb:34:1:53:3 | return return in throughArray |
|
||||
| type_tracker.rb:50:25:50:25 | 6 | type tracker without call steps | type_tracker.rb:50:25:50:25 | 6 |
|
||||
| type_tracker.rb:50:25:50:25 | 6 | type tracker without call steps | type_tracker.rb:52:5:52:13 | ...[...] |
|
||||
| type_tracker.rb:50:25:50:25 | 6 | type tracker without call steps with content element 5 or unknown | type_tracker.rb:50:14:50:26 | call to [] |
|
||||
| type_tracker.rb:51:5:51:10 | [post] array4 | type tracker without call steps | type_tracker.rb:51:5:51:10 | [post] array4 |
|
||||
| type_tracker.rb:51:5:51:13 | call to []= | type tracker without call steps | type_tracker.rb:51:5:51:13 | call to []= |
|
||||
| type_tracker.rb:51:17:51:19 | __synth__0 | type tracker without call steps | type_tracker.rb:51:17:51:19 | __synth__0 |
|
||||
| type_tracker.rb:52:5:52:13 | ...[...] | type tracker without call steps | type_tracker.rb:34:1:53:3 | return return in throughArray |
|
||||
| type_tracker.rb:52:5:52:13 | ...[...] | type tracker without call steps | type_tracker.rb:52:5:52:13 | ...[...] |
|
||||
trackEnd
|
||||
| type_tracker.rb:1:1:10:3 | self (type_tracker.rb) | type_tracker.rb:1:1:10:3 | self (type_tracker.rb) |
|
||||
| type_tracker.rb:1:1:10:3 | self (type_tracker.rb) | type_tracker.rb:18:1:21:3 | self (positional) |
|
||||
@@ -358,3 +499,185 @@ trackEnd
|
||||
| type_tracker.rb:32:26:32:26 | 8 | type_tracker.rb:25:13:25:14 | p1 |
|
||||
| type_tracker.rb:32:26:32:26 | 8 | type_tracker.rb:26:10:26:11 | p1 |
|
||||
| type_tracker.rb:32:26:32:26 | 8 | type_tracker.rb:32:26:32:26 | 8 |
|
||||
| type_tracker.rb:34:1:53:3 | &block | type_tracker.rb:34:1:53:3 | &block |
|
||||
| type_tracker.rb:34:1:53:3 | return return in throughArray | type_tracker.rb:34:1:53:3 | return return in throughArray |
|
||||
| type_tracker.rb:34:1:53:3 | self in throughArray | type_tracker.rb:34:1:53:3 | self in throughArray |
|
||||
| type_tracker.rb:34:1:53:3 | throughArray | type_tracker.rb:34:1:53:3 | throughArray |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:34:1:53:3 | return return in throughArray |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:34:18:34:20 | obj |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:34:18:34:20 | obj |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:34:18:34:20 | obj |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:34:18:34:20 | obj |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:35:12:35:14 | obj |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:35:12:35:14 | obj |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:36:5:36:10 | ...[...] |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:36:5:36:10 | ...[...] |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:39:5:39:12 | __synth__0 |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:39:5:39:12 | __synth__0 |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:39:5:39:18 | ... |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:39:5:39:18 | ... |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:39:16:39:18 | ... = ... |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:39:16:39:18 | ... = ... |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:39:16:39:18 | ... = ... |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:39:16:39:18 | ... = ... |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:39:16:39:18 | obj |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:39:16:39:18 | obj |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:40:5:40:12 | ...[...] |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:40:5:40:12 | ...[...] |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:43:5:43:13 | __synth__0 |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:43:5:43:13 | __synth__0 |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:43:5:43:19 | ... |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:43:5:43:19 | ... |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:43:17:43:19 | ... = ... |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:43:17:43:19 | ... = ... |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:43:17:43:19 | ... = ... |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:43:17:43:19 | ... = ... |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:43:17:43:19 | obj |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:43:17:43:19 | obj |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:44:5:44:13 | ...[...] |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:44:5:44:13 | ...[...] |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:47:5:47:13 | __synth__0 |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:47:5:47:13 | __synth__0 |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:47:5:47:19 | ... |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:47:5:47:19 | ... |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:47:17:47:19 | ... = ... |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:47:17:47:19 | ... = ... |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:47:17:47:19 | ... = ... |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:47:17:47:19 | ... = ... |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:47:17:47:19 | obj |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:47:17:47:19 | obj |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:51:5:51:13 | __synth__0 |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:51:5:51:13 | __synth__0 |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:51:5:51:19 | ... |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:51:5:51:19 | ... |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:51:17:51:19 | ... = ... |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:51:17:51:19 | ... = ... |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:51:17:51:19 | ... = ... |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:51:17:51:19 | ... = ... |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:51:17:51:19 | obj |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:51:17:51:19 | obj |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:52:5:52:13 | ...[...] |
|
||||
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:52:5:52:13 | ...[...] |
|
||||
| type_tracker.rb:34:23:34:23 | y | type_tracker.rb:34:23:34:23 | y |
|
||||
| type_tracker.rb:34:23:34:23 | y | type_tracker.rb:34:23:34:23 | y |
|
||||
| type_tracker.rb:34:23:34:23 | y | type_tracker.rb:34:23:34:23 | y |
|
||||
| type_tracker.rb:34:23:34:23 | y | type_tracker.rb:34:23:34:23 | y |
|
||||
| type_tracker.rb:34:23:34:23 | y | type_tracker.rb:39:11:39:11 | y |
|
||||
| type_tracker.rb:34:23:34:23 | y | type_tracker.rb:39:11:39:11 | y |
|
||||
| type_tracker.rb:34:23:34:23 | y | type_tracker.rb:44:12:44:12 | y |
|
||||
| type_tracker.rb:34:23:34:23 | y | type_tracker.rb:44:12:44:12 | y |
|
||||
| type_tracker.rb:34:23:34:23 | y | type_tracker.rb:51:12:51:12 | y |
|
||||
| type_tracker.rb:34:23:34:23 | y | type_tracker.rb:51:12:51:12 | y |
|
||||
| type_tracker.rb:34:26:34:26 | z | type_tracker.rb:34:26:34:26 | z |
|
||||
| type_tracker.rb:34:26:34:26 | z | type_tracker.rb:34:26:34:26 | z |
|
||||
| type_tracker.rb:34:26:34:26 | z | type_tracker.rb:34:26:34:26 | z |
|
||||
| type_tracker.rb:34:26:34:26 | z | type_tracker.rb:34:26:34:26 | z |
|
||||
| type_tracker.rb:34:26:34:26 | z | type_tracker.rb:52:12:52:12 | z |
|
||||
| type_tracker.rb:34:26:34:26 | z | type_tracker.rb:52:12:52:12 | z |
|
||||
| type_tracker.rb:35:5:35:7 | tmp | type_tracker.rb:35:5:35:7 | tmp |
|
||||
| type_tracker.rb:35:11:35:15 | Array | type_tracker.rb:35:11:35:15 | Array |
|
||||
| type_tracker.rb:35:11:35:15 | call to [] | type_tracker.rb:35:5:35:15 | ... = ... |
|
||||
| type_tracker.rb:35:11:35:15 | call to [] | type_tracker.rb:35:5:35:15 | ... = ... |
|
||||
| type_tracker.rb:35:11:35:15 | call to [] | type_tracker.rb:35:11:35:15 | call to [] |
|
||||
| type_tracker.rb:35:11:35:15 | call to [] | type_tracker.rb:36:5:36:7 | tmp |
|
||||
| type_tracker.rb:36:5:36:10 | ...[...] | type_tracker.rb:36:5:36:10 | ...[...] |
|
||||
| type_tracker.rb:36:9:36:9 | 0 | type_tracker.rb:36:9:36:9 | 0 |
|
||||
| type_tracker.rb:38:5:38:9 | array | type_tracker.rb:38:5:38:9 | array |
|
||||
| type_tracker.rb:38:13:38:25 | Array | type_tracker.rb:38:13:38:25 | Array |
|
||||
| type_tracker.rb:38:13:38:25 | call to [] | type_tracker.rb:38:5:38:25 | ... = ... |
|
||||
| type_tracker.rb:38:13:38:25 | call to [] | type_tracker.rb:38:5:38:25 | ... = ... |
|
||||
| type_tracker.rb:38:13:38:25 | call to [] | type_tracker.rb:38:13:38:25 | call to [] |
|
||||
| type_tracker.rb:38:13:38:25 | call to [] | type_tracker.rb:39:5:39:9 | array |
|
||||
| type_tracker.rb:38:13:38:25 | call to [] | type_tracker.rb:40:5:40:9 | array |
|
||||
| type_tracker.rb:38:14:38:14 | 1 | type_tracker.rb:38:14:38:14 | 1 |
|
||||
| type_tracker.rb:38:14:38:14 | 1 | type_tracker.rb:40:5:40:12 | ...[...] |
|
||||
| type_tracker.rb:38:16:38:16 | 2 | type_tracker.rb:38:16:38:16 | 2 |
|
||||
| type_tracker.rb:38:18:38:18 | 3 | type_tracker.rb:38:18:38:18 | 3 |
|
||||
| type_tracker.rb:38:20:38:20 | 4 | type_tracker.rb:38:20:38:20 | 4 |
|
||||
| type_tracker.rb:38:22:38:22 | 5 | type_tracker.rb:38:22:38:22 | 5 |
|
||||
| type_tracker.rb:38:24:38:24 | 6 | type_tracker.rb:38:24:38:24 | 6 |
|
||||
| type_tracker.rb:39:5:39:9 | [post] array | type_tracker.rb:39:5:39:9 | [post] array |
|
||||
| type_tracker.rb:39:5:39:9 | [post] array | type_tracker.rb:40:5:40:9 | array |
|
||||
| type_tracker.rb:39:5:39:12 | call to []= | type_tracker.rb:39:5:39:12 | call to []= |
|
||||
| type_tracker.rb:39:16:39:18 | __synth__0 | type_tracker.rb:39:16:39:18 | __synth__0 |
|
||||
| type_tracker.rb:40:5:40:12 | ...[...] | type_tracker.rb:40:5:40:12 | ...[...] |
|
||||
| type_tracker.rb:40:11:40:11 | 0 | type_tracker.rb:40:11:40:11 | 0 |
|
||||
| type_tracker.rb:42:5:42:10 | array2 | type_tracker.rb:42:5:42:10 | array2 |
|
||||
| type_tracker.rb:42:14:42:26 | Array | type_tracker.rb:42:14:42:26 | Array |
|
||||
| type_tracker.rb:42:14:42:26 | call to [] | type_tracker.rb:42:5:42:26 | ... = ... |
|
||||
| type_tracker.rb:42:14:42:26 | call to [] | type_tracker.rb:42:5:42:26 | ... = ... |
|
||||
| type_tracker.rb:42:14:42:26 | call to [] | type_tracker.rb:42:14:42:26 | call to [] |
|
||||
| type_tracker.rb:42:14:42:26 | call to [] | type_tracker.rb:43:5:43:10 | array2 |
|
||||
| type_tracker.rb:42:14:42:26 | call to [] | type_tracker.rb:44:5:44:10 | array2 |
|
||||
| type_tracker.rb:42:15:42:15 | 1 | type_tracker.rb:42:15:42:15 | 1 |
|
||||
| type_tracker.rb:42:15:42:15 | 1 | type_tracker.rb:44:5:44:13 | ...[...] |
|
||||
| type_tracker.rb:42:17:42:17 | 2 | type_tracker.rb:42:17:42:17 | 2 |
|
||||
| type_tracker.rb:42:17:42:17 | 2 | type_tracker.rb:44:5:44:13 | ...[...] |
|
||||
| type_tracker.rb:42:19:42:19 | 3 | type_tracker.rb:42:19:42:19 | 3 |
|
||||
| type_tracker.rb:42:19:42:19 | 3 | type_tracker.rb:44:5:44:13 | ...[...] |
|
||||
| type_tracker.rb:42:21:42:21 | 4 | type_tracker.rb:42:21:42:21 | 4 |
|
||||
| type_tracker.rb:42:21:42:21 | 4 | type_tracker.rb:44:5:44:13 | ...[...] |
|
||||
| type_tracker.rb:42:23:42:23 | 5 | type_tracker.rb:42:23:42:23 | 5 |
|
||||
| type_tracker.rb:42:23:42:23 | 5 | type_tracker.rb:44:5:44:13 | ...[...] |
|
||||
| type_tracker.rb:42:25:42:25 | 6 | type_tracker.rb:42:25:42:25 | 6 |
|
||||
| type_tracker.rb:42:25:42:25 | 6 | type_tracker.rb:44:5:44:13 | ...[...] |
|
||||
| type_tracker.rb:43:5:43:10 | [post] array2 | type_tracker.rb:43:5:43:10 | [post] array2 |
|
||||
| type_tracker.rb:43:5:43:10 | [post] array2 | type_tracker.rb:44:5:44:10 | array2 |
|
||||
| type_tracker.rb:43:5:43:13 | call to []= | type_tracker.rb:43:5:43:13 | call to []= |
|
||||
| type_tracker.rb:43:12:43:12 | 0 | type_tracker.rb:43:12:43:12 | 0 |
|
||||
| type_tracker.rb:43:17:43:19 | __synth__0 | type_tracker.rb:43:17:43:19 | __synth__0 |
|
||||
| type_tracker.rb:44:5:44:13 | ...[...] | type_tracker.rb:44:5:44:13 | ...[...] |
|
||||
| type_tracker.rb:46:5:46:10 | array3 | type_tracker.rb:46:5:46:10 | array3 |
|
||||
| type_tracker.rb:46:14:46:26 | Array | type_tracker.rb:46:14:46:26 | Array |
|
||||
| type_tracker.rb:46:14:46:26 | call to [] | type_tracker.rb:46:5:46:26 | ... = ... |
|
||||
| type_tracker.rb:46:14:46:26 | call to [] | type_tracker.rb:46:5:46:26 | ... = ... |
|
||||
| type_tracker.rb:46:14:46:26 | call to [] | type_tracker.rb:46:14:46:26 | call to [] |
|
||||
| type_tracker.rb:46:14:46:26 | call to [] | type_tracker.rb:47:5:47:10 | array3 |
|
||||
| type_tracker.rb:46:14:46:26 | call to [] | type_tracker.rb:48:5:48:10 | array3 |
|
||||
| type_tracker.rb:46:15:46:15 | 1 | type_tracker.rb:46:15:46:15 | 1 |
|
||||
| type_tracker.rb:46:17:46:17 | 2 | type_tracker.rb:46:17:46:17 | 2 |
|
||||
| type_tracker.rb:46:17:46:17 | 2 | type_tracker.rb:48:5:48:13 | ...[...] |
|
||||
| type_tracker.rb:46:19:46:19 | 3 | type_tracker.rb:46:19:46:19 | 3 |
|
||||
| type_tracker.rb:46:21:46:21 | 4 | type_tracker.rb:46:21:46:21 | 4 |
|
||||
| type_tracker.rb:46:23:46:23 | 5 | type_tracker.rb:46:23:46:23 | 5 |
|
||||
| type_tracker.rb:46:25:46:25 | 6 | type_tracker.rb:46:25:46:25 | 6 |
|
||||
| type_tracker.rb:47:5:47:10 | [post] array3 | type_tracker.rb:47:5:47:10 | [post] array3 |
|
||||
| type_tracker.rb:47:5:47:10 | [post] array3 | type_tracker.rb:48:5:48:10 | array3 |
|
||||
| type_tracker.rb:47:5:47:13 | call to []= | type_tracker.rb:47:5:47:13 | call to []= |
|
||||
| type_tracker.rb:47:12:47:12 | 0 | type_tracker.rb:47:12:47:12 | 0 |
|
||||
| type_tracker.rb:47:17:47:19 | __synth__0 | type_tracker.rb:47:17:47:19 | __synth__0 |
|
||||
| type_tracker.rb:48:5:48:13 | ...[...] | type_tracker.rb:48:5:48:13 | ...[...] |
|
||||
| type_tracker.rb:48:12:48:12 | 1 | type_tracker.rb:48:12:48:12 | 1 |
|
||||
| type_tracker.rb:50:5:50:10 | array4 | type_tracker.rb:50:5:50:10 | array4 |
|
||||
| type_tracker.rb:50:14:50:26 | Array | type_tracker.rb:50:14:50:26 | Array |
|
||||
| type_tracker.rb:50:14:50:26 | call to [] | type_tracker.rb:50:5:50:26 | ... = ... |
|
||||
| type_tracker.rb:50:14:50:26 | call to [] | type_tracker.rb:50:5:50:26 | ... = ... |
|
||||
| type_tracker.rb:50:14:50:26 | call to [] | type_tracker.rb:50:14:50:26 | call to [] |
|
||||
| type_tracker.rb:50:14:50:26 | call to [] | type_tracker.rb:51:5:51:10 | array4 |
|
||||
| type_tracker.rb:50:14:50:26 | call to [] | type_tracker.rb:52:5:52:10 | array4 |
|
||||
| type_tracker.rb:50:15:50:15 | 1 | type_tracker.rb:34:1:53:3 | return return in throughArray |
|
||||
| type_tracker.rb:50:15:50:15 | 1 | type_tracker.rb:50:15:50:15 | 1 |
|
||||
| type_tracker.rb:50:15:50:15 | 1 | type_tracker.rb:52:5:52:13 | ...[...] |
|
||||
| type_tracker.rb:50:17:50:17 | 2 | type_tracker.rb:34:1:53:3 | return return in throughArray |
|
||||
| type_tracker.rb:50:17:50:17 | 2 | type_tracker.rb:50:17:50:17 | 2 |
|
||||
| type_tracker.rb:50:17:50:17 | 2 | type_tracker.rb:52:5:52:13 | ...[...] |
|
||||
| type_tracker.rb:50:19:50:19 | 3 | type_tracker.rb:34:1:53:3 | return return in throughArray |
|
||||
| type_tracker.rb:50:19:50:19 | 3 | type_tracker.rb:50:19:50:19 | 3 |
|
||||
| type_tracker.rb:50:19:50:19 | 3 | type_tracker.rb:52:5:52:13 | ...[...] |
|
||||
| type_tracker.rb:50:21:50:21 | 4 | type_tracker.rb:34:1:53:3 | return return in throughArray |
|
||||
| type_tracker.rb:50:21:50:21 | 4 | type_tracker.rb:50:21:50:21 | 4 |
|
||||
| type_tracker.rb:50:21:50:21 | 4 | type_tracker.rb:52:5:52:13 | ...[...] |
|
||||
| type_tracker.rb:50:23:50:23 | 5 | type_tracker.rb:34:1:53:3 | return return in throughArray |
|
||||
| type_tracker.rb:50:23:50:23 | 5 | type_tracker.rb:50:23:50:23 | 5 |
|
||||
| type_tracker.rb:50:23:50:23 | 5 | type_tracker.rb:52:5:52:13 | ...[...] |
|
||||
| type_tracker.rb:50:25:50:25 | 6 | type_tracker.rb:34:1:53:3 | return return in throughArray |
|
||||
| type_tracker.rb:50:25:50:25 | 6 | type_tracker.rb:50:25:50:25 | 6 |
|
||||
| type_tracker.rb:50:25:50:25 | 6 | type_tracker.rb:52:5:52:13 | ...[...] |
|
||||
| type_tracker.rb:51:5:51:10 | [post] array4 | type_tracker.rb:51:5:51:10 | [post] array4 |
|
||||
| type_tracker.rb:51:5:51:10 | [post] array4 | type_tracker.rb:52:5:52:10 | array4 |
|
||||
| type_tracker.rb:51:5:51:13 | call to []= | type_tracker.rb:51:5:51:13 | call to []= |
|
||||
| type_tracker.rb:51:17:51:19 | __synth__0 | type_tracker.rb:51:17:51:19 | __synth__0 |
|
||||
| type_tracker.rb:52:5:52:13 | ...[...] | type_tracker.rb:34:1:53:3 | return return in throughArray |
|
||||
| type_tracker.rb:52:5:52:13 | ...[...] | type_tracker.rb:52:5:52:13 | ...[...] |
|
||||
forwardButNoBackwardFlow
|
||||
backwardButNoForwardFlow
|
||||
|
||||
@@ -19,3 +19,27 @@ query predicate trackEnd(LocalSourceNode src, DataFlow::Node dst) {
|
||||
end.flowsTo(dst)
|
||||
)
|
||||
}
|
||||
|
||||
predicate backtrack(LocalSourceNode sink, TypeBackTracker t, LocalSourceNode src) {
|
||||
t.start() and
|
||||
sink = src
|
||||
or
|
||||
exists(TypeBackTracker t2, LocalSourceNode mid |
|
||||
backtrack(sink, t2, mid) and
|
||||
src = mid.backtrack(t2, t)
|
||||
)
|
||||
}
|
||||
|
||||
predicate backtrackEnd(LocalSourceNode sink, LocalSourceNode src) {
|
||||
backtrack(sink, TypeBackTracker::end(), src)
|
||||
}
|
||||
|
||||
query predicate forwardButNoBackwardFlow(LocalSourceNode src, LocalSourceNode sink) {
|
||||
trackEnd(src, sink) and
|
||||
not backtrackEnd(sink, src)
|
||||
}
|
||||
|
||||
query predicate backwardButNoForwardFlow(LocalSourceNode src, LocalSourceNode sink) {
|
||||
backtrackEnd(sink, src) and
|
||||
not trackEnd(src, sink)
|
||||
}
|
||||
|
||||
@@ -30,3 +30,24 @@ end
|
||||
keyword(p1: 3, p2: 4)
|
||||
keyword(p2: 5, p1: 6)
|
||||
keyword(:p2 => 7, :p1 => 8)
|
||||
|
||||
def throughArray(obj, y, z)
|
||||
tmp = [obj]
|
||||
tmp[0]
|
||||
|
||||
array = [1,2,3,4,5,6]
|
||||
array[y] = obj
|
||||
array[0]
|
||||
|
||||
array2 = [1,2,3,4,5,6]
|
||||
array2[0] = obj
|
||||
array2[y]
|
||||
|
||||
array3 = [1,2,3,4,5,6]
|
||||
array3[0] = obj
|
||||
array3[1]
|
||||
|
||||
array4 = [1,2,3,4,5,6]
|
||||
array4[y] = obj
|
||||
array4[z]
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user