mirror of
https://github.com/github/codeql.git
synced 2025-12-18 01:33:15 +01:00
Ruby: Data-flow through hashes
This commit is contained in:
@@ -57,6 +57,24 @@ module SummaryComponent {
|
|||||||
*/
|
*/
|
||||||
SummaryComponent elementAny() { result = SC::content(TAnyElementContent()) }
|
SummaryComponent elementAny() { result = SC::content(TAnyElementContent()) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a summary component that represents an element in a collection at known
|
||||||
|
* integer index `lower` or above.
|
||||||
|
*/
|
||||||
|
SummaryComponent elementLowerBound(int lower) {
|
||||||
|
result = SC::content(TElementLowerBoundContent(lower))
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets a summary component that represents a value in a pair at an unknown key. */
|
||||||
|
SummaryComponent pairValueUnknown() {
|
||||||
|
result = SC::content(TSingletonContent(TUnknownPairValueContent()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets a summary component that represents a value in a pair at a known key. */
|
||||||
|
SummaryComponent pairValueKnown(ConstantValue cv) {
|
||||||
|
result = SC::content(TSingletonContent(TKnownPairValueContent(cv)))
|
||||||
|
}
|
||||||
|
|
||||||
/** Gets a summary component that represents the return value of a call. */
|
/** Gets a summary component that represents the return value of a call. */
|
||||||
SummaryComponent return() { result = SC::return(any(NormalReturnKind rk)) }
|
SummaryComponent return() { result = SC::return(any(NormalReturnKind rk)) }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ private import DataFlowPublic
|
|||||||
private import DataFlowDispatch
|
private import DataFlowDispatch
|
||||||
private import SsaImpl as SsaImpl
|
private import SsaImpl as SsaImpl
|
||||||
private import FlowSummaryImpl as FlowSummaryImpl
|
private import FlowSummaryImpl as FlowSummaryImpl
|
||||||
|
private import FlowSummaryImplSpecific as FlowSummaryImplSpecific
|
||||||
|
|
||||||
/** Gets the callable in which this node occurs. */
|
/** Gets the callable in which this node occurs. */
|
||||||
DataFlowCallable nodeGetEnclosingCallable(NodeImpl n) { result = n.getEnclosingCallable() }
|
DataFlowCallable nodeGetEnclosingCallable(NodeImpl n) { result = n.getEnclosingCallable() }
|
||||||
@@ -177,7 +178,7 @@ private class Argument extends CfgNodes::ExprCfgNode {
|
|||||||
exists(int i |
|
exists(int i |
|
||||||
this = call.getArgument(i) and
|
this = call.getArgument(i) and
|
||||||
not this.getExpr() instanceof BlockArgument and
|
not this.getExpr() instanceof BlockArgument and
|
||||||
not exists(this.getExpr().(Pair).getKey().getConstantValue().getSymbol()) and
|
not this.getExpr().(Pair).getKey().getConstantValue().isSymbol(_) and
|
||||||
arg.isPositional(i)
|
arg.isPositional(i)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
@@ -334,7 +335,10 @@ private module Cached {
|
|||||||
cached
|
cached
|
||||||
newtype TContentSet =
|
newtype TContentSet =
|
||||||
TSingletonContent(Content c) or
|
TSingletonContent(Content c) or
|
||||||
TAnyElementContent()
|
TAnyElementContent() or
|
||||||
|
TElementLowerBoundContent(int lower) {
|
||||||
|
FlowSummaryImplSpecific::ParsePositions::isParsedElementLowerBoundPosition(_, lower)
|
||||||
|
}
|
||||||
|
|
||||||
cached
|
cached
|
||||||
newtype TContent =
|
newtype TContent =
|
||||||
@@ -342,8 +346,10 @@ private module Cached {
|
|||||||
not cv.isInt(_) or
|
not cv.isInt(_) or
|
||||||
cv.getInt() in [0 .. 10]
|
cv.getInt() in [0 .. 10]
|
||||||
} or
|
} or
|
||||||
TFieldContent(string name) { name = any(InstanceVariable v).getName() } or
|
TUnknownElementContent() or
|
||||||
TUnknownElementContent()
|
TKnownPairValueContent(ConstantValue cv) or
|
||||||
|
TUnknownPairValueContent() or
|
||||||
|
TFieldContent(string name) { name = any(InstanceVariable v).getName() }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `e` is an `ExprNode` that may be returned by a call to `c`.
|
* Holds if `e` is an `ExprNode` that may be returned by a call to `c`.
|
||||||
@@ -362,6 +368,8 @@ private module Cached {
|
|||||||
|
|
||||||
class TElementContent = TKnownElementContent or TUnknownElementContent;
|
class TElementContent = TKnownElementContent or TUnknownElementContent;
|
||||||
|
|
||||||
|
class TPairValueContent = TKnownPairValueContent or TUnknownPairValueContent;
|
||||||
|
|
||||||
import Cached
|
import Cached
|
||||||
|
|
||||||
/** Holds if `n` should be hidden from path explanations. */
|
/** Holds if `n` should be hidden from path explanations. */
|
||||||
@@ -818,6 +826,25 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
|
|||||||
).getReceiver()
|
).getReceiver()
|
||||||
or
|
or
|
||||||
FlowSummaryImpl::Private::Steps::summaryStoreStep(node1, c, node2)
|
FlowSummaryImpl::Private::Steps::summaryStoreStep(node1, c, node2)
|
||||||
|
or
|
||||||
|
// Needed for pairs passed into method calls where the key is not a symbol,
|
||||||
|
// that is, where it is not a keyword argument.
|
||||||
|
node2.asExpr() =
|
||||||
|
any(CfgNodes::ExprNodes::PairCfgNode pair |
|
||||||
|
exists(CfgNodes::ExprCfgNode key |
|
||||||
|
key = pair.getKey() and
|
||||||
|
pair.getValue() = node1.asExpr()
|
||||||
|
|
|
||||||
|
exists(ConstantValue cv |
|
||||||
|
cv = key.getConstantValue() and
|
||||||
|
not cv.isSymbol(_) and // handled as a keyword argument
|
||||||
|
c.isSingleton(TKnownPairValueContent(cv))
|
||||||
|
)
|
||||||
|
or
|
||||||
|
not exists(key.getConstantValue()) and
|
||||||
|
c.isSingleton(TUnknownPairValueContent())
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -242,6 +242,35 @@ module Content {
|
|||||||
not exists(TKnownElementContent(cv)) and
|
not exists(TKnownElementContent(cv)) and
|
||||||
result = TUnknownElementContent()
|
result = TUnknownElementContent()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the constant value of `e`, which corresponds to a valid known
|
||||||
|
* element index. Unlike calling simply `e.getConstantValue()`, this
|
||||||
|
* excludes negative array indices.
|
||||||
|
*/
|
||||||
|
ConstantValue getKnownElementIndex(Expr e) {
|
||||||
|
result = getElementContent(e.getConstantValue()).(KnownElementContent).getIndex()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A value in a pair with a known or unknown key. */
|
||||||
|
class PairValueContent extends Content, TPairValueContent { }
|
||||||
|
|
||||||
|
/** A value in a pair with a known key. */
|
||||||
|
class KnownPairValueContent extends PairValueContent, TKnownPairValueContent {
|
||||||
|
private ConstantValue cv;
|
||||||
|
|
||||||
|
KnownPairValueContent() { this = TKnownPairValueContent(cv) }
|
||||||
|
|
||||||
|
/** Gets the index in the collection. */
|
||||||
|
ConstantValue getIndex() { result = cv }
|
||||||
|
|
||||||
|
override string toString() { result = "pair " + cv }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A value in a pair with an unknown key. */
|
||||||
|
class UnknownPairValueContent extends PairValueContent, TUnknownPairValueContent {
|
||||||
|
override string toString() { result = "pair" }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -257,6 +286,12 @@ class ContentSet extends TContentSet {
|
|||||||
/** Holds if this content set represents all `ElementContent`s. */
|
/** Holds if this content set represents all `ElementContent`s. */
|
||||||
predicate isAnyElement() { this = TAnyElementContent() }
|
predicate isAnyElement() { this = TAnyElementContent() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if this content set represents all `KnownElementContent`s where
|
||||||
|
* the index is an integer greater than or equal to `lower`.
|
||||||
|
*/
|
||||||
|
predicate isElementLowerBound(int lower) { this = TElementLowerBoundContent(lower) }
|
||||||
|
|
||||||
/** Gets a textual representation of this content set. */
|
/** Gets a textual representation of this content set. */
|
||||||
string toString() {
|
string toString() {
|
||||||
exists(Content c |
|
exists(Content c |
|
||||||
@@ -265,7 +300,12 @@ class ContentSet extends TContentSet {
|
|||||||
)
|
)
|
||||||
or
|
or
|
||||||
this.isAnyElement() and
|
this.isAnyElement() and
|
||||||
result = "any array element"
|
result = "any element"
|
||||||
|
or
|
||||||
|
exists(int lower |
|
||||||
|
this.isElementLowerBound(lower) and
|
||||||
|
result = lower + ".."
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Gets a content that may be stored into when storing into this set. */
|
/** Gets a content that may be stored into when storing into this set. */
|
||||||
@@ -274,6 +314,9 @@ class ContentSet extends TContentSet {
|
|||||||
or
|
or
|
||||||
this.isAnyElement() and
|
this.isAnyElement() and
|
||||||
result = TUnknownElementContent()
|
result = TUnknownElementContent()
|
||||||
|
or
|
||||||
|
this.isElementLowerBound(_) and
|
||||||
|
result = TUnknownElementContent()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Gets a content that may be read from when reading from this set. */
|
/** Gets a content that may be read from when reading from this set. */
|
||||||
@@ -282,6 +325,12 @@ class ContentSet extends TContentSet {
|
|||||||
or
|
or
|
||||||
this.isAnyElement() and
|
this.isAnyElement() and
|
||||||
result instanceof Content::ElementContent
|
result instanceof Content::ElementContent
|
||||||
|
or
|
||||||
|
exists(int lower, int i |
|
||||||
|
this.isElementLowerBound(lower) and
|
||||||
|
result.(Content::KnownElementContent).getIndex().isInt(i) and
|
||||||
|
i >= lower
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -67,6 +67,11 @@ private SummaryComponent interpretElementArg(string arg) {
|
|||||||
arg = "any" and
|
arg = "any" and
|
||||||
result = FlowSummary::SummaryComponent::elementAny()
|
result = FlowSummary::SummaryComponent::elementAny()
|
||||||
or
|
or
|
||||||
|
exists(int lower |
|
||||||
|
ParsePositions::isParsedElementLowerBoundPosition(arg, lower) and
|
||||||
|
result = FlowSummary::SummaryComponent::elementLowerBound(lower)
|
||||||
|
)
|
||||||
|
or
|
||||||
exists(ConstantValue cv | result = FlowSummary::SummaryComponent::elementKnown(cv) |
|
exists(ConstantValue cv | result = FlowSummary::SummaryComponent::elementKnown(cv) |
|
||||||
cv.isInt(AccessPath::parseInt(arg))
|
cv.isInt(AccessPath::parseInt(arg))
|
||||||
or
|
or
|
||||||
@@ -103,6 +108,16 @@ SummaryComponent interpretComponentSpecific(AccessPathToken c) {
|
|||||||
interpretElementArg(c.getAnArgument("WithoutElement")) and
|
interpretElementArg(c.getAnArgument("WithoutElement")) and
|
||||||
result = FlowSummary::SummaryComponent::withoutContent(cs)
|
result = FlowSummary::SummaryComponent::withoutContent(cs)
|
||||||
)
|
)
|
||||||
|
or
|
||||||
|
exists(string arg | arg = c.getAnArgument("PairValue") |
|
||||||
|
arg = "?" and
|
||||||
|
result = FlowSummary::SummaryComponent::pairValueUnknown()
|
||||||
|
or
|
||||||
|
exists(ConstantValue cv |
|
||||||
|
result = FlowSummary::SummaryComponent::pairValueKnown(cv) and
|
||||||
|
cv.serialize() = arg
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Gets the textual representation of a summary component in the format used for flow summaries. */
|
/** Gets the textual representation of a summary component in the format used for flow summaries. */
|
||||||
@@ -217,6 +232,13 @@ module ParsePositions {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private predicate isElementBody(string body) {
|
||||||
|
exists(AccessPathToken tok |
|
||||||
|
tok.getName() = "Element" and
|
||||||
|
body = tok.getAnArgument()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
predicate isParsedParameterPosition(string c, int i) {
|
predicate isParsedParameterPosition(string c, int i) {
|
||||||
isParamBody(c) and
|
isParamBody(c) and
|
||||||
i = AccessPath::parseInt(c)
|
i = AccessPath::parseInt(c)
|
||||||
@@ -241,6 +263,11 @@ module ParsePositions {
|
|||||||
isArgBody(c) and
|
isArgBody(c) and
|
||||||
c = paramName + ":"
|
c = paramName + ":"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
predicate isParsedElementLowerBoundPosition(string c, int lower) {
|
||||||
|
isElementBody(c) and
|
||||||
|
lower = AccessPath::parseLowerBound(c)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Gets the argument position obtained by parsing `X` in `Parameter[X]`. */
|
/** Gets the argument position obtained by parsing `X` in `Parameter[X]`. */
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import core.Object::Object
|
|||||||
import core.Kernel::Kernel
|
import core.Kernel::Kernel
|
||||||
import core.Module
|
import core.Module
|
||||||
import core.Array
|
import core.Array
|
||||||
|
import core.Hash
|
||||||
import core.String
|
import core.String
|
||||||
import core.Regexp
|
import core.Regexp
|
||||||
import core.IO
|
import core.IO
|
||||||
|
|||||||
@@ -6,10 +6,17 @@ private import codeql.ruby.DataFlow
|
|||||||
private import codeql.ruby.dataflow.FlowSummary
|
private import codeql.ruby.dataflow.FlowSummary
|
||||||
private import codeql.ruby.dataflow.internal.DataFlowDispatch
|
private import codeql.ruby.dataflow.internal.DataFlowDispatch
|
||||||
|
|
||||||
|
/** An array index that may be tracked precisely in data flow. */
|
||||||
private class ArrayIndex extends int {
|
private class ArrayIndex extends int {
|
||||||
ArrayIndex() { this = any(DataFlow::Content::KnownElementContent c).getIndex().getInt() }
|
ArrayIndex() { this = any(DataFlow::Content::KnownElementContent c).getIndex().getInt() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string lastBlockParam(MethodCall mc, string name, int lastBlockParam) {
|
||||||
|
mc.getMethodName() = name and
|
||||||
|
result = name + "(" + lastBlockParam + ")" and
|
||||||
|
lastBlockParam = mc.getBlock().getNumberOfParameters() - 1
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides flow summaries for the `Array` class.
|
* Provides flow summaries for the `Array` class.
|
||||||
*
|
*
|
||||||
@@ -19,23 +26,9 @@ private class ArrayIndex extends int {
|
|||||||
* module instead.
|
* module instead.
|
||||||
*/
|
*/
|
||||||
module Array {
|
module Array {
|
||||||
/**
|
private predicate isUnknownElementIndex(Expr e) {
|
||||||
* Gets the constant value of `arg`, which corresponds to a valid known
|
not exists(DataFlow::Content::getKnownElementIndex(e)) and
|
||||||
* element index. Unlike calling simply `arg.getConstantValue()`, this
|
not e instanceof RangeLiteral
|
||||||
* excludes negative array indices.
|
|
||||||
*/
|
|
||||||
bindingset[arg]
|
|
||||||
private ConstantValue getKnownElementIndex(Expr arg) {
|
|
||||||
result =
|
|
||||||
DataFlow::Content::getElementContent(arg.getConstantValue())
|
|
||||||
.(DataFlow::Content::KnownElementContent)
|
|
||||||
.getIndex()
|
|
||||||
}
|
|
||||||
|
|
||||||
bindingset[arg]
|
|
||||||
private predicate isUnknownElementIndex(Expr arg) {
|
|
||||||
not exists(getKnownElementIndex(arg)) and
|
|
||||||
not arg instanceof RangeLiteral
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ArrayLiteralSummary extends SummarizedCallable {
|
private class ArrayLiteralSummary extends SummarizedCallable {
|
||||||
@@ -195,11 +188,12 @@ module Array {
|
|||||||
ElementReferenceReadKnownSummary() {
|
ElementReferenceReadKnownSummary() {
|
||||||
this = methodName + "(" + cv.serialize() + ")" and
|
this = methodName + "(" + cv.serialize() + ")" and
|
||||||
mc.getNumberOfArguments() = 1 and
|
mc.getNumberOfArguments() = 1 and
|
||||||
cv = getKnownElementIndex(mc.getArgument(0))
|
cv = DataFlow::Content::getKnownElementIndex(mc.getArgument(0)) and
|
||||||
|
if methodName = "slice" then cv.isInt(_) else any()
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self].Element[" + [cv.serialize(), "?"] + "]" and
|
input = "Argument[self].Element[?," + cv.serialize() + "]" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
}
|
}
|
||||||
@@ -230,7 +224,7 @@ module Array {
|
|||||||
|
|
||||||
ElementReferenceRangeReadKnownSummary() {
|
ElementReferenceRangeReadKnownSummary() {
|
||||||
mc.getNumberOfArguments() = 2 and
|
mc.getNumberOfArguments() = 2 and
|
||||||
start = getKnownElementIndex(mc.getArgument(0)).getInt() and
|
start = DataFlow::Content::getKnownElementIndex(mc.getArgument(0)).getInt() and
|
||||||
exists(int length | mc.getArgument(1).getConstantValue().isInt(length) |
|
exists(int length | mc.getArgument(1).getConstantValue().isInt(length) |
|
||||||
end = (start + length - 1) and
|
end = (start + length - 1) and
|
||||||
this = "[](" + start + ", " + length + ")"
|
this = "[](" + start + ", " + length + ")"
|
||||||
@@ -260,8 +254,8 @@ module Array {
|
|||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
preservesValue = true and
|
preservesValue = true and
|
||||||
(
|
(
|
||||||
input = "Argument[self].Element[?]" and
|
input = "Argument[self].WithElement[?]" and
|
||||||
output = "ReturnValue.Element[?]"
|
output = "ReturnValue"
|
||||||
or
|
or
|
||||||
exists(ArrayIndex i | i >= start and i <= end |
|
exists(ArrayIndex i | i >= start and i <= end |
|
||||||
input = "Argument[self].Element[" + i + "]" and
|
input = "Argument[self].Element[" + i + "]" and
|
||||||
@@ -281,8 +275,8 @@ module Array {
|
|||||||
(
|
(
|
||||||
mc.getNumberOfArguments() = 2 and
|
mc.getNumberOfArguments() = 2 and
|
||||||
(
|
(
|
||||||
not mc.getArgument(0).getConstantValue().isInt(_) or
|
not exists(mc.getArgument(0).getConstantValue()) or
|
||||||
not mc.getArgument(1).getConstantValue().isInt(_)
|
not exists(mc.getArgument(1).getConstantValue())
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
mc.getNumberOfArguments() = 1 and
|
mc.getNumberOfArguments() = 1 and
|
||||||
@@ -296,7 +290,7 @@ module Array {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self].Element[any]" and
|
input = "Argument[self].Element[?,0..]" and
|
||||||
output = "ReturnValue.Element[?]" and
|
output = "ReturnValue.Element[?]" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
}
|
}
|
||||||
@@ -318,7 +312,7 @@ module Array {
|
|||||||
|
|
||||||
ElementReferenceStoreKnownSummary() {
|
ElementReferenceStoreKnownSummary() {
|
||||||
mc.getNumberOfArguments() = 2 and
|
mc.getNumberOfArguments() = 2 and
|
||||||
cv = getKnownElementIndex(mc.getArgument(0)) and
|
cv = DataFlow::Content::getKnownElementIndex(mc.getArgument(0)) and
|
||||||
this = "[" + cv.serialize() + "]="
|
this = "[" + cv.serialize() + "]="
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -382,8 +376,8 @@ module Array {
|
|||||||
AssocSummary() { this = ["assoc", "rassoc"] }
|
AssocSummary() { this = ["assoc", "rassoc"] }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self].Element[any].Element[any]" and
|
input = "Argument[self].Element[any].WithElement[any]" and
|
||||||
output = "ReturnValue.Element[?]" and
|
output = "ReturnValue" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -403,11 +397,11 @@ module Array {
|
|||||||
AtKnownSummary() {
|
AtKnownSummary() {
|
||||||
this = "at(" + cv.serialize() + "]" and
|
this = "at(" + cv.serialize() + "]" and
|
||||||
mc.getNumberOfArguments() = 1 and
|
mc.getNumberOfArguments() = 1 and
|
||||||
cv = getKnownElementIndex(mc.getArgument(0))
|
cv = DataFlow::Content::getKnownElementIndex(mc.getArgument(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self].Element[" + [cv.serialize(), "?"] + "]" and
|
input = "Argument[self].Element[" + cv.serialize() + ",?]" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
}
|
}
|
||||||
@@ -490,7 +484,7 @@ module Array {
|
|||||||
CompactBangSummary() { this = "compact!" }
|
CompactBangSummary() { this = "compact!" }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self].Element[any]" and
|
input = "Argument[self].Element[?,0..]" and
|
||||||
output = ["ReturnValue.Element[?]", "Argument[self].Element[?]"] and
|
output = ["ReturnValue.Element[?]", "Argument[self].Element[?]"] and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
}
|
}
|
||||||
@@ -519,19 +513,82 @@ module Array {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DeleteSummary extends SimpleSummarizedCallable {
|
abstract private class DeleteSummary extends SummarizedCallable {
|
||||||
DeleteSummary() { this = "delete" }
|
MethodCall mc;
|
||||||
|
|
||||||
|
bindingset[this]
|
||||||
|
DeleteSummary() { mc.getMethodName() = "delete" }
|
||||||
|
|
||||||
|
final override MethodCall getACall() { result = mc }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
(
|
(
|
||||||
input = "Argument[self].Element[any]" and
|
input = "Argument[self].WithoutElement[any]" and
|
||||||
output = ["Argument[self].Element[?]", "ReturnValue"]
|
output = "Argument[self]"
|
||||||
|
or
|
||||||
|
input = "Argument[self].WithElement[?]" and
|
||||||
|
output = "Argument[self]"
|
||||||
or
|
or
|
||||||
input = "Argument[block].ReturnValue" and
|
input = "Argument[block].ReturnValue" and
|
||||||
output = "ReturnValue"
|
output = "ReturnValue"
|
||||||
|
) and
|
||||||
|
preservesValue = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DeleteKnownSummary extends DeleteSummary {
|
||||||
|
private ConstantValue cv;
|
||||||
|
|
||||||
|
DeleteKnownSummary() {
|
||||||
|
this = "delete(" + cv.serialize() + ")" and
|
||||||
|
mc.getArgument(0).getConstantValue() = cv
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
|
super.propagatesFlowExt(input, output, preservesValue)
|
||||||
|
or
|
||||||
|
(
|
||||||
|
(
|
||||||
|
if cv.isInt(_)
|
||||||
|
then
|
||||||
|
// array indices may get shifted
|
||||||
|
input = "Argument[self].WithoutElement[" + cv.serialize() + "].Element[0..]" and
|
||||||
|
output = "Argument[self].Element[?]"
|
||||||
|
or
|
||||||
|
input = "Argument[self].WithoutElement[0..]" and
|
||||||
|
output = "Argument[self]"
|
||||||
|
else (
|
||||||
|
input = "Argument[self].WithoutElement[" + cv.serialize() + "]" and
|
||||||
|
output = "Argument[self]"
|
||||||
|
)
|
||||||
|
)
|
||||||
or
|
or
|
||||||
input = "Argument[self].WithoutElement[any]" and
|
input = "Argument[self].Element[" + cv.serialize() + ",?]" and
|
||||||
|
output = "ReturnValue"
|
||||||
|
) and
|
||||||
|
preservesValue = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DeleteUnknownSummary extends DeleteSummary {
|
||||||
|
DeleteUnknownSummary() {
|
||||||
|
this = "delete" and
|
||||||
|
not exists(DataFlow::Content::getKnownElementIndex(mc.getArgument(0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
|
super.propagatesFlowExt(input, output, preservesValue)
|
||||||
|
or
|
||||||
|
(
|
||||||
|
// array indices may get shifted
|
||||||
|
input = "Argument[self].Element[0..]" and
|
||||||
|
output = "Argument[self].Element[?]"
|
||||||
|
or
|
||||||
|
input = "Argument[self].WithoutElement[0..].WithElement[any]" and
|
||||||
output = "Argument[self]"
|
output = "Argument[self]"
|
||||||
|
or
|
||||||
|
input = "Argument[self].Element[any]" and
|
||||||
|
output = "ReturnValue"
|
||||||
) and
|
) and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
}
|
}
|
||||||
@@ -603,17 +660,26 @@ module Array {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DeleteIfSummary extends SimpleSummarizedCallable {
|
private class DeleteIfSummary extends SummarizedCallable {
|
||||||
DeleteIfSummary() { this = "delete_if" }
|
MethodCall mc;
|
||||||
|
int lastBlockParam;
|
||||||
|
|
||||||
|
DeleteIfSummary() { this = lastBlockParam(mc, "delete_if", lastBlockParam) }
|
||||||
|
|
||||||
|
final override MethodCall getACall() { result = mc }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self].Element[any]" and
|
(
|
||||||
output =
|
input = "Argument[self].Element[any]" and
|
||||||
["Argument[block].Parameter[0]", "ReturnValue.Element[?]", "Argument[self].Element[?]"] and
|
output = "Argument[block].Parameter[" + lastBlockParam + "]"
|
||||||
preservesValue = true
|
or
|
||||||
or
|
// array indices may get shifted
|
||||||
input = "Argument[self].WithoutElement[any]" and
|
input = "Argument[self].Element[0..]" and
|
||||||
output = "Argument[self]" and
|
output = ["ReturnValue.Element[?]", "Argument[self].Element[?]"]
|
||||||
|
or
|
||||||
|
input = "Argument[self].WithoutElement[0..].WithElement[any]" and
|
||||||
|
output = ["ReturnValue", "Argument[self]"]
|
||||||
|
) and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -631,9 +697,9 @@ module Array {
|
|||||||
private string getDigArg(MethodCall dig, int i) {
|
private string getDigArg(MethodCall dig, int i) {
|
||||||
dig.getMethodName() = "dig" and
|
dig.getMethodName() = "dig" and
|
||||||
exists(Expr arg | arg = dig.getArgument(i) |
|
exists(Expr arg | arg = dig.getArgument(i) |
|
||||||
result = getKnownElementIndex(arg).(ConstantValue::ConstantIntegerValue).serialize()
|
result = DataFlow::Content::getKnownElementIndex(arg).serialize()
|
||||||
or
|
or
|
||||||
not getKnownElementIndex(arg).isInt(_) and
|
not exists(DataFlow::Content::getKnownElementIndex(arg)) and
|
||||||
result = "?"
|
result = "?"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -647,7 +713,7 @@ module Array {
|
|||||||
private string buildDigInputSpecComponent(RelevantDigMethodCall dig, int i) {
|
private string buildDigInputSpecComponent(RelevantDigMethodCall dig, int i) {
|
||||||
exists(string s |
|
exists(string s |
|
||||||
s = getDigArg(dig, i) and
|
s = getDigArg(dig, i) and
|
||||||
if s = "?" then result = "any" else result = [s, "?"]
|
if s = "?" then result = "any" else result = s + ",?"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -683,14 +749,24 @@ module Array {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class EachSummary extends SimpleSummarizedCallable {
|
private class EachSummary extends SummarizedCallable {
|
||||||
// `each` and `reverse_each` are the same in terms of flow inputs/outputs.
|
MethodCall mc;
|
||||||
EachSummary() { this = ["each", "reverse_each"] }
|
int lastBlockParam;
|
||||||
|
|
||||||
|
EachSummary() {
|
||||||
|
exists(string name |
|
||||||
|
// `each` and `reverse_each` are the same in terms of flow inputs/outputs.
|
||||||
|
name = ["each", "reverse_each"] and
|
||||||
|
this = lastBlockParam(mc, name, lastBlockParam)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
final override MethodCall getACall() { result = mc }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
(
|
(
|
||||||
input = "Argument[self].Element[any]" and
|
input = "Argument[self].Element[any]" and
|
||||||
output = "Argument[block].Parameter[0]"
|
output = "Argument[block].Parameter[" + lastBlockParam + "]"
|
||||||
or
|
or
|
||||||
input = "Argument[self].WithElement[any]" and
|
input = "Argument[self].WithElement[any]" and
|
||||||
output = "ReturnValue"
|
output = "ReturnValue"
|
||||||
@@ -700,7 +776,7 @@ module Array {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class EachIndexSummary extends SimpleSummarizedCallable {
|
private class EachIndexSummary extends SimpleSummarizedCallable {
|
||||||
EachIndexSummary() { this = "each_index" }
|
EachIndexSummary() { this = ["each_index", "each_key"] }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self].WithElement[any]" and
|
input = "Argument[self].WithElement[any]" and
|
||||||
@@ -719,17 +795,17 @@ module Array {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class FetchKnownSummary extends FetchSummary {
|
private class FetchKnownSummary extends FetchSummary {
|
||||||
int i;
|
ConstantValue cv;
|
||||||
|
|
||||||
FetchKnownSummary() {
|
FetchKnownSummary() {
|
||||||
this = "fetch(" + i + ")" and
|
this = "fetch(" + cv.serialize() + ")" and
|
||||||
mc.getArgument(0).getConstantValue().isInt(i) and
|
cv = mc.getArgument(0).getConstantValue() and
|
||||||
i >= 0
|
not cv.isInt(any(int i | i < 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
(
|
(
|
||||||
input = "Argument[self].Element[?," + i + "]" and
|
input = "Argument[self].Element[?," + cv.serialize() + "]" and
|
||||||
output = "ReturnValue"
|
output = "ReturnValue"
|
||||||
or
|
or
|
||||||
input = "Argument[0]" and
|
input = "Argument[0]" and
|
||||||
@@ -745,7 +821,9 @@ module Array {
|
|||||||
private class FetchUnknownSummary extends FetchSummary {
|
private class FetchUnknownSummary extends FetchSummary {
|
||||||
FetchUnknownSummary() {
|
FetchUnknownSummary() {
|
||||||
this = "fetch(index)" and
|
this = "fetch(index)" and
|
||||||
not exists(int i | mc.getArgument(0).getConstantValue().isInt(i) and i >= 0)
|
not exists(ConstantValue cv |
|
||||||
|
cv = mc.getArgument(0).getConstantValue() and not cv.isInt(any(int i | i < 0))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
@@ -936,17 +1014,29 @@ module Array {
|
|||||||
override MethodCall getACall() { result = mc }
|
override MethodCall getACall() { result = mc }
|
||||||
}
|
}
|
||||||
|
|
||||||
private class KeepIfSummary extends SimpleSummarizedCallable {
|
private class KeepIfSummary extends SummarizedCallable {
|
||||||
KeepIfSummary() { this = "keep_if" }
|
MethodCall mc;
|
||||||
|
int lastBlockParam;
|
||||||
|
|
||||||
|
KeepIfSummary() { this = lastBlockParam(mc, "keep_if", lastBlockParam) }
|
||||||
|
|
||||||
|
final override MethodCall getACall() { result = mc }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self].Element[any]" and
|
(
|
||||||
output =
|
input = "Argument[self].WithoutElement[any]" and
|
||||||
["ReturnValue.Element[?]", "Argument[self].Element[?]", "Argument[block].Parameter[0]"] and
|
output = "Argument[self]"
|
||||||
preservesValue = true
|
or
|
||||||
or
|
// array indices may get shifted
|
||||||
input = "Argument[self].WithoutElement[any]" and
|
input = "Argument[self].Element[0..]" and
|
||||||
output = "Argument[self]" and
|
output = ["ReturnValue.Element[?]", "Argument[self].Element[?]"]
|
||||||
|
or
|
||||||
|
input = "Argument[self].WithoutElement[0..].WithElement[any]" and
|
||||||
|
output = ["ReturnValue", "Argument[self]"]
|
||||||
|
or
|
||||||
|
input = "Argument[self].Element[any]" and
|
||||||
|
output = "Argument[block].Parameter[" + lastBlockParam + "]"
|
||||||
|
) and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1107,17 +1197,26 @@ module Array {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class RejectBangSummary extends SimpleSummarizedCallable {
|
private class RejectBangSummary extends SummarizedCallable {
|
||||||
RejectBangSummary() { this = "reject!" }
|
MethodCall mc;
|
||||||
|
int lastBlockParam;
|
||||||
|
|
||||||
|
RejectBangSummary() { this = lastBlockParam(mc, "reject!", lastBlockParam) }
|
||||||
|
|
||||||
|
final override MethodCall getACall() { result = mc }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self].Element[any]" and
|
(
|
||||||
output =
|
// array indices may get shifted
|
||||||
["ReturnValue.Element[?]", "Argument[self].Element[?]", "Argument[block].Parameter[0]"] and
|
input = "Argument[self].Element[0..]" and
|
||||||
preservesValue = true
|
output = ["ReturnValue.Element[?]", "Argument[self].Element[?]"]
|
||||||
or
|
or
|
||||||
input = "Argument[self].WithoutElement[any]" and
|
input = "Argument[self].WithoutElement[0..].WithElement[any]" and
|
||||||
output = "Argument[self]" and
|
output = ["ReturnValue", "Argument[self]"]
|
||||||
|
or
|
||||||
|
input = "Argument[self].Element[any]" and
|
||||||
|
output = "Argument[block].Parameter[" + lastBlockParam + "]"
|
||||||
|
) and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1169,7 +1268,7 @@ module Array {
|
|||||||
private int c;
|
private int c;
|
||||||
|
|
||||||
RotateKnownSummary() {
|
RotateKnownSummary() {
|
||||||
getKnownElementIndex(mc.getArgument(0)).isInt(c) and
|
DataFlow::Content::getKnownElementIndex(mc.getArgument(0)).isInt(c) and
|
||||||
this = "rotate(" + c + ")"
|
this = "rotate(" + c + ")"
|
||||||
or
|
or
|
||||||
not exists(mc.getArgument(0)) and c = 1 and this = "rotate"
|
not exists(mc.getArgument(0)) and c = 1 and this = "rotate"
|
||||||
@@ -1197,7 +1296,7 @@ module Array {
|
|||||||
RotateUnknownSummary() {
|
RotateUnknownSummary() {
|
||||||
this = "rotate(index)" and
|
this = "rotate(index)" and
|
||||||
exists(mc.getArgument(0)) and
|
exists(mc.getArgument(0)) and
|
||||||
not getKnownElementIndex(mc.getArgument(0)).isInt(_)
|
not DataFlow::Content::getKnownElementIndex(mc.getArgument(0)).isInt(_)
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
@@ -1267,18 +1366,34 @@ module Array {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SelectBangSummary extends SimpleSummarizedCallable {
|
private class SelectBangSummary extends SummarizedCallable {
|
||||||
// `filter!` is an alias for `select!`
|
MethodCall mc;
|
||||||
SelectBangSummary() { this = ["select!", "filter!"] }
|
int lastBlockParam;
|
||||||
|
|
||||||
|
SelectBangSummary() {
|
||||||
|
exists(string name |
|
||||||
|
name = ["select!", "filter!"] and
|
||||||
|
this = lastBlockParam(mc, name, lastBlockParam)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
final override MethodCall getACall() { result = mc }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self].Element[any]" and
|
(
|
||||||
output =
|
input = "Argument[self].Element[any]" and
|
||||||
["Argument[block].Parameter[0]", "Argument[self].Element[?]", "ReturnValue.Element[?]"] and
|
output = "Argument[block].Parameter[" + lastBlockParam + "]"
|
||||||
preservesValue = true
|
or
|
||||||
or
|
// array indices may get shifted
|
||||||
input = "Argument[self].WithoutElement[any]" and
|
input = "Argument[self].Element[0..]" and
|
||||||
output = "Argument[self]" and
|
output = ["ReturnValue.Element[?]", "Argument[self].Element[?]"]
|
||||||
|
or
|
||||||
|
input = "Argument[self].WithoutElement[0..].WithElement[any]" and
|
||||||
|
output = ["ReturnValue", "Argument[self]"]
|
||||||
|
or
|
||||||
|
input = "Argument[self].WithoutElement[any]" and
|
||||||
|
output = "Argument[self]"
|
||||||
|
) and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1306,8 +1421,18 @@ module Array {
|
|||||||
or
|
or
|
||||||
preservesValue = true and
|
preservesValue = true and
|
||||||
(
|
(
|
||||||
|
input = "Argument[self].WithoutElement[0..]" and
|
||||||
|
output = "Argument[self]"
|
||||||
|
or
|
||||||
input = "Argument[self].Element[?]" and
|
input = "Argument[self].Element[?]" and
|
||||||
output = ["ReturnValue", "Argument[self].Element[?]"]
|
output =
|
||||||
|
[
|
||||||
|
"ReturnValue", // array
|
||||||
|
"ReturnValue.Element[1]" // hash
|
||||||
|
]
|
||||||
|
or
|
||||||
|
input = "Argument[self].WithoutElement[0..].WithoutElement[?].Element[any]" and
|
||||||
|
output = "ReturnValue.Element[1]"
|
||||||
or
|
or
|
||||||
exists(ArrayIndex i | input = "Argument[self].Element[" + i + "]" |
|
exists(ArrayIndex i | input = "Argument[self].Element[" + i + "]" |
|
||||||
i = 0 and output = "ReturnValue"
|
i = 0 and output = "ReturnValue"
|
||||||
@@ -1403,7 +1528,7 @@ module Array {
|
|||||||
SliceBangKnownIndexSummary() {
|
SliceBangKnownIndexSummary() {
|
||||||
this = "slice!(" + n + ")" and
|
this = "slice!(" + n + ")" and
|
||||||
mc.getNumberOfArguments() = 1 and
|
mc.getNumberOfArguments() = 1 and
|
||||||
n = getKnownElementIndex(mc.getArgument(0)).getInt()
|
n = DataFlow::Content::getKnownElementIndex(mc.getArgument(0)).getInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
@@ -1461,7 +1586,7 @@ module Array {
|
|||||||
|
|
||||||
SliceBangRangeKnownSummary() {
|
SliceBangRangeKnownSummary() {
|
||||||
mc.getNumberOfArguments() = 2 and
|
mc.getNumberOfArguments() = 2 and
|
||||||
start = getKnownElementIndex(mc.getArgument(0)).getInt() and
|
start = DataFlow::Content::getKnownElementIndex(mc.getArgument(0)).getInt() and
|
||||||
exists(int length | mc.getArgument(1).getConstantValue().isInt(length) |
|
exists(int length | mc.getArgument(1).getConstantValue().isInt(length) |
|
||||||
end = (start + length - 1) and
|
end = (start + length - 1) and
|
||||||
this = "slice!(" + start + ", " + length + ")"
|
this = "slice!(" + start + ", " + length + ")"
|
||||||
@@ -1633,47 +1758,43 @@ module Array {
|
|||||||
override Call getACall() { result = mc }
|
override Call getACall() { result = mc }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private string getValuesAtComponent(MethodCall mc, int i) {
|
||||||
* A call to `values_at` where all the arguments are known, positive integers.
|
mc.getMethodName() = "values_at" and
|
||||||
*/
|
result = DataFlow::Content::getKnownElementIndex(mc.getArgument(i)).serialize()
|
||||||
|
}
|
||||||
|
|
||||||
private class ValuesAtKnownSummary extends ValuesAtSummary {
|
private class ValuesAtKnownSummary extends ValuesAtSummary {
|
||||||
ValuesAtKnownSummary() {
|
ValuesAtKnownSummary() {
|
||||||
this = "values_at(known)" and
|
this =
|
||||||
forall(int i | i in [0 .. mc.getNumberOfArguments() - 1] |
|
"values_at(" +
|
||||||
getKnownElementIndex(mc.getArgument(i)).isInt(_)
|
strictconcat(int i |
|
||||||
)
|
exists(mc.getArgument(i))
|
||||||
|
|
|
||||||
|
getValuesAtComponent(mc, i), "," order by i
|
||||||
|
) + ")"
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
preservesValue = true and
|
super.propagatesFlowExt(input, output, preservesValue)
|
||||||
(
|
or
|
||||||
input = "Argument[self].Element[?]" and
|
exists(string s, int i |
|
||||||
output = "ReturnValue.Element[?]"
|
s = getValuesAtComponent(mc, i) and
|
||||||
or
|
input = "Argument[self].Element[" + s + "]" and
|
||||||
exists(ArrayIndex elementIndex, int argIndex |
|
output = "ReturnValue.Element[" + i + "]"
|
||||||
argIndex in [0 .. mc.getNumberOfArguments() - 1] and
|
) and
|
||||||
getKnownElementIndex(mc.getArgument(argIndex)).isInt(elementIndex)
|
preservesValue = true
|
||||||
|
|
|
||||||
input = "Argument[self].Element[" + elementIndex + "]" and
|
|
||||||
output = "ReturnValue.Element[" + argIndex + "]"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* A call to `values_at` where at least one of the arguments is not a known,
|
|
||||||
* positive integer.
|
|
||||||
*/
|
|
||||||
private class ValuesAtUnknownSummary extends ValuesAtSummary {
|
private class ValuesAtUnknownSummary extends ValuesAtSummary {
|
||||||
ValuesAtUnknownSummary() {
|
ValuesAtUnknownSummary() {
|
||||||
this = "values_at(unknown)" and
|
exists(int i | exists(mc.getArgument(i)) | not exists(getValuesAtComponent(mc, i))) and
|
||||||
exists(int i | i in [0 .. mc.getNumberOfArguments() - 1] |
|
this = "values_at(?)"
|
||||||
not getKnownElementIndex(mc.getArgument(i)).isInt(_)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
|
super.propagatesFlowExt(input, output, preservesValue)
|
||||||
|
or
|
||||||
input = "Argument[self].Element[any]" and
|
input = "Argument[self].Element[any]" and
|
||||||
output = "ReturnValue.Element[?]" and
|
output = "ReturnValue.Element[?]" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
@@ -1742,9 +1863,16 @@ module Enumerable {
|
|||||||
CompactSummary() { this = "compact" }
|
CompactSummary() { this = "compact" }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self].Element[any]" and
|
input = "Argument[self].Element[?,0..]" and
|
||||||
output = "ReturnValue.Element[?]" and
|
output = "ReturnValue.Element[?]" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
or
|
||||||
|
exists(ConstantValue cv |
|
||||||
|
not cv.isInt(_) and
|
||||||
|
input = "Argument[self].WithElement[" + cv.serialize() + "]" and
|
||||||
|
output = "ReturnValue" and
|
||||||
|
preservesValue = true
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2254,33 +2382,75 @@ module Enumerable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class QuerySummary extends SimpleSummarizedCallable {
|
private class QuerySummary extends SummarizedCallable {
|
||||||
QuerySummary() { this = ["all?", "any?", "none?", "one?"] }
|
MethodCall mc;
|
||||||
|
int lastBlockParam;
|
||||||
|
|
||||||
|
QuerySummary() {
|
||||||
|
exists(string name |
|
||||||
|
name = ["all?", "any?", "none?", "one?"] and
|
||||||
|
this = lastBlockParam(mc, name, lastBlockParam)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
final override MethodCall getACall() { result = mc }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self].Element[any]" and
|
input = "Argument[self].Element[any]" and
|
||||||
output = "Argument[block].Parameter[0]" and
|
output = "Argument[block].Parameter[" + lastBlockParam + "]" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class RejectSummary extends SimpleSummarizedCallable {
|
private class RejectSummary extends SummarizedCallable {
|
||||||
RejectSummary() { this = "reject" }
|
MethodCall mc;
|
||||||
|
int lastBlockParam;
|
||||||
|
|
||||||
|
RejectSummary() { this = lastBlockParam(mc, "reject", lastBlockParam) }
|
||||||
|
|
||||||
|
final override MethodCall getACall() { result = mc }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self].Element[any]" and
|
(
|
||||||
output = ["Argument[block].Parameter[0]", "ReturnValue.Element[?]"] and
|
// array indices may get shifted
|
||||||
|
input = "Argument[self].Element[0..]" and
|
||||||
|
output = "ReturnValue.Element[?]"
|
||||||
|
or
|
||||||
|
input = "Argument[self].WithoutElement[0..].WithElement[any]" and
|
||||||
|
output = "ReturnValue"
|
||||||
|
or
|
||||||
|
input = "Argument[self].Element[any]" and
|
||||||
|
output = "Argument[block].Parameter[" + lastBlockParam + "]"
|
||||||
|
) and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SelectSummary extends SimpleSummarizedCallable {
|
private class SelectSummary extends SummarizedCallable {
|
||||||
// `find_all` and `filter` are aliases of `select`.
|
MethodCall mc;
|
||||||
SelectSummary() { this = ["select", "find_all", "filter"] }
|
int lastBlockParam;
|
||||||
|
|
||||||
|
SelectSummary() {
|
||||||
|
exists(string name |
|
||||||
|
name = ["select", "find_all", "filter"] and
|
||||||
|
this = lastBlockParam(mc, name, lastBlockParam)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
final override MethodCall getACall() { result = mc }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self].Element[any]" and
|
(
|
||||||
output = ["Argument[block].Parameter[0]", "ReturnValue.Element[?]"] and
|
// array indices may get shifted
|
||||||
|
input = "Argument[self].Element[0..]" and
|
||||||
|
output = "ReturnValue.Element[?]"
|
||||||
|
or
|
||||||
|
input = "Argument[self].WithoutElement[0..].WithElement[any]" and
|
||||||
|
output = "ReturnValue"
|
||||||
|
or
|
||||||
|
input = "Argument[self].Element[any]" and
|
||||||
|
output = "Argument[block].Parameter[" + lastBlockParam + "]"
|
||||||
|
) and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2406,7 +2576,7 @@ module Enumerable {
|
|||||||
ToASummary() { this = ["to_a", "entries", "to_ary"] }
|
ToASummary() { this = ["to_a", "entries", "to_ary"] }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self]" and
|
input = "Argument[self].WithElement[?,0..]" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
}
|
}
|
||||||
|
|||||||
533
ruby/ql/lib/codeql/ruby/frameworks/core/Hash.qll
Normal file
533
ruby/ql/lib/codeql/ruby/frameworks/core/Hash.qll
Normal file
@@ -0,0 +1,533 @@
|
|||||||
|
/** Provides flow summaries for the `Hash` class. */
|
||||||
|
|
||||||
|
private import codeql.ruby.AST
|
||||||
|
private import codeql.ruby.ApiGraphs
|
||||||
|
private import codeql.ruby.DataFlow
|
||||||
|
private import codeql.ruby.dataflow.FlowSummary
|
||||||
|
private import codeql.ruby.dataflow.internal.DataFlowDispatch
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides flow summaries for the `Hash` class.
|
||||||
|
*
|
||||||
|
* The summaries are ordered (and implemented) based on
|
||||||
|
* https://docs.ruby-lang.org/en/3.1/Hash.html.
|
||||||
|
*
|
||||||
|
* Some summaries are shared with the `Array` class, and those are defined
|
||||||
|
* in `Array.qll`.
|
||||||
|
*/
|
||||||
|
module Hash {
|
||||||
|
// cannot use API graphs due to negative recursion
|
||||||
|
private predicate isHashLiteralPair(Pair pair, ConstantValue cv) {
|
||||||
|
cv = DataFlow::Content::getKnownElementIndex(pair.getKey()) and
|
||||||
|
pair = any(MethodCall mc | mc.getMethodName() = "[]").getAnArgument()
|
||||||
|
}
|
||||||
|
|
||||||
|
private class HashLiteralSymbolSummary extends SummarizedCallable {
|
||||||
|
private ConstantValue::ConstantSymbolValue symbol;
|
||||||
|
|
||||||
|
HashLiteralSymbolSummary() {
|
||||||
|
isHashLiteralPair(_, symbol) and
|
||||||
|
this = "Hash.[:" + symbol.serialize() + "]"
|
||||||
|
}
|
||||||
|
|
||||||
|
final override MethodCall getACall() {
|
||||||
|
result = API::getTopLevelMember("Hash").getAMethodCall("[]").getExprNode().getExpr() and
|
||||||
|
exists(result.getKeywordArgument(symbol.getSymbol()))
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
|
// { symbol: x }
|
||||||
|
input = "Argument[" + symbol.getSymbol() + ":]" and
|
||||||
|
output = "ReturnValue.Element[" + symbol.serialize() + "]" and
|
||||||
|
preservesValue = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class HashLiteralNonSymbolSummary extends SummarizedCallable {
|
||||||
|
private ConstantValue cv;
|
||||||
|
|
||||||
|
HashLiteralNonSymbolSummary() {
|
||||||
|
this = "Hash.[]" and
|
||||||
|
isHashLiteralPair(_, cv) and
|
||||||
|
not cv.isSymbol(_)
|
||||||
|
}
|
||||||
|
|
||||||
|
final override MethodCall getACall() {
|
||||||
|
result = API::getTopLevelMember("Hash").getAMethodCall("[]").getExprNode().getExpr() and
|
||||||
|
isHashLiteralPair(result.getAnArgument(), cv)
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
|
// { 'nonsymbol' => x }
|
||||||
|
input = "Argument[0..].PairValue[" + cv.serialize() + "]" and
|
||||||
|
output = "ReturnValue.Element[" + cv.serialize() + "]" and
|
||||||
|
preservesValue = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class HashNewSummary extends SummarizedCallable {
|
||||||
|
HashNewSummary() { this = "Hash[]" }
|
||||||
|
|
||||||
|
final override ElementReference getACall() {
|
||||||
|
result.getReceiver() = API::getTopLevelMember("Hash").getAUse().asExpr().getExpr() and
|
||||||
|
result.getNumberOfArguments() = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
|
(
|
||||||
|
// Hash[{symbol: x}]
|
||||||
|
input = "Argument[0].WithElement[any]" and
|
||||||
|
output = "ReturnValue"
|
||||||
|
or
|
||||||
|
// Hash[[:symbol, x]]
|
||||||
|
input = "Argument[0].Element[any].Element[1]" and
|
||||||
|
output = "ReturnValue.Element[?]"
|
||||||
|
) and
|
||||||
|
preservesValue = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class HashNewSummary2 extends SummarizedCallable {
|
||||||
|
private int i;
|
||||||
|
private ConstantValue cv;
|
||||||
|
|
||||||
|
HashNewSummary2() {
|
||||||
|
this = "Hash[" + i + ", " + cv.serialize() + "]" and
|
||||||
|
i % 2 = 1 and
|
||||||
|
exists(ElementReference er |
|
||||||
|
cv = er.getArgument(i - 1).getConstantValue() and
|
||||||
|
exists(er.getArgument(i))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
final override ElementReference getACall() {
|
||||||
|
result.getReceiver() = API::getTopLevelMember("Hash").getAUse().asExpr().getExpr() and
|
||||||
|
cv = result.getArgument(i - 1).getConstantValue() and
|
||||||
|
exists(result.getArgument(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
|
// Hash[:symbol, x]
|
||||||
|
input = "Argument[" + i + "]" and
|
||||||
|
output = "ReturnValue.Element[" + cv.serialize() + "]" and
|
||||||
|
preservesValue = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TryConvertSummary extends SummarizedCallable {
|
||||||
|
TryConvertSummary() { this = "Hash.try_convert" }
|
||||||
|
|
||||||
|
override MethodCall getACall() {
|
||||||
|
result = API::getTopLevelMember("Hash").getAMethodCall("try_convert").getExprNode().getExpr()
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
|
input = "Argument[0].WithElement[any]" and
|
||||||
|
output = "ReturnValue" and
|
||||||
|
preservesValue = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract private class StoreSummary extends SummarizedCallable {
|
||||||
|
MethodCall mc;
|
||||||
|
|
||||||
|
bindingset[this]
|
||||||
|
StoreSummary() { mc.getMethodName() = "store" and mc.getNumberOfArguments() = 2 }
|
||||||
|
|
||||||
|
final override MethodCall getACall() { result = mc }
|
||||||
|
|
||||||
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
|
input = "Argument[1]" and
|
||||||
|
output = "ReturnValue" and
|
||||||
|
preservesValue = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class StoreKnownSummary extends StoreSummary {
|
||||||
|
private ConstantValue cv;
|
||||||
|
|
||||||
|
StoreKnownSummary() {
|
||||||
|
cv = DataFlow::Content::getKnownElementIndex(mc.getArgument(0)) and
|
||||||
|
this = "store(" + cv.serialize() + ")"
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
|
super.propagatesFlowExt(input, output, preservesValue)
|
||||||
|
or
|
||||||
|
input = "Argument[1]" and
|
||||||
|
output = "Argument[self].Element[" + cv.serialize() + "]" and
|
||||||
|
preservesValue = true
|
||||||
|
or
|
||||||
|
input = "Argument[self].WithoutElement[" + cv.serialize() + "]" and
|
||||||
|
output = "Argument[self]" and
|
||||||
|
preservesValue = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class StoreUnknownSummary extends StoreSummary {
|
||||||
|
StoreUnknownSummary() {
|
||||||
|
not exists(DataFlow::Content::getKnownElementIndex(mc.getArgument(0))) and
|
||||||
|
this = "store"
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
|
super.propagatesFlowExt(input, output, preservesValue)
|
||||||
|
or
|
||||||
|
input = "Argument[1]" and
|
||||||
|
output = "Argument[self].Element[?]" and
|
||||||
|
preservesValue = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract private class AssocSummary extends SummarizedCallable {
|
||||||
|
MethodCall mc;
|
||||||
|
|
||||||
|
bindingset[this]
|
||||||
|
AssocSummary() { mc.getMethodName() = "assoc" }
|
||||||
|
|
||||||
|
override MethodCall getACall() { result = mc }
|
||||||
|
}
|
||||||
|
|
||||||
|
private class AssocKnownSummary extends AssocSummary {
|
||||||
|
private ConstantValue cv;
|
||||||
|
|
||||||
|
AssocKnownSummary() {
|
||||||
|
this = "assoc(" + cv.serialize() + "]" and
|
||||||
|
not cv.isInt(_) and // exclude arrays
|
||||||
|
mc.getNumberOfArguments() = 1 and
|
||||||
|
cv = DataFlow::Content::getKnownElementIndex(mc.getArgument(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
|
input = "Argument[self].Element[" + cv.serialize() + ",?]" and
|
||||||
|
output = "ReturnValue.Element[1]" and
|
||||||
|
preservesValue = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class AssocUnknownSummary extends AssocSummary {
|
||||||
|
AssocUnknownSummary() {
|
||||||
|
this = "assoc" and
|
||||||
|
mc.getNumberOfArguments() = 1 and
|
||||||
|
not exists(DataFlow::Content::getKnownElementIndex(mc.getArgument(0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
|
input = "Argument[self].Element[any].WithoutElement[any]" and
|
||||||
|
output = "ReturnValue.Element[1]" and
|
||||||
|
preservesValue = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class EachPairSummary extends SimpleSummarizedCallable {
|
||||||
|
EachPairSummary() { this = "each_pair" }
|
||||||
|
|
||||||
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
|
(
|
||||||
|
input = "Argument[self].Element[any]" and
|
||||||
|
output = "Argument[block].Parameter[1]"
|
||||||
|
or
|
||||||
|
input = "Argument[self].WithElement[any]" and
|
||||||
|
output = "ReturnValue"
|
||||||
|
) and
|
||||||
|
preservesValue = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class EachValueSummary extends SimpleSummarizedCallable {
|
||||||
|
EachValueSummary() { this = "each_value" }
|
||||||
|
|
||||||
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
|
(
|
||||||
|
input = "Argument[self].Element[any]" and
|
||||||
|
output = "Argument[block].Parameter[0]"
|
||||||
|
or
|
||||||
|
input = "Argument[self].WithElement[any]" and
|
||||||
|
output = "ReturnValue"
|
||||||
|
) and
|
||||||
|
preservesValue = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string getExceptComponent(MethodCall mc, int i) {
|
||||||
|
mc.getMethodName() = "except" and
|
||||||
|
result = DataFlow::Content::getKnownElementIndex(mc.getArgument(i)).serialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ExceptSummary extends SummarizedCallable {
|
||||||
|
MethodCall mc;
|
||||||
|
|
||||||
|
ExceptSummary() {
|
||||||
|
mc.getMethodName() = "except" and
|
||||||
|
this =
|
||||||
|
"except(" + concat(int i, string s | s = getExceptComponent(mc, i) | s, "," order by i) +
|
||||||
|
")"
|
||||||
|
}
|
||||||
|
|
||||||
|
final override MethodCall getACall() { result = mc }
|
||||||
|
|
||||||
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
|
input =
|
||||||
|
"Argument[self]" +
|
||||||
|
concat(int i, string s |
|
||||||
|
s = getExceptComponent(mc, i)
|
||||||
|
|
|
||||||
|
".WithoutElement[" + s + "]" order by i
|
||||||
|
) and
|
||||||
|
output = "ReturnValue" and
|
||||||
|
preservesValue = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract private class FetchValuesSummary extends SummarizedCallable {
|
||||||
|
MethodCall mc;
|
||||||
|
|
||||||
|
bindingset[this]
|
||||||
|
FetchValuesSummary() { mc.getMethodName() = "fetch_values" }
|
||||||
|
|
||||||
|
final override MethodCall getACall() { result = mc }
|
||||||
|
|
||||||
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
|
(
|
||||||
|
input = "Argument[self].WithElement[?]" and
|
||||||
|
output = "ReturnValue"
|
||||||
|
or
|
||||||
|
input = "Argument[0]" and
|
||||||
|
output = "Argument[block].Parameter[0]"
|
||||||
|
or
|
||||||
|
input = "Argument[block].ReturnValue" and
|
||||||
|
output = "ReturnValue.Element[?]"
|
||||||
|
) and
|
||||||
|
preservesValue = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class FetchValuesKnownSummary extends FetchValuesSummary {
|
||||||
|
ConstantValue cv;
|
||||||
|
|
||||||
|
FetchValuesKnownSummary() {
|
||||||
|
forex(Expr arg | arg = mc.getAnArgument() | exists(arg.getConstantValue())) and
|
||||||
|
cv = mc.getAnArgument().getConstantValue() and
|
||||||
|
this = "fetch_values(" + cv.serialize() + ")"
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
|
super.propagatesFlowExt(input, output, preservesValue)
|
||||||
|
or
|
||||||
|
input = "Argument[self].Element[" + cv.serialize() + "]" and
|
||||||
|
output = "ReturnValue.Element[?]" and
|
||||||
|
preservesValue = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class FetchValuesUnknownSummary extends FetchValuesSummary {
|
||||||
|
FetchValuesUnknownSummary() {
|
||||||
|
exists(Expr arg | arg = mc.getAnArgument() | not exists(arg.getConstantValue())) and
|
||||||
|
this = "fetch_values(?)"
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
|
super.propagatesFlowExt(input, output, preservesValue)
|
||||||
|
or
|
||||||
|
input = "Argument[self].Element[any]" and
|
||||||
|
output = "ReturnValue.Element[?]" and
|
||||||
|
preservesValue = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MergeSummary extends SimpleSummarizedCallable {
|
||||||
|
MergeSummary() { this = "merge" }
|
||||||
|
|
||||||
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
|
(
|
||||||
|
input = "Argument[any].WithElement[any]" and
|
||||||
|
output = "ReturnValue"
|
||||||
|
or
|
||||||
|
input = "Argument[any].Element[any]" and
|
||||||
|
output = "Argument[block].Parameter[1,2]"
|
||||||
|
) and
|
||||||
|
preservesValue = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MergeBangSummary extends SimpleSummarizedCallable {
|
||||||
|
MergeBangSummary() { this = ["merge!", "update"] }
|
||||||
|
|
||||||
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
|
(
|
||||||
|
input = "Argument[any].WithElement[any]" and
|
||||||
|
output = ["ReturnValue", "Argument[self]"]
|
||||||
|
or
|
||||||
|
input = "Argument[any].Element[any]" and
|
||||||
|
output = "Argument[block].Parameter[1,2]"
|
||||||
|
) and
|
||||||
|
preservesValue = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RassocSummary extends SimpleSummarizedCallable {
|
||||||
|
RassocSummary() { this = "rassoc" }
|
||||||
|
|
||||||
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
|
input = "Argument[self].Element[any].WithoutElement[any]" and
|
||||||
|
output = "ReturnValue.Element[1]" and
|
||||||
|
preservesValue = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract private class SliceSummary extends SummarizedCallable {
|
||||||
|
MethodCall mc;
|
||||||
|
|
||||||
|
bindingset[this]
|
||||||
|
SliceSummary() { mc.getMethodName() = "slice" }
|
||||||
|
|
||||||
|
final override MethodCall getACall() { result = mc }
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SliceKnownSummary extends SliceSummary {
|
||||||
|
ConstantValue cv;
|
||||||
|
|
||||||
|
SliceKnownSummary() {
|
||||||
|
cv = mc.getAnArgument().getConstantValue() and
|
||||||
|
this = "slice(" + cv.serialize() + ")" and
|
||||||
|
not cv.isInt(_) // covered in `Array.qll`
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
|
input = "Argument[self].WithElement[?," + cv.serialize() + "]" and
|
||||||
|
output = "ReturnValue" and
|
||||||
|
preservesValue = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SliceUnknownSummary extends SliceSummary {
|
||||||
|
SliceUnknownSummary() {
|
||||||
|
exists(Expr arg | arg = mc.getAnArgument() | not exists(arg.getConstantValue())) and
|
||||||
|
this = "slice(?)"
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
|
input = "Argument[self].WithoutElement[0..].WithElement[any]" and
|
||||||
|
output = "ReturnValue" and
|
||||||
|
preservesValue = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ToASummary extends SimpleSummarizedCallable {
|
||||||
|
ToASummary() { this = "to_a" }
|
||||||
|
|
||||||
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
|
input = "Argument[self].WithoutElement[0..].Element[any]" and
|
||||||
|
output = "ReturnValue.Element[?].Element[1]" and
|
||||||
|
preservesValue = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ToHWithoutBlockSummary extends SimpleSummarizedCallable {
|
||||||
|
ToHWithoutBlockSummary() { this = ["to_h", "to_hash"] and not exists(mc.getBlock()) }
|
||||||
|
|
||||||
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
|
input = "Argument[self].WithElement[any]" and
|
||||||
|
output = "ReturnValue" and
|
||||||
|
preservesValue = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ToHWithBlockSummary extends SimpleSummarizedCallable {
|
||||||
|
ToHWithBlockSummary() { this = "to_h" and exists(mc.getBlock()) }
|
||||||
|
|
||||||
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
|
(
|
||||||
|
input = "Argument[self].Element[any]" and
|
||||||
|
output = "Argument[block].Parameter[1]"
|
||||||
|
or
|
||||||
|
input = "Argument[block].ReturnValue.Element[1]" and
|
||||||
|
output = "ReturnValue.Element[?]"
|
||||||
|
) and
|
||||||
|
preservesValue = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TransformKeysSummary extends SimpleSummarizedCallable {
|
||||||
|
TransformKeysSummary() { this = "transform_keys" }
|
||||||
|
|
||||||
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
|
input = "Argument[self].Element[any]" and
|
||||||
|
output = "ReturnValue.Element[?]" and
|
||||||
|
preservesValue = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TransformKeysBangSummary extends SimpleSummarizedCallable {
|
||||||
|
TransformKeysBangSummary() { this = "transform_keys!" }
|
||||||
|
|
||||||
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
|
(
|
||||||
|
input = "Argument[self].Element[any]" and
|
||||||
|
output = "Argument[self].Element[?]"
|
||||||
|
or
|
||||||
|
input = "Argument[self].WithoutElement[any]" and
|
||||||
|
output = "Argument[self]"
|
||||||
|
) and
|
||||||
|
preservesValue = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TransformValuesSummary extends SimpleSummarizedCallable {
|
||||||
|
TransformValuesSummary() { this = "transform_values" }
|
||||||
|
|
||||||
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
|
(
|
||||||
|
input = "Argument[self].Element[any]" and
|
||||||
|
output = "Argument[block].Parameter[0]"
|
||||||
|
or
|
||||||
|
input = "Argument[block].ReturnValue" and
|
||||||
|
output = "ReturnValue.Element[?]"
|
||||||
|
) and
|
||||||
|
preservesValue = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TransformValuesBangSummary extends SimpleSummarizedCallable {
|
||||||
|
TransformValuesBangSummary() { this = "transform_values!" }
|
||||||
|
|
||||||
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
|
(
|
||||||
|
input = "Argument[self].Element[any]" and
|
||||||
|
output = "Argument[block].Parameter[0]"
|
||||||
|
or
|
||||||
|
input = "Argument[block].ReturnValue" and
|
||||||
|
output = "Argument[self].Element[?]"
|
||||||
|
or
|
||||||
|
input = "Argument[self].WithoutElement[any]" and
|
||||||
|
output = "Argument[self]"
|
||||||
|
) and
|
||||||
|
preservesValue = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ValuesSummary extends SimpleSummarizedCallable {
|
||||||
|
ValuesSummary() { this = "values" }
|
||||||
|
|
||||||
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
|
input = "Argument[self].Element[any]" and
|
||||||
|
output = "ReturnValue.Element[?]" and
|
||||||
|
preservesValue = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract private class ValuesAtSummary extends SummarizedCallable {
|
||||||
|
MethodCall mc;
|
||||||
|
|
||||||
|
bindingset[this]
|
||||||
|
ValuesAtSummary() { mc.getMethodName() = "values_at" }
|
||||||
|
|
||||||
|
final override MethodCall getACall() { result = mc }
|
||||||
|
|
||||||
|
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
|
input = "Argument[self].WithElement[?]" and
|
||||||
|
output = "ReturnValue" and
|
||||||
|
preservesValue = true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -332,16 +332,16 @@ edges
|
|||||||
| array_flow.rb:178:16:178:16 | c [element 1] : | array_flow.rb:179:11:179:11 | d [element 2, element 1] : |
|
| array_flow.rb:178:16:178:16 | c [element 1] : | array_flow.rb:179:11:179:11 | d [element 2, element 1] : |
|
||||||
| array_flow.rb:178:16:178:16 | c [element 1] : | array_flow.rb:180:11:180:11 | d [element 2, element 1] : |
|
| array_flow.rb:178:16:178:16 | c [element 1] : | array_flow.rb:180:11:180:11 | d [element 2, element 1] : |
|
||||||
| array_flow.rb:178:16:178:16 | c [element 1] : | array_flow.rb:180:11:180:11 | d [element 2, element 1] : |
|
| array_flow.rb:178:16:178:16 | c [element 1] : | array_flow.rb:180:11:180:11 | d [element 2, element 1] : |
|
||||||
| array_flow.rb:179:11:179:11 | d [element 2, element 1] : | array_flow.rb:179:11:179:22 | call to assoc [element] : |
|
| array_flow.rb:179:11:179:11 | d [element 2, element 1] : | array_flow.rb:179:11:179:22 | call to assoc [element 1] : |
|
||||||
| array_flow.rb:179:11:179:11 | d [element 2, element 1] : | array_flow.rb:179:11:179:22 | call to assoc [element] : |
|
| array_flow.rb:179:11:179:11 | d [element 2, element 1] : | array_flow.rb:179:11:179:22 | call to assoc [element 1] : |
|
||||||
| array_flow.rb:179:11:179:22 | call to assoc [element] : | array_flow.rb:179:11:179:25 | ...[...] : |
|
| array_flow.rb:179:11:179:22 | call to assoc [element 1] : | array_flow.rb:179:11:179:25 | ...[...] : |
|
||||||
| array_flow.rb:179:11:179:22 | call to assoc [element] : | array_flow.rb:179:11:179:25 | ...[...] : |
|
| array_flow.rb:179:11:179:22 | call to assoc [element 1] : | array_flow.rb:179:11:179:25 | ...[...] : |
|
||||||
| array_flow.rb:179:11:179:25 | ...[...] : | array_flow.rb:179:10:179:26 | ( ... ) |
|
| array_flow.rb:179:11:179:25 | ...[...] : | array_flow.rb:179:10:179:26 | ( ... ) |
|
||||||
| array_flow.rb:179:11:179:25 | ...[...] : | array_flow.rb:179:10:179:26 | ( ... ) |
|
| array_flow.rb:179:11:179:25 | ...[...] : | array_flow.rb:179:10:179:26 | ( ... ) |
|
||||||
| array_flow.rb:180:11:180:11 | d [element 2, element 1] : | array_flow.rb:180:11:180:22 | call to assoc [element] : |
|
| array_flow.rb:180:11:180:11 | d [element 2, element 1] : | array_flow.rb:180:11:180:22 | call to assoc [element 1] : |
|
||||||
| array_flow.rb:180:11:180:11 | d [element 2, element 1] : | array_flow.rb:180:11:180:22 | call to assoc [element] : |
|
| array_flow.rb:180:11:180:11 | d [element 2, element 1] : | array_flow.rb:180:11:180:22 | call to assoc [element 1] : |
|
||||||
| array_flow.rb:180:11:180:22 | call to assoc [element] : | array_flow.rb:180:11:180:25 | ...[...] : |
|
| array_flow.rb:180:11:180:22 | call to assoc [element 1] : | array_flow.rb:180:11:180:25 | ...[...] : |
|
||||||
| array_flow.rb:180:11:180:22 | call to assoc [element] : | array_flow.rb:180:11:180:25 | ...[...] : |
|
| array_flow.rb:180:11:180:22 | call to assoc [element 1] : | array_flow.rb:180:11:180:25 | ...[...] : |
|
||||||
| array_flow.rb:180:11:180:25 | ...[...] : | array_flow.rb:180:10:180:26 | ( ... ) |
|
| array_flow.rb:180:11:180:25 | ...[...] : | array_flow.rb:180:10:180:26 | ( ... ) |
|
||||||
| array_flow.rb:180:11:180:25 | ...[...] : | array_flow.rb:180:10:180:26 | ( ... ) |
|
| array_flow.rb:180:11:180:25 | ...[...] : | array_flow.rb:180:10:180:26 | ( ... ) |
|
||||||
| array_flow.rb:184:13:184:22 | call to source : | array_flow.rb:186:10:186:10 | a [element 1] : |
|
| array_flow.rb:184:13:184:22 | call to source : | array_flow.rb:186:10:186:10 | a [element 1] : |
|
||||||
@@ -520,26 +520,12 @@ edges
|
|||||||
| array_flow.rb:312:10:312:10 | b [element 2] : | array_flow.rb:312:10:312:13 | ...[...] |
|
| array_flow.rb:312:10:312:10 | b [element 2] : | array_flow.rb:312:10:312:13 | ...[...] |
|
||||||
| array_flow.rb:316:16:316:27 | call to source : | array_flow.rb:317:9:317:9 | a [element 2] : |
|
| array_flow.rb:316:16:316:27 | call to source : | array_flow.rb:317:9:317:9 | a [element 2] : |
|
||||||
| array_flow.rb:316:16:316:27 | call to source : | array_flow.rb:317:9:317:9 | a [element 2] : |
|
| array_flow.rb:316:16:316:27 | call to source : | array_flow.rb:317:9:317:9 | a [element 2] : |
|
||||||
| array_flow.rb:317:9:317:9 | [post] a [element] : | array_flow.rb:319:10:319:10 | a [element] : |
|
|
||||||
| array_flow.rb:317:9:317:9 | [post] a [element] : | array_flow.rb:319:10:319:10 | a [element] : |
|
|
||||||
| array_flow.rb:317:9:317:9 | [post] a [element] : | array_flow.rb:320:10:320:10 | a [element] : |
|
|
||||||
| array_flow.rb:317:9:317:9 | [post] a [element] : | array_flow.rb:320:10:320:10 | a [element] : |
|
|
||||||
| array_flow.rb:317:9:317:9 | [post] a [element] : | array_flow.rb:321:10:321:10 | a [element] : |
|
|
||||||
| array_flow.rb:317:9:317:9 | [post] a [element] : | array_flow.rb:321:10:321:10 | a [element] : |
|
|
||||||
| array_flow.rb:317:9:317:9 | a [element 2] : | array_flow.rb:317:9:317:9 | [post] a [element] : |
|
|
||||||
| array_flow.rb:317:9:317:9 | a [element 2] : | array_flow.rb:317:9:317:9 | [post] a [element] : |
|
|
||||||
| array_flow.rb:317:9:317:9 | a [element 2] : | array_flow.rb:317:9:317:36 | call to delete : |
|
| array_flow.rb:317:9:317:9 | a [element 2] : | array_flow.rb:317:9:317:36 | call to delete : |
|
||||||
| array_flow.rb:317:9:317:9 | a [element 2] : | array_flow.rb:317:9:317:36 | call to delete : |
|
| array_flow.rb:317:9:317:9 | a [element 2] : | array_flow.rb:317:9:317:36 | call to delete : |
|
||||||
| array_flow.rb:317:9:317:36 | call to delete : | array_flow.rb:318:10:318:10 | b |
|
| array_flow.rb:317:9:317:36 | call to delete : | array_flow.rb:318:10:318:10 | b |
|
||||||
| array_flow.rb:317:9:317:36 | call to delete : | array_flow.rb:318:10:318:10 | b |
|
| array_flow.rb:317:9:317:36 | call to delete : | array_flow.rb:318:10:318:10 | b |
|
||||||
| array_flow.rb:317:23:317:34 | call to source : | array_flow.rb:317:9:317:36 | call to delete : |
|
| array_flow.rb:317:23:317:34 | call to source : | array_flow.rb:317:9:317:36 | call to delete : |
|
||||||
| array_flow.rb:317:23:317:34 | call to source : | array_flow.rb:317:9:317:36 | call to delete : |
|
| array_flow.rb:317:23:317:34 | call to source : | array_flow.rb:317:9:317:36 | call to delete : |
|
||||||
| array_flow.rb:319:10:319:10 | a [element] : | array_flow.rb:319:10:319:13 | ...[...] |
|
|
||||||
| array_flow.rb:319:10:319:10 | a [element] : | array_flow.rb:319:10:319:13 | ...[...] |
|
|
||||||
| array_flow.rb:320:10:320:10 | a [element] : | array_flow.rb:320:10:320:13 | ...[...] |
|
|
||||||
| array_flow.rb:320:10:320:10 | a [element] : | array_flow.rb:320:10:320:13 | ...[...] |
|
|
||||||
| array_flow.rb:321:10:321:10 | a [element] : | array_flow.rb:321:10:321:13 | ...[...] |
|
|
||||||
| array_flow.rb:321:10:321:10 | a [element] : | array_flow.rb:321:10:321:13 | ...[...] |
|
|
||||||
| array_flow.rb:325:16:325:27 | call to source : | array_flow.rb:326:9:326:9 | a [element 2] : |
|
| array_flow.rb:325:16:325:27 | call to source : | array_flow.rb:326:9:326:9 | a [element 2] : |
|
||||||
| array_flow.rb:325:16:325:27 | call to source : | array_flow.rb:326:9:326:9 | a [element 2] : |
|
| array_flow.rb:325:16:325:27 | call to source : | array_flow.rb:326:9:326:9 | a [element 2] : |
|
||||||
| array_flow.rb:325:30:325:41 | call to source : | array_flow.rb:326:9:326:9 | a [element 3] : |
|
| array_flow.rb:325:30:325:41 | call to source : | array_flow.rb:326:9:326:9 | a [element 3] : |
|
||||||
@@ -578,6 +564,8 @@ edges
|
|||||||
| array_flow.rb:334:10:334:10 | a [element] : | array_flow.rb:334:10:334:13 | ...[...] |
|
| array_flow.rb:334:10:334:10 | a [element] : | array_flow.rb:334:10:334:13 | ...[...] |
|
||||||
| array_flow.rb:338:16:338:25 | call to source : | array_flow.rb:339:9:339:9 | a [element 2] : |
|
| array_flow.rb:338:16:338:25 | call to source : | array_flow.rb:339:9:339:9 | a [element 2] : |
|
||||||
| array_flow.rb:338:16:338:25 | call to source : | array_flow.rb:339:9:339:9 | a [element 2] : |
|
| array_flow.rb:338:16:338:25 | call to source : | array_flow.rb:339:9:339:9 | a [element 2] : |
|
||||||
|
| array_flow.rb:338:16:338:25 | call to source : | array_flow.rb:345:10:345:10 | a [element 2] : |
|
||||||
|
| array_flow.rb:338:16:338:25 | call to source : | array_flow.rb:345:10:345:10 | a [element 2] : |
|
||||||
| array_flow.rb:339:9:339:9 | [post] a [element] : | array_flow.rb:343:10:343:10 | a [element] : |
|
| array_flow.rb:339:9:339:9 | [post] a [element] : | array_flow.rb:343:10:343:10 | a [element] : |
|
||||||
| array_flow.rb:339:9:339:9 | [post] a [element] : | array_flow.rb:343:10:343:10 | a [element] : |
|
| array_flow.rb:339:9:339:9 | [post] a [element] : | array_flow.rb:343:10:343:10 | a [element] : |
|
||||||
| array_flow.rb:339:9:339:9 | [post] a [element] : | array_flow.rb:344:10:344:10 | a [element] : |
|
| array_flow.rb:339:9:339:9 | [post] a [element] : | array_flow.rb:344:10:344:10 | a [element] : |
|
||||||
@@ -600,6 +588,8 @@ edges
|
|||||||
| array_flow.rb:343:10:343:10 | a [element] : | array_flow.rb:343:10:343:13 | ...[...] |
|
| array_flow.rb:343:10:343:10 | a [element] : | array_flow.rb:343:10:343:13 | ...[...] |
|
||||||
| array_flow.rb:344:10:344:10 | a [element] : | array_flow.rb:344:10:344:13 | ...[...] |
|
| array_flow.rb:344:10:344:10 | a [element] : | array_flow.rb:344:10:344:13 | ...[...] |
|
||||||
| array_flow.rb:344:10:344:10 | a [element] : | array_flow.rb:344:10:344:13 | ...[...] |
|
| array_flow.rb:344:10:344:10 | a [element] : | array_flow.rb:344:10:344:13 | ...[...] |
|
||||||
|
| array_flow.rb:345:10:345:10 | a [element 2] : | array_flow.rb:345:10:345:13 | ...[...] |
|
||||||
|
| array_flow.rb:345:10:345:10 | a [element 2] : | array_flow.rb:345:10:345:13 | ...[...] |
|
||||||
| array_flow.rb:345:10:345:10 | a [element] : | array_flow.rb:345:10:345:13 | ...[...] |
|
| array_flow.rb:345:10:345:10 | a [element] : | array_flow.rb:345:10:345:13 | ...[...] |
|
||||||
| array_flow.rb:345:10:345:10 | a [element] : | array_flow.rb:345:10:345:13 | ...[...] |
|
| array_flow.rb:345:10:345:10 | a [element] : | array_flow.rb:345:10:345:13 | ...[...] |
|
||||||
| array_flow.rb:349:16:349:25 | call to source : | array_flow.rb:350:9:350:9 | a [element 2] : |
|
| array_flow.rb:349:16:349:25 | call to source : | array_flow.rb:350:9:350:9 | a [element 2] : |
|
||||||
@@ -1703,14 +1693,14 @@ edges
|
|||||||
| array_flow.rb:945:16:945:16 | c [element 0] : | array_flow.rb:946:10:946:10 | d [element 2, element 0] : |
|
| array_flow.rb:945:16:945:16 | c [element 0] : | array_flow.rb:946:10:946:10 | d [element 2, element 0] : |
|
||||||
| array_flow.rb:945:16:945:16 | c [element 0] : | array_flow.rb:947:10:947:10 | d [element 2, element 0] : |
|
| array_flow.rb:945:16:945:16 | c [element 0] : | array_flow.rb:947:10:947:10 | d [element 2, element 0] : |
|
||||||
| array_flow.rb:945:16:945:16 | c [element 0] : | array_flow.rb:947:10:947:10 | d [element 2, element 0] : |
|
| array_flow.rb:945:16:945:16 | c [element 0] : | array_flow.rb:947:10:947:10 | d [element 2, element 0] : |
|
||||||
| array_flow.rb:946:10:946:10 | d [element 2, element 0] : | array_flow.rb:946:10:946:22 | call to rassoc [element] : |
|
| array_flow.rb:946:10:946:10 | d [element 2, element 0] : | array_flow.rb:946:10:946:22 | call to rassoc [element 0] : |
|
||||||
| array_flow.rb:946:10:946:10 | d [element 2, element 0] : | array_flow.rb:946:10:946:22 | call to rassoc [element] : |
|
| array_flow.rb:946:10:946:10 | d [element 2, element 0] : | array_flow.rb:946:10:946:22 | call to rassoc [element 0] : |
|
||||||
| array_flow.rb:946:10:946:22 | call to rassoc [element] : | array_flow.rb:946:10:946:25 | ...[...] |
|
| array_flow.rb:946:10:946:22 | call to rassoc [element 0] : | array_flow.rb:946:10:946:25 | ...[...] |
|
||||||
| array_flow.rb:946:10:946:22 | call to rassoc [element] : | array_flow.rb:946:10:946:25 | ...[...] |
|
| array_flow.rb:946:10:946:22 | call to rassoc [element 0] : | array_flow.rb:946:10:946:25 | ...[...] |
|
||||||
| array_flow.rb:947:10:947:10 | d [element 2, element 0] : | array_flow.rb:947:10:947:22 | call to rassoc [element] : |
|
| array_flow.rb:947:10:947:10 | d [element 2, element 0] : | array_flow.rb:947:10:947:22 | call to rassoc [element 0] : |
|
||||||
| array_flow.rb:947:10:947:10 | d [element 2, element 0] : | array_flow.rb:947:10:947:22 | call to rassoc [element] : |
|
| array_flow.rb:947:10:947:10 | d [element 2, element 0] : | array_flow.rb:947:10:947:22 | call to rassoc [element 0] : |
|
||||||
| array_flow.rb:947:10:947:22 | call to rassoc [element] : | array_flow.rb:947:10:947:25 | ...[...] |
|
| array_flow.rb:947:10:947:22 | call to rassoc [element 0] : | array_flow.rb:947:10:947:25 | ...[...] |
|
||||||
| array_flow.rb:947:10:947:22 | call to rassoc [element] : | array_flow.rb:947:10:947:25 | ...[...] |
|
| array_flow.rb:947:10:947:22 | call to rassoc [element 0] : | array_flow.rb:947:10:947:25 | ...[...] |
|
||||||
| array_flow.rb:951:10:951:21 | call to source : | array_flow.rb:952:9:952:9 | a [element 0] : |
|
| array_flow.rb:951:10:951:21 | call to source : | array_flow.rb:952:9:952:9 | a [element 0] : |
|
||||||
| array_flow.rb:951:10:951:21 | call to source : | array_flow.rb:952:9:952:9 | a [element 0] : |
|
| array_flow.rb:951:10:951:21 | call to source : | array_flow.rb:952:9:952:9 | a [element 0] : |
|
||||||
| array_flow.rb:951:10:951:21 | call to source : | array_flow.rb:957:9:957:9 | a [element 0] : |
|
| array_flow.rb:951:10:951:21 | call to source : | array_flow.rb:957:9:957:9 | a [element 0] : |
|
||||||
@@ -3277,8 +3267,12 @@ edges
|
|||||||
| array_flow.rb:1574:10:1574:10 | b [element] : | array_flow.rb:1574:10:1574:13 | ...[...] |
|
| array_flow.rb:1574:10:1574:10 | b [element] : | array_flow.rb:1574:10:1574:13 | ...[...] |
|
||||||
| array_flow.rb:1576:9:1576:9 | a [element 1] : | array_flow.rb:1576:9:1576:28 | call to values_at [element] : |
|
| array_flow.rb:1576:9:1576:9 | a [element 1] : | array_flow.rb:1576:9:1576:28 | call to values_at [element] : |
|
||||||
| array_flow.rb:1576:9:1576:9 | a [element 1] : | array_flow.rb:1576:9:1576:28 | call to values_at [element] : |
|
| array_flow.rb:1576:9:1576:9 | a [element 1] : | array_flow.rb:1576:9:1576:28 | call to values_at [element] : |
|
||||||
|
| array_flow.rb:1576:9:1576:9 | a [element 3] : | array_flow.rb:1576:9:1576:28 | call to values_at [element 1] : |
|
||||||
|
| array_flow.rb:1576:9:1576:9 | a [element 3] : | array_flow.rb:1576:9:1576:28 | call to values_at [element 1] : |
|
||||||
| array_flow.rb:1576:9:1576:9 | a [element 3] : | array_flow.rb:1576:9:1576:28 | call to values_at [element] : |
|
| array_flow.rb:1576:9:1576:9 | a [element 3] : | array_flow.rb:1576:9:1576:28 | call to values_at [element] : |
|
||||||
| array_flow.rb:1576:9:1576:9 | a [element 3] : | array_flow.rb:1576:9:1576:28 | call to values_at [element] : |
|
| array_flow.rb:1576:9:1576:9 | a [element 3] : | array_flow.rb:1576:9:1576:28 | call to values_at [element] : |
|
||||||
|
| array_flow.rb:1576:9:1576:28 | call to values_at [element 1] : | array_flow.rb:1578:10:1578:10 | b [element 1] : |
|
||||||
|
| array_flow.rb:1576:9:1576:28 | call to values_at [element 1] : | array_flow.rb:1578:10:1578:10 | b [element 1] : |
|
||||||
| array_flow.rb:1576:9:1576:28 | call to values_at [element] : | array_flow.rb:1577:10:1577:10 | b [element] : |
|
| array_flow.rb:1576:9:1576:28 | call to values_at [element] : | array_flow.rb:1577:10:1577:10 | b [element] : |
|
||||||
| array_flow.rb:1576:9:1576:28 | call to values_at [element] : | array_flow.rb:1577:10:1577:10 | b [element] : |
|
| array_flow.rb:1576:9:1576:28 | call to values_at [element] : | array_flow.rb:1577:10:1577:10 | b [element] : |
|
||||||
| array_flow.rb:1576:9:1576:28 | call to values_at [element] : | array_flow.rb:1578:10:1578:10 | b [element] : |
|
| array_flow.rb:1576:9:1576:28 | call to values_at [element] : | array_flow.rb:1578:10:1578:10 | b [element] : |
|
||||||
@@ -3289,6 +3283,8 @@ edges
|
|||||||
| array_flow.rb:1576:9:1576:28 | call to values_at [element] : | array_flow.rb:1580:10:1580:10 | b [element] : |
|
| array_flow.rb:1576:9:1576:28 | call to values_at [element] : | array_flow.rb:1580:10:1580:10 | b [element] : |
|
||||||
| array_flow.rb:1577:10:1577:10 | b [element] : | array_flow.rb:1577:10:1577:13 | ...[...] |
|
| array_flow.rb:1577:10:1577:10 | b [element] : | array_flow.rb:1577:10:1577:13 | ...[...] |
|
||||||
| array_flow.rb:1577:10:1577:10 | b [element] : | array_flow.rb:1577:10:1577:13 | ...[...] |
|
| array_flow.rb:1577:10:1577:10 | b [element] : | array_flow.rb:1577:10:1577:13 | ...[...] |
|
||||||
|
| array_flow.rb:1578:10:1578:10 | b [element 1] : | array_flow.rb:1578:10:1578:13 | ...[...] |
|
||||||
|
| array_flow.rb:1578:10:1578:10 | b [element 1] : | array_flow.rb:1578:10:1578:13 | ...[...] |
|
||||||
| array_flow.rb:1578:10:1578:10 | b [element] : | array_flow.rb:1578:10:1578:13 | ...[...] |
|
| array_flow.rb:1578:10:1578:10 | b [element] : | array_flow.rb:1578:10:1578:13 | ...[...] |
|
||||||
| array_flow.rb:1578:10:1578:10 | b [element] : | array_flow.rb:1578:10:1578:13 | ...[...] |
|
| array_flow.rb:1578:10:1578:10 | b [element] : | array_flow.rb:1578:10:1578:13 | ...[...] |
|
||||||
| array_flow.rb:1579:10:1579:10 | b [element] : | array_flow.rb:1579:10:1579:13 | ...[...] |
|
| array_flow.rb:1579:10:1579:10 | b [element] : | array_flow.rb:1579:10:1579:13 | ...[...] |
|
||||||
@@ -3748,16 +3744,16 @@ nodes
|
|||||||
| array_flow.rb:179:10:179:26 | ( ... ) | semmle.label | ( ... ) |
|
| array_flow.rb:179:10:179:26 | ( ... ) | semmle.label | ( ... ) |
|
||||||
| array_flow.rb:179:11:179:11 | d [element 2, element 1] : | semmle.label | d [element 2, element 1] : |
|
| array_flow.rb:179:11:179:11 | d [element 2, element 1] : | semmle.label | d [element 2, element 1] : |
|
||||||
| array_flow.rb:179:11:179:11 | d [element 2, element 1] : | semmle.label | d [element 2, element 1] : |
|
| array_flow.rb:179:11:179:11 | d [element 2, element 1] : | semmle.label | d [element 2, element 1] : |
|
||||||
| array_flow.rb:179:11:179:22 | call to assoc [element] : | semmle.label | call to assoc [element] : |
|
| array_flow.rb:179:11:179:22 | call to assoc [element 1] : | semmle.label | call to assoc [element 1] : |
|
||||||
| array_flow.rb:179:11:179:22 | call to assoc [element] : | semmle.label | call to assoc [element] : |
|
| array_flow.rb:179:11:179:22 | call to assoc [element 1] : | semmle.label | call to assoc [element 1] : |
|
||||||
| array_flow.rb:179:11:179:25 | ...[...] : | semmle.label | ...[...] : |
|
| array_flow.rb:179:11:179:25 | ...[...] : | semmle.label | ...[...] : |
|
||||||
| array_flow.rb:179:11:179:25 | ...[...] : | semmle.label | ...[...] : |
|
| array_flow.rb:179:11:179:25 | ...[...] : | semmle.label | ...[...] : |
|
||||||
| array_flow.rb:180:10:180:26 | ( ... ) | semmle.label | ( ... ) |
|
| array_flow.rb:180:10:180:26 | ( ... ) | semmle.label | ( ... ) |
|
||||||
| array_flow.rb:180:10:180:26 | ( ... ) | semmle.label | ( ... ) |
|
| array_flow.rb:180:10:180:26 | ( ... ) | semmle.label | ( ... ) |
|
||||||
| array_flow.rb:180:11:180:11 | d [element 2, element 1] : | semmle.label | d [element 2, element 1] : |
|
| array_flow.rb:180:11:180:11 | d [element 2, element 1] : | semmle.label | d [element 2, element 1] : |
|
||||||
| array_flow.rb:180:11:180:11 | d [element 2, element 1] : | semmle.label | d [element 2, element 1] : |
|
| array_flow.rb:180:11:180:11 | d [element 2, element 1] : | semmle.label | d [element 2, element 1] : |
|
||||||
| array_flow.rb:180:11:180:22 | call to assoc [element] : | semmle.label | call to assoc [element] : |
|
| array_flow.rb:180:11:180:22 | call to assoc [element 1] : | semmle.label | call to assoc [element 1] : |
|
||||||
| array_flow.rb:180:11:180:22 | call to assoc [element] : | semmle.label | call to assoc [element] : |
|
| array_flow.rb:180:11:180:22 | call to assoc [element 1] : | semmle.label | call to assoc [element 1] : |
|
||||||
| array_flow.rb:180:11:180:25 | ...[...] : | semmle.label | ...[...] : |
|
| array_flow.rb:180:11:180:25 | ...[...] : | semmle.label | ...[...] : |
|
||||||
| array_flow.rb:180:11:180:25 | ...[...] : | semmle.label | ...[...] : |
|
| array_flow.rb:180:11:180:25 | ...[...] : | semmle.label | ...[...] : |
|
||||||
| array_flow.rb:184:13:184:22 | call to source : | semmle.label | call to source : |
|
| array_flow.rb:184:13:184:22 | call to source : | semmle.label | call to source : |
|
||||||
@@ -3970,8 +3966,6 @@ nodes
|
|||||||
| array_flow.rb:312:10:312:13 | ...[...] | semmle.label | ...[...] |
|
| array_flow.rb:312:10:312:13 | ...[...] | semmle.label | ...[...] |
|
||||||
| array_flow.rb:316:16:316:27 | call to source : | semmle.label | call to source : |
|
| array_flow.rb:316:16:316:27 | call to source : | semmle.label | call to source : |
|
||||||
| array_flow.rb:316:16:316:27 | call to source : | semmle.label | call to source : |
|
| array_flow.rb:316:16:316:27 | call to source : | semmle.label | call to source : |
|
||||||
| array_flow.rb:317:9:317:9 | [post] a [element] : | semmle.label | [post] a [element] : |
|
|
||||||
| array_flow.rb:317:9:317:9 | [post] a [element] : | semmle.label | [post] a [element] : |
|
|
||||||
| array_flow.rb:317:9:317:9 | a [element 2] : | semmle.label | a [element 2] : |
|
| array_flow.rb:317:9:317:9 | a [element 2] : | semmle.label | a [element 2] : |
|
||||||
| array_flow.rb:317:9:317:9 | a [element 2] : | semmle.label | a [element 2] : |
|
| array_flow.rb:317:9:317:9 | a [element 2] : | semmle.label | a [element 2] : |
|
||||||
| array_flow.rb:317:9:317:36 | call to delete : | semmle.label | call to delete : |
|
| array_flow.rb:317:9:317:36 | call to delete : | semmle.label | call to delete : |
|
||||||
@@ -3980,18 +3974,6 @@ nodes
|
|||||||
| array_flow.rb:317:23:317:34 | call to source : | semmle.label | call to source : |
|
| array_flow.rb:317:23:317:34 | call to source : | semmle.label | call to source : |
|
||||||
| array_flow.rb:318:10:318:10 | b | semmle.label | b |
|
| array_flow.rb:318:10:318:10 | b | semmle.label | b |
|
||||||
| array_flow.rb:318:10:318:10 | b | semmle.label | b |
|
| array_flow.rb:318:10:318:10 | b | semmle.label | b |
|
||||||
| array_flow.rb:319:10:319:10 | a [element] : | semmle.label | a [element] : |
|
|
||||||
| array_flow.rb:319:10:319:10 | a [element] : | semmle.label | a [element] : |
|
|
||||||
| array_flow.rb:319:10:319:13 | ...[...] | semmle.label | ...[...] |
|
|
||||||
| array_flow.rb:319:10:319:13 | ...[...] | semmle.label | ...[...] |
|
|
||||||
| array_flow.rb:320:10:320:10 | a [element] : | semmle.label | a [element] : |
|
|
||||||
| array_flow.rb:320:10:320:10 | a [element] : | semmle.label | a [element] : |
|
|
||||||
| array_flow.rb:320:10:320:13 | ...[...] | semmle.label | ...[...] |
|
|
||||||
| array_flow.rb:320:10:320:13 | ...[...] | semmle.label | ...[...] |
|
|
||||||
| array_flow.rb:321:10:321:10 | a [element] : | semmle.label | a [element] : |
|
|
||||||
| array_flow.rb:321:10:321:10 | a [element] : | semmle.label | a [element] : |
|
|
||||||
| array_flow.rb:321:10:321:13 | ...[...] | semmle.label | ...[...] |
|
|
||||||
| array_flow.rb:321:10:321:13 | ...[...] | semmle.label | ...[...] |
|
|
||||||
| array_flow.rb:325:16:325:27 | call to source : | semmle.label | call to source : |
|
| array_flow.rb:325:16:325:27 | call to source : | semmle.label | call to source : |
|
||||||
| array_flow.rb:325:16:325:27 | call to source : | semmle.label | call to source : |
|
| array_flow.rb:325:16:325:27 | call to source : | semmle.label | call to source : |
|
||||||
| array_flow.rb:325:30:325:41 | call to source : | semmle.label | call to source : |
|
| array_flow.rb:325:30:325:41 | call to source : | semmle.label | call to source : |
|
||||||
@@ -4056,6 +4038,8 @@ nodes
|
|||||||
| array_flow.rb:344:10:344:10 | a [element] : | semmle.label | a [element] : |
|
| array_flow.rb:344:10:344:10 | a [element] : | semmle.label | a [element] : |
|
||||||
| array_flow.rb:344:10:344:13 | ...[...] | semmle.label | ...[...] |
|
| array_flow.rb:344:10:344:13 | ...[...] | semmle.label | ...[...] |
|
||||||
| array_flow.rb:344:10:344:13 | ...[...] | semmle.label | ...[...] |
|
| array_flow.rb:344:10:344:13 | ...[...] | semmle.label | ...[...] |
|
||||||
|
| array_flow.rb:345:10:345:10 | a [element 2] : | semmle.label | a [element 2] : |
|
||||||
|
| array_flow.rb:345:10:345:10 | a [element 2] : | semmle.label | a [element 2] : |
|
||||||
| array_flow.rb:345:10:345:10 | a [element] : | semmle.label | a [element] : |
|
| array_flow.rb:345:10:345:10 | a [element] : | semmle.label | a [element] : |
|
||||||
| array_flow.rb:345:10:345:10 | a [element] : | semmle.label | a [element] : |
|
| array_flow.rb:345:10:345:10 | a [element] : | semmle.label | a [element] : |
|
||||||
| array_flow.rb:345:10:345:13 | ...[...] | semmle.label | ...[...] |
|
| array_flow.rb:345:10:345:13 | ...[...] | semmle.label | ...[...] |
|
||||||
@@ -5278,14 +5262,14 @@ nodes
|
|||||||
| array_flow.rb:945:16:945:16 | c [element 0] : | semmle.label | c [element 0] : |
|
| array_flow.rb:945:16:945:16 | c [element 0] : | semmle.label | c [element 0] : |
|
||||||
| array_flow.rb:946:10:946:10 | d [element 2, element 0] : | semmle.label | d [element 2, element 0] : |
|
| array_flow.rb:946:10:946:10 | d [element 2, element 0] : | semmle.label | d [element 2, element 0] : |
|
||||||
| array_flow.rb:946:10:946:10 | d [element 2, element 0] : | semmle.label | d [element 2, element 0] : |
|
| array_flow.rb:946:10:946:10 | d [element 2, element 0] : | semmle.label | d [element 2, element 0] : |
|
||||||
| array_flow.rb:946:10:946:22 | call to rassoc [element] : | semmle.label | call to rassoc [element] : |
|
| array_flow.rb:946:10:946:22 | call to rassoc [element 0] : | semmle.label | call to rassoc [element 0] : |
|
||||||
| array_flow.rb:946:10:946:22 | call to rassoc [element] : | semmle.label | call to rassoc [element] : |
|
| array_flow.rb:946:10:946:22 | call to rassoc [element 0] : | semmle.label | call to rassoc [element 0] : |
|
||||||
| array_flow.rb:946:10:946:25 | ...[...] | semmle.label | ...[...] |
|
| array_flow.rb:946:10:946:25 | ...[...] | semmle.label | ...[...] |
|
||||||
| array_flow.rb:946:10:946:25 | ...[...] | semmle.label | ...[...] |
|
| array_flow.rb:946:10:946:25 | ...[...] | semmle.label | ...[...] |
|
||||||
| array_flow.rb:947:10:947:10 | d [element 2, element 0] : | semmle.label | d [element 2, element 0] : |
|
| array_flow.rb:947:10:947:10 | d [element 2, element 0] : | semmle.label | d [element 2, element 0] : |
|
||||||
| array_flow.rb:947:10:947:10 | d [element 2, element 0] : | semmle.label | d [element 2, element 0] : |
|
| array_flow.rb:947:10:947:10 | d [element 2, element 0] : | semmle.label | d [element 2, element 0] : |
|
||||||
| array_flow.rb:947:10:947:22 | call to rassoc [element] : | semmle.label | call to rassoc [element] : |
|
| array_flow.rb:947:10:947:22 | call to rassoc [element 0] : | semmle.label | call to rassoc [element 0] : |
|
||||||
| array_flow.rb:947:10:947:22 | call to rassoc [element] : | semmle.label | call to rassoc [element] : |
|
| array_flow.rb:947:10:947:22 | call to rassoc [element 0] : | semmle.label | call to rassoc [element 0] : |
|
||||||
| array_flow.rb:947:10:947:25 | ...[...] | semmle.label | ...[...] |
|
| array_flow.rb:947:10:947:25 | ...[...] | semmle.label | ...[...] |
|
||||||
| array_flow.rb:947:10:947:25 | ...[...] | semmle.label | ...[...] |
|
| array_flow.rb:947:10:947:25 | ...[...] | semmle.label | ...[...] |
|
||||||
| array_flow.rb:951:10:951:21 | call to source : | semmle.label | call to source : |
|
| array_flow.rb:951:10:951:21 | call to source : | semmle.label | call to source : |
|
||||||
@@ -6910,12 +6894,16 @@ nodes
|
|||||||
| array_flow.rb:1576:9:1576:9 | a [element 1] : | semmle.label | a [element 1] : |
|
| array_flow.rb:1576:9:1576:9 | a [element 1] : | semmle.label | a [element 1] : |
|
||||||
| array_flow.rb:1576:9:1576:9 | a [element 3] : | semmle.label | a [element 3] : |
|
| array_flow.rb:1576:9:1576:9 | a [element 3] : | semmle.label | a [element 3] : |
|
||||||
| array_flow.rb:1576:9:1576:9 | a [element 3] : | semmle.label | a [element 3] : |
|
| array_flow.rb:1576:9:1576:9 | a [element 3] : | semmle.label | a [element 3] : |
|
||||||
|
| array_flow.rb:1576:9:1576:28 | call to values_at [element 1] : | semmle.label | call to values_at [element 1] : |
|
||||||
|
| array_flow.rb:1576:9:1576:28 | call to values_at [element 1] : | semmle.label | call to values_at [element 1] : |
|
||||||
| array_flow.rb:1576:9:1576:28 | call to values_at [element] : | semmle.label | call to values_at [element] : |
|
| array_flow.rb:1576:9:1576:28 | call to values_at [element] : | semmle.label | call to values_at [element] : |
|
||||||
| array_flow.rb:1576:9:1576:28 | call to values_at [element] : | semmle.label | call to values_at [element] : |
|
| array_flow.rb:1576:9:1576:28 | call to values_at [element] : | semmle.label | call to values_at [element] : |
|
||||||
| array_flow.rb:1577:10:1577:10 | b [element] : | semmle.label | b [element] : |
|
| array_flow.rb:1577:10:1577:10 | b [element] : | semmle.label | b [element] : |
|
||||||
| array_flow.rb:1577:10:1577:10 | b [element] : | semmle.label | b [element] : |
|
| array_flow.rb:1577:10:1577:10 | b [element] : | semmle.label | b [element] : |
|
||||||
| array_flow.rb:1577:10:1577:13 | ...[...] | semmle.label | ...[...] |
|
| array_flow.rb:1577:10:1577:13 | ...[...] | semmle.label | ...[...] |
|
||||||
| array_flow.rb:1577:10:1577:13 | ...[...] | semmle.label | ...[...] |
|
| array_flow.rb:1577:10:1577:13 | ...[...] | semmle.label | ...[...] |
|
||||||
|
| array_flow.rb:1578:10:1578:10 | b [element 1] : | semmle.label | b [element 1] : |
|
||||||
|
| array_flow.rb:1578:10:1578:10 | b [element 1] : | semmle.label | b [element 1] : |
|
||||||
| array_flow.rb:1578:10:1578:10 | b [element] : | semmle.label | b [element] : |
|
| array_flow.rb:1578:10:1578:10 | b [element] : | semmle.label | b [element] : |
|
||||||
| array_flow.rb:1578:10:1578:10 | b [element] : | semmle.label | b [element] : |
|
| array_flow.rb:1578:10:1578:10 | b [element] : | semmle.label | b [element] : |
|
||||||
| array_flow.rb:1578:10:1578:13 | ...[...] | semmle.label | ...[...] |
|
| array_flow.rb:1578:10:1578:13 | ...[...] | semmle.label | ...[...] |
|
||||||
@@ -7125,9 +7113,6 @@ subpaths
|
|||||||
| array_flow.rb:312:10:312:13 | ...[...] | array_flow.rb:308:16:308:25 | call to source : | array_flow.rb:312:10:312:13 | ...[...] | $@ | array_flow.rb:308:16:308:25 | call to source : | call to source : |
|
| array_flow.rb:312:10:312:13 | ...[...] | array_flow.rb:308:16:308:25 | call to source : | array_flow.rb:312:10:312:13 | ...[...] | $@ | array_flow.rb:308:16:308:25 | call to source : | call to source : |
|
||||||
| array_flow.rb:318:10:318:10 | b | array_flow.rb:316:16:316:27 | call to source : | array_flow.rb:318:10:318:10 | b | $@ | array_flow.rb:316:16:316:27 | call to source : | call to source : |
|
| array_flow.rb:318:10:318:10 | b | array_flow.rb:316:16:316:27 | call to source : | array_flow.rb:318:10:318:10 | b | $@ | array_flow.rb:316:16:316:27 | call to source : | call to source : |
|
||||||
| array_flow.rb:318:10:318:10 | b | array_flow.rb:317:23:317:34 | call to source : | array_flow.rb:318:10:318:10 | b | $@ | array_flow.rb:317:23:317:34 | call to source : | call to source : |
|
| array_flow.rb:318:10:318:10 | b | array_flow.rb:317:23:317:34 | call to source : | array_flow.rb:318:10:318:10 | b | $@ | array_flow.rb:317:23:317:34 | call to source : | call to source : |
|
||||||
| array_flow.rb:319:10:319:13 | ...[...] | array_flow.rb:316:16:316:27 | call to source : | array_flow.rb:319:10:319:13 | ...[...] | $@ | array_flow.rb:316:16:316:27 | call to source : | call to source : |
|
|
||||||
| array_flow.rb:320:10:320:13 | ...[...] | array_flow.rb:316:16:316:27 | call to source : | array_flow.rb:320:10:320:13 | ...[...] | $@ | array_flow.rb:316:16:316:27 | call to source : | call to source : |
|
|
||||||
| array_flow.rb:321:10:321:13 | ...[...] | array_flow.rb:316:16:316:27 | call to source : | array_flow.rb:321:10:321:13 | ...[...] | $@ | array_flow.rb:316:16:316:27 | call to source : | call to source : |
|
|
||||||
| array_flow.rb:327:10:327:10 | b | array_flow.rb:325:16:325:27 | call to source : | array_flow.rb:327:10:327:10 | b | $@ | array_flow.rb:325:16:325:27 | call to source : | call to source : |
|
| array_flow.rb:327:10:327:10 | b | array_flow.rb:325:16:325:27 | call to source : | array_flow.rb:327:10:327:10 | b | $@ | array_flow.rb:325:16:325:27 | call to source : | call to source : |
|
||||||
| array_flow.rb:328:10:328:13 | ...[...] | array_flow.rb:325:30:325:41 | call to source : | array_flow.rb:328:10:328:13 | ...[...] | $@ | array_flow.rb:325:30:325:41 | call to source : | call to source : |
|
| array_flow.rb:328:10:328:13 | ...[...] | array_flow.rb:325:30:325:41 | call to source : | array_flow.rb:328:10:328:13 | ...[...] | $@ | array_flow.rb:325:30:325:41 | call to source : | call to source : |
|
||||||
| array_flow.rb:332:10:332:10 | b | array_flow.rb:330:16:330:27 | call to source : | array_flow.rb:332:10:332:10 | b | $@ | array_flow.rb:330:16:330:27 | call to source : | call to source : |
|
| array_flow.rb:332:10:332:10 | b | array_flow.rb:330:16:330:27 | call to source : | array_flow.rb:332:10:332:10 | b | $@ | array_flow.rb:330:16:330:27 | call to source : | call to source : |
|
||||||
|
|||||||
@@ -176,8 +176,8 @@ def m19
|
|||||||
b = ["b", 1]
|
b = ["b", 1]
|
||||||
c = ["c", source(19)]
|
c = ["c", source(19)]
|
||||||
d = [a, b, c]
|
d = [a, b, c]
|
||||||
sink (d.assoc("a")[0]) # $ hasValueFlow=19
|
sink (d.assoc("a")[1]) # $ hasValueFlow=19
|
||||||
sink (d.assoc("c")[0]) # $ hasValueFlow=19
|
sink (d.assoc("c")[1]) # $ hasValueFlow=19
|
||||||
end
|
end
|
||||||
|
|
||||||
def m20(i)
|
def m20(i)
|
||||||
@@ -316,9 +316,9 @@ def m36
|
|||||||
a = [0, 1, source(36.1)]
|
a = [0, 1, source(36.1)]
|
||||||
b = a.delete(2) { source(36.2) }
|
b = a.delete(2) { source(36.2) }
|
||||||
sink b # $ hasValueFlow=36.1 $ hasValueFlow=36.2
|
sink b # $ hasValueFlow=36.1 $ hasValueFlow=36.2
|
||||||
sink a[0] # $ hasValueFlow=36.1
|
sink a[0]
|
||||||
sink a[1] # $ hasValueFlow=36.1
|
sink a[1]
|
||||||
sink a[2] # $ hasValueFlow=36.1
|
sink a[2]
|
||||||
end
|
end
|
||||||
|
|
||||||
def m37(i)
|
def m37(i)
|
||||||
|
|||||||
1231
ruby/ql/test/library-tests/dataflow/hash-flow/hash-flow.expected
Normal file
1231
ruby/ql/test/library-tests/dataflow/hash-flow/hash-flow.expected
Normal file
File diff suppressed because it is too large
Load Diff
15
ruby/ql/test/library-tests/dataflow/hash-flow/hash-flow.ql
Normal file
15
ruby/ql/test/library-tests/dataflow/hash-flow/hash-flow.ql
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* @kind path-problem
|
||||||
|
*/
|
||||||
|
|
||||||
|
import ruby
|
||||||
|
import TestUtilities.InlineFlowTest
|
||||||
|
import PathGraph
|
||||||
|
|
||||||
|
class HasFlowTest extends InlineFlowTest {
|
||||||
|
override DataFlow::Configuration getTaintFlowConfig() { none() }
|
||||||
|
}
|
||||||
|
|
||||||
|
from DataFlow::PathNode source, DataFlow::PathNode sink, DefaultValueFlowConf conf
|
||||||
|
where conf.hasFlowPath(source, sink)
|
||||||
|
select sink, source, sink, "$@", source, source.toString()
|
||||||
725
ruby/ql/test/library-tests/dataflow/hash-flow/hash_flow.rb
Normal file
725
ruby/ql/test/library-tests/dataflow/hash-flow/hash_flow.rb
Normal file
@@ -0,0 +1,725 @@
|
|||||||
|
def taint x
|
||||||
|
x
|
||||||
|
end
|
||||||
|
|
||||||
|
def sink x
|
||||||
|
puts x
|
||||||
|
end
|
||||||
|
|
||||||
|
def m1()
|
||||||
|
hash = {
|
||||||
|
:a => taint(1.1),
|
||||||
|
:b => 1,
|
||||||
|
c: taint(1.2),
|
||||||
|
d: 2,
|
||||||
|
'e': taint(1.3),
|
||||||
|
'f': 3,
|
||||||
|
'g' => taint(1.4),
|
||||||
|
'h' => 4,
|
||||||
|
0 => taint(1.5),
|
||||||
|
1 => 5
|
||||||
|
}
|
||||||
|
sink(hash[:a]) # $ hasValueFlow=1.1
|
||||||
|
sink(hash[:b])
|
||||||
|
sink(hash[:c]) # $ hasValueFlow=1.2
|
||||||
|
sink(hash[:d])
|
||||||
|
sink(hash['e']) # $ hasValueFlow=1.3
|
||||||
|
sink(hash['f'])
|
||||||
|
sink(hash['g']) # $ hasValueFlow=1.4
|
||||||
|
sink(hash['h'])
|
||||||
|
sink(hash[0]) # $ hasValueFlow=1.5
|
||||||
|
sink(hash[1])
|
||||||
|
end
|
||||||
|
|
||||||
|
m1()
|
||||||
|
|
||||||
|
def m2()
|
||||||
|
hash = Hash.new
|
||||||
|
hash[0] = taint(2.1)
|
||||||
|
hash[1] = 1
|
||||||
|
hash[:a] = taint(2.2)
|
||||||
|
hash[:b] = 2
|
||||||
|
hash['a'] = taint(2.3)
|
||||||
|
hash['b'] = 3
|
||||||
|
sink(hash[0]) # $ hasValueFlow=2.1
|
||||||
|
sink(hash[1])
|
||||||
|
sink(hash[:a]) # $ hasValueFlow=2.2
|
||||||
|
sink(hash[:b])
|
||||||
|
sink(hash['a']) # $ hasValueFlow=2.3
|
||||||
|
sink(hash['b'])
|
||||||
|
end
|
||||||
|
|
||||||
|
m2()
|
||||||
|
|
||||||
|
def m3()
|
||||||
|
hash1 = Hash[a: taint(3.1), b: 1]
|
||||||
|
sink(hash1[:a]) # $ hasValueFlow=3.1
|
||||||
|
sink(hash1[:b])
|
||||||
|
|
||||||
|
x = {a: taint(3.2), b: 1}
|
||||||
|
hash2 = Hash[x]
|
||||||
|
sink(hash2[:a]) # $ hasValueFlow=3.2
|
||||||
|
sink(hash2[:b])
|
||||||
|
|
||||||
|
hash3 = Hash[[[:a, taint(3.3)], [:b, 1]]]
|
||||||
|
sink(hash3[:a]) # $ hasValueFlow=3.3
|
||||||
|
sink(hash3[:b]) # $ SPURIOUS hasValueFlow=3.3
|
||||||
|
|
||||||
|
hash4 = Hash[:a, taint(3.4), :b, 1]
|
||||||
|
sink(hash4[:a]) # $ hasValueFlow=3.4
|
||||||
|
sink(hash4[:b])
|
||||||
|
end
|
||||||
|
|
||||||
|
m3()
|
||||||
|
|
||||||
|
def m4()
|
||||||
|
hash1 = ::Hash.[](a: taint(4.1), b: 1)
|
||||||
|
sink(hash1[:a]) # $ hasValueFlow=4.1
|
||||||
|
sink(hash1[:b])
|
||||||
|
end
|
||||||
|
|
||||||
|
m4()
|
||||||
|
|
||||||
|
def m5()
|
||||||
|
hash = {
|
||||||
|
:a => taint(5.1),
|
||||||
|
:b => 1
|
||||||
|
}
|
||||||
|
hash2 = Hash.try_convert(hash)
|
||||||
|
sink(hash2[:a]) # $ hasValueFlow=5.1
|
||||||
|
sink(hash2[:b])
|
||||||
|
end
|
||||||
|
|
||||||
|
m5()
|
||||||
|
|
||||||
|
def m6()
|
||||||
|
hash = Hash.new
|
||||||
|
b = (hash[:a] = taint(6.1))
|
||||||
|
sink(b) # $ hasValueFlow=6.1
|
||||||
|
end
|
||||||
|
|
||||||
|
m6()
|
||||||
|
|
||||||
|
def m7(x)
|
||||||
|
hash = Hash.new
|
||||||
|
b = hash.store(:a, taint(7.1))
|
||||||
|
sink(hash[:a]) # $ hasValueFlow=7.1
|
||||||
|
sink(b) # $ hasValueFlow=7.1
|
||||||
|
hash.store(:a, 1)
|
||||||
|
sink(hash[:a])
|
||||||
|
c = hash.store(x, taint(7.2))
|
||||||
|
sink(hash[:a]) # $ hasValueFlow=7.2
|
||||||
|
sink(hash[10]) # $ hasValueFlow=7.2
|
||||||
|
sink(c) # $ hasValueFlow=7.2
|
||||||
|
end
|
||||||
|
|
||||||
|
m7("foo")
|
||||||
|
|
||||||
|
def m8()
|
||||||
|
hash = {
|
||||||
|
:a => taint(8.1),
|
||||||
|
:b => 1
|
||||||
|
}
|
||||||
|
hash.any? { |key_or_value|
|
||||||
|
sink(key_or_value) # $ hasValueFlow=8.1
|
||||||
|
}
|
||||||
|
hash.any? { |key,value|
|
||||||
|
sink(key)
|
||||||
|
sink(value) # $ hasValueFlow=8.1
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
m8()
|
||||||
|
|
||||||
|
def m9(x, y)
|
||||||
|
hash = {
|
||||||
|
:a => taint(10.1),
|
||||||
|
:b => 1
|
||||||
|
}
|
||||||
|
b = hash.assoc(:a)
|
||||||
|
sink(b[0])
|
||||||
|
sink(b[1]) # $ hasValueFlow=10.1
|
||||||
|
sink(b[x]) # $ hasValueFlow=10.1
|
||||||
|
c = hash.assoc(y)
|
||||||
|
sink(c[1]) # $ hasValueFlow=10.1
|
||||||
|
end
|
||||||
|
|
||||||
|
m9(1, :a)
|
||||||
|
|
||||||
|
def m10()
|
||||||
|
hash = {
|
||||||
|
:a => taint(9.1),
|
||||||
|
:b => 1
|
||||||
|
}
|
||||||
|
hash.clear
|
||||||
|
sink(hash[:a])
|
||||||
|
end
|
||||||
|
|
||||||
|
m10()
|
||||||
|
|
||||||
|
def m11()
|
||||||
|
hash = {
|
||||||
|
:a => taint(11.1),
|
||||||
|
:b => 1
|
||||||
|
}
|
||||||
|
a = hash.compact
|
||||||
|
sink(a[:a]) # $ hasValueFlow=11.1
|
||||||
|
sink(a[:b])
|
||||||
|
end
|
||||||
|
|
||||||
|
m11()
|
||||||
|
|
||||||
|
def m12()
|
||||||
|
hash = {
|
||||||
|
:a => taint(12.1),
|
||||||
|
:b => 1
|
||||||
|
}
|
||||||
|
a = hash.delete(:a)
|
||||||
|
sink(a) # $ hasValueFlow=12.1
|
||||||
|
sink(hash[:a])
|
||||||
|
end
|
||||||
|
|
||||||
|
m12()
|
||||||
|
|
||||||
|
def m13()
|
||||||
|
hash = {
|
||||||
|
:a => taint(13.1),
|
||||||
|
:b => 1
|
||||||
|
}
|
||||||
|
a = hash.delete_if do |key, value|
|
||||||
|
sink key
|
||||||
|
sink value # $ hasValueFlow=13.1
|
||||||
|
end
|
||||||
|
sink(a[:a]) # $ hasValueFlow=13.1
|
||||||
|
sink(hash[:a]) # $ hasValueFlow=13.1
|
||||||
|
sink(hash[0])
|
||||||
|
end
|
||||||
|
|
||||||
|
m13()
|
||||||
|
|
||||||
|
def m14()
|
||||||
|
hash = {
|
||||||
|
:a => taint(14.1),
|
||||||
|
:b => 1,
|
||||||
|
:c => {
|
||||||
|
:d => taint(14.2),
|
||||||
|
:e => 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sink(hash.dig(:a)) # $ hasValueFlow=14.1
|
||||||
|
sink(hash.dig(:b))
|
||||||
|
sink(hash.dig(:c,:d)) # $ hasValueFlow=14.2
|
||||||
|
sink(hash.dig(:c,:e))
|
||||||
|
end
|
||||||
|
|
||||||
|
m14()
|
||||||
|
|
||||||
|
def m15()
|
||||||
|
hash = {
|
||||||
|
:a => taint(15.1),
|
||||||
|
:b => 1
|
||||||
|
}
|
||||||
|
x = hash.each do |key, value|
|
||||||
|
sink key
|
||||||
|
sink value # $ hasValueFlow=15.1
|
||||||
|
end
|
||||||
|
sink(x[:a]) # $ hasValueFlow=15.1
|
||||||
|
sink(x[:b])
|
||||||
|
end
|
||||||
|
|
||||||
|
m15()
|
||||||
|
|
||||||
|
def m16()
|
||||||
|
hash = {
|
||||||
|
:a => taint(16.1),
|
||||||
|
:b => 1
|
||||||
|
}
|
||||||
|
x = hash.each_key do |key|
|
||||||
|
sink key
|
||||||
|
end
|
||||||
|
sink(x[:a]) # $ hasValueFlow=16.1
|
||||||
|
sink(x[:b])
|
||||||
|
end
|
||||||
|
|
||||||
|
m16()
|
||||||
|
|
||||||
|
def m17()
|
||||||
|
hash = {
|
||||||
|
:a => taint(17.1),
|
||||||
|
:b => 1
|
||||||
|
}
|
||||||
|
x = hash.each_pair do |key, value|
|
||||||
|
sink key
|
||||||
|
sink value # $ hasValueFlow=17.1
|
||||||
|
end
|
||||||
|
sink(x[:a]) # $ hasValueFlow=17.1
|
||||||
|
sink(x[:b])
|
||||||
|
end
|
||||||
|
|
||||||
|
m17()
|
||||||
|
|
||||||
|
def m18()
|
||||||
|
hash = {
|
||||||
|
:a => taint(18.1),
|
||||||
|
:b => 1
|
||||||
|
}
|
||||||
|
x = hash.each_value do |value|
|
||||||
|
sink value # $ hasValueFlow=18.1
|
||||||
|
end
|
||||||
|
sink(x[:a]) # $ hasValueFlow=18.1
|
||||||
|
sink(x[:b])
|
||||||
|
end
|
||||||
|
|
||||||
|
m18()
|
||||||
|
|
||||||
|
def m19(x)
|
||||||
|
hash = {
|
||||||
|
:a => taint(19.1),
|
||||||
|
:b => 1,
|
||||||
|
:c => taint(19.2),
|
||||||
|
:d => taint(19.3)
|
||||||
|
}
|
||||||
|
x = hash.except(:a,x,:d)
|
||||||
|
sink(x[:a])
|
||||||
|
sink(x[:b])
|
||||||
|
sink(x[:c]) # $ hasValueFlow=19.2
|
||||||
|
sink(x[:d])
|
||||||
|
end
|
||||||
|
|
||||||
|
m19(:c)
|
||||||
|
|
||||||
|
def m20(x)
|
||||||
|
hash = {
|
||||||
|
:a => taint(20.1),
|
||||||
|
:b => 1,
|
||||||
|
:c => taint(20.2)
|
||||||
|
}
|
||||||
|
b = hash.fetch(taint(20.3)) do |x|
|
||||||
|
sink x # $ hasValueFlow=20.3
|
||||||
|
end
|
||||||
|
sink(b) # $ hasValueFlow=20.1 $ hasValueFlow=20.2
|
||||||
|
b = hash.fetch(:a)
|
||||||
|
sink b # $ hasValueFlow=20.1
|
||||||
|
b = hash.fetch(:a, taint(20.4))
|
||||||
|
sink b # $ hasValueFlow=20.1 $ hasValueFlow=20.4
|
||||||
|
b = hash.fetch(:b, taint(20.5))
|
||||||
|
sink b # $ hasValueFlow=20.5
|
||||||
|
b = hash.fetch(x, taint(20.6))
|
||||||
|
sink b # $ hasValueFlow=20.1 $ hasValueFlow=20.2 $ hasValueFlow=20.6
|
||||||
|
end
|
||||||
|
|
||||||
|
m20(:a)
|
||||||
|
|
||||||
|
def m21(x)
|
||||||
|
hash = {
|
||||||
|
:a => taint(21.1),
|
||||||
|
:b => 1,
|
||||||
|
:c => taint(21.2)
|
||||||
|
}
|
||||||
|
b = hash.fetch_values(taint(21.3)) do |x|
|
||||||
|
sink x # $ hasValueFlow=21.3
|
||||||
|
taint(21.4)
|
||||||
|
end
|
||||||
|
sink(b[0]) # $ hasValueFlow=21.1 $ hasValueFlow=21.2 $ hasValueFlow=21.4
|
||||||
|
b = hash.fetch_values(:a)
|
||||||
|
sink(b[0]) # $ hasValueFlow=21.1
|
||||||
|
b = hash.fetch_values(:a,x)
|
||||||
|
sink(b[1]) # $ hasValueFlow=21.1 $ hasValueFlow=21.2
|
||||||
|
end
|
||||||
|
|
||||||
|
m21(:c)
|
||||||
|
|
||||||
|
def m22()
|
||||||
|
hash = {
|
||||||
|
:a => taint(22.1),
|
||||||
|
:b => 1,
|
||||||
|
:c => taint(22.2)
|
||||||
|
}
|
||||||
|
b = hash.filter do |key, value|
|
||||||
|
sink key
|
||||||
|
sink value # $ hasValueFlow=22.1 $ hasValueFlow=22.2
|
||||||
|
true
|
||||||
|
end
|
||||||
|
sink (b[:a]) # $ hasValueFlow=22.1
|
||||||
|
end
|
||||||
|
|
||||||
|
m22()
|
||||||
|
|
||||||
|
def m23()
|
||||||
|
hash = {
|
||||||
|
:a => taint(23.1),
|
||||||
|
:b => 1,
|
||||||
|
:c => taint(23.2)
|
||||||
|
}
|
||||||
|
hash.filter! do |key, value|
|
||||||
|
sink key
|
||||||
|
sink value # $ hasValueFlow=23.1 $ hasValueFlow=23.2
|
||||||
|
true
|
||||||
|
end
|
||||||
|
sink (hash[:a]) # $ hasValueFlow=23.1
|
||||||
|
end
|
||||||
|
|
||||||
|
m23()
|
||||||
|
|
||||||
|
def m24()
|
||||||
|
hash = {
|
||||||
|
:a => taint(24.1),
|
||||||
|
:b => 1,
|
||||||
|
:c => taint(24.2)
|
||||||
|
}
|
||||||
|
b = hash.flatten
|
||||||
|
sink (b[1]) # $ hasValueFlow=24.1 $ hasValueFlow=24.2
|
||||||
|
end
|
||||||
|
|
||||||
|
m24()
|
||||||
|
|
||||||
|
def m25()
|
||||||
|
hash = {
|
||||||
|
:a => taint(25.1),
|
||||||
|
:b => 1,
|
||||||
|
:c => taint(25.2)
|
||||||
|
}
|
||||||
|
b = hash.keep_if do |key, value|
|
||||||
|
sink key
|
||||||
|
sink value # $ hasValueFlow=25.1 $ hasValueFlow=25.2
|
||||||
|
true
|
||||||
|
end
|
||||||
|
sink (hash[:a]) # $ hasValueFlow=25.1
|
||||||
|
sink (b[:a]) # $ hasValueFlow=25.1
|
||||||
|
end
|
||||||
|
|
||||||
|
m25()
|
||||||
|
|
||||||
|
def m26()
|
||||||
|
hash1 = {
|
||||||
|
:a => taint(26.1),
|
||||||
|
:b => 1,
|
||||||
|
:c => taint(26.2)
|
||||||
|
}
|
||||||
|
hash2 = {
|
||||||
|
:d => taint(26.3),
|
||||||
|
:e => 1,
|
||||||
|
:f => taint(26.4)
|
||||||
|
}
|
||||||
|
hash = hash1.merge(hash2) do |key, old_value, new_value|
|
||||||
|
sink key
|
||||||
|
sink old_value # $ hasValueFlow=26.1 $ hasValueFlow=26.2 $ hasValueFlow=26.3 $ hasValueFlow=26.4
|
||||||
|
sink new_value # $ hasValueFlow=26.1 $ hasValueFlow=26.2 $ hasValueFlow=26.3 $ hasValueFlow=26.4
|
||||||
|
end
|
||||||
|
sink (hash[:a]) # $ hasValueFlow=26.1
|
||||||
|
sink (hash[:b])
|
||||||
|
sink (hash[:c]) # $ hasValueFlow=26.2
|
||||||
|
sink (hash[:d]) # $ hasValueFlow=26.3
|
||||||
|
sink (hash[:e])
|
||||||
|
sink (hash[:f]) # $ hasValueFlow=26.4
|
||||||
|
end
|
||||||
|
|
||||||
|
m26()
|
||||||
|
|
||||||
|
def m27()
|
||||||
|
hash1 = {
|
||||||
|
:a => taint(27.1),
|
||||||
|
:b => 1,
|
||||||
|
:c => taint(27.2)
|
||||||
|
}
|
||||||
|
hash2 = {
|
||||||
|
:d => taint(27.3),
|
||||||
|
:e => 1,
|
||||||
|
:f => taint(27.4)
|
||||||
|
}
|
||||||
|
hash = hash1.merge!(hash2) do |key, old_value, new_value|
|
||||||
|
sink key
|
||||||
|
sink old_value # $ hasValueFlow=27.1 $ hasValueFlow=27.2 $ hasValueFlow=27.3 $ hasValueFlow=27.4
|
||||||
|
sink new_value # $ hasValueFlow=27.1 $ hasValueFlow=27.2 $ hasValueFlow=27.3 $ hasValueFlow=27.4
|
||||||
|
end
|
||||||
|
sink (hash[:a]) # $ hasValueFlow=27.1
|
||||||
|
sink (hash[:b])
|
||||||
|
sink (hash[:c]) # $ hasValueFlow=27.2
|
||||||
|
sink (hash[:d]) # $ hasValueFlow=27.3
|
||||||
|
sink (hash[:e])
|
||||||
|
sink (hash[:f]) # $ hasValueFlow=27.4
|
||||||
|
|
||||||
|
sink (hash1[:a]) # $ hasValueFlow=27.1
|
||||||
|
sink (hash1[:b])
|
||||||
|
sink (hash1[:c]) # $ hasValueFlow=27.2
|
||||||
|
sink (hash1[:d]) # $ hasValueFlow=27.3
|
||||||
|
sink (hash1[:e])
|
||||||
|
sink (hash1[:f]) # $ hasValueFlow=27.4
|
||||||
|
end
|
||||||
|
|
||||||
|
m27()
|
||||||
|
|
||||||
|
def m28
|
||||||
|
hash = {
|
||||||
|
:a => taint(28.1),
|
||||||
|
:b => 1
|
||||||
|
}
|
||||||
|
b = hash.rassoc(0)
|
||||||
|
sink(b[0])
|
||||||
|
sink(b[1]) # $ hasValueFlow=28.1
|
||||||
|
end
|
||||||
|
|
||||||
|
m28()
|
||||||
|
|
||||||
|
def m29
|
||||||
|
hash = {
|
||||||
|
:a => taint(29.1),
|
||||||
|
:b => 1
|
||||||
|
}
|
||||||
|
b = hash.reject do |key,value|
|
||||||
|
sink key
|
||||||
|
sink value # $ hasValueFlow=29.1
|
||||||
|
value > 10
|
||||||
|
end
|
||||||
|
sink b[:a] # $ hasValueFlow=29.1
|
||||||
|
end
|
||||||
|
|
||||||
|
m29()
|
||||||
|
|
||||||
|
def m30
|
||||||
|
hash = {
|
||||||
|
:a => taint(30.1),
|
||||||
|
:b => 1
|
||||||
|
}
|
||||||
|
b = hash.reject! do |key,value|
|
||||||
|
sink key
|
||||||
|
sink value # $ hasValueFlow=30.1
|
||||||
|
value > 10
|
||||||
|
end
|
||||||
|
sink b[:a] # $ hasValueFlow=30.1
|
||||||
|
sink hash[:a] # $ hasValueFlow=30.1
|
||||||
|
end
|
||||||
|
|
||||||
|
m30()
|
||||||
|
|
||||||
|
def m31()
|
||||||
|
hash = {
|
||||||
|
:a => taint(31.1),
|
||||||
|
:b => 1,
|
||||||
|
:c => taint(31.2)
|
||||||
|
}
|
||||||
|
hash2 = {
|
||||||
|
:c => taint(31.3)
|
||||||
|
}
|
||||||
|
hash2.replace(hash)
|
||||||
|
sink (hash2[:a]) # $ hasValueFlow=31.1
|
||||||
|
sink (hash2[:b])
|
||||||
|
sink (hash2[:c]) # $ hasValueFlow=31.2
|
||||||
|
end
|
||||||
|
|
||||||
|
def m32()
|
||||||
|
hash = {
|
||||||
|
:a => taint(32.1),
|
||||||
|
:b => 1,
|
||||||
|
:c => taint(32.2)
|
||||||
|
}
|
||||||
|
b = hash.select do |key, value|
|
||||||
|
sink key
|
||||||
|
sink value # $ hasValueFlow=32.1 $ hasValueFlow=32.2
|
||||||
|
true
|
||||||
|
end
|
||||||
|
sink (b[:a]) # $ hasValueFlow=32.1
|
||||||
|
end
|
||||||
|
|
||||||
|
m32()
|
||||||
|
|
||||||
|
def m33()
|
||||||
|
hash = {
|
||||||
|
:a => taint(33.1),
|
||||||
|
:b => 1,
|
||||||
|
:c => taint(33.2)
|
||||||
|
}
|
||||||
|
hash.select! do |key, value|
|
||||||
|
sink key
|
||||||
|
sink value # $ hasValueFlow=33.1 $ hasValueFlow=33.2
|
||||||
|
true
|
||||||
|
end
|
||||||
|
sink (hash[:a]) # $ hasValueFlow=33.1
|
||||||
|
end
|
||||||
|
|
||||||
|
m33()
|
||||||
|
|
||||||
|
def m34()
|
||||||
|
hash = {
|
||||||
|
:a => taint(34.1),
|
||||||
|
:b => 1,
|
||||||
|
:c => taint(34.2)
|
||||||
|
}
|
||||||
|
b = hash.shift
|
||||||
|
sink (hash[:a]) # $ hasValueFlow=34.1
|
||||||
|
sink (b[0])
|
||||||
|
sink (b[1]) # $ hasValueFlow=34.1 $ hasValueFlow=34.2
|
||||||
|
end
|
||||||
|
|
||||||
|
m34()
|
||||||
|
|
||||||
|
def m35(x)
|
||||||
|
hash = {
|
||||||
|
:a => taint(35.1),
|
||||||
|
:b => 1,
|
||||||
|
:c => taint(35.2)
|
||||||
|
}
|
||||||
|
b = hash.slice(:a, :b)
|
||||||
|
sink (b[:a]) # $ hasValueFlow=35.1
|
||||||
|
sink (b[:b])
|
||||||
|
sink (b[:c])
|
||||||
|
|
||||||
|
c = hash.slice(:a, x)
|
||||||
|
sink (c[:a]) # $ hasValueFlow=35.1
|
||||||
|
sink (c[:b])
|
||||||
|
sink (c[:c]) # $ hasValueFlow=35.2
|
||||||
|
end
|
||||||
|
|
||||||
|
m35(:c)
|
||||||
|
|
||||||
|
def m36()
|
||||||
|
hash = {
|
||||||
|
:a => taint(36.1),
|
||||||
|
:b => 1,
|
||||||
|
:c => taint(36.2)
|
||||||
|
}
|
||||||
|
a = hash.to_a
|
||||||
|
sink (a[0][0])
|
||||||
|
sink (a[0][1]) # $ hasValueFlow=36.1 $ hasValueFlow=36.2
|
||||||
|
end
|
||||||
|
|
||||||
|
m36()
|
||||||
|
|
||||||
|
def m37()
|
||||||
|
hash = {
|
||||||
|
:a => taint(37.1),
|
||||||
|
:b => 1,
|
||||||
|
:c => taint(37.2)
|
||||||
|
}
|
||||||
|
a = hash.to_h
|
||||||
|
sink (a[:a]) # $ hasValueFlow=37.1
|
||||||
|
sink (a[:b])
|
||||||
|
sink (a[:c]) # $ hasValueFlow=37.2
|
||||||
|
|
||||||
|
b = hash.to_h do |key, value|
|
||||||
|
sink key
|
||||||
|
sink value # $ hasValueFlow=37.1 $ hasValueFlow=37.2
|
||||||
|
[:d, taint(37.3)]
|
||||||
|
end
|
||||||
|
sink (b[:d]) # $ hasValueFlow=37.3
|
||||||
|
end
|
||||||
|
|
||||||
|
m37()
|
||||||
|
|
||||||
|
def m38()
|
||||||
|
hash = {
|
||||||
|
:a => taint(38.1),
|
||||||
|
:b => 1,
|
||||||
|
:c => taint(38.2)
|
||||||
|
}
|
||||||
|
a = hash.transform_keys {|key| key.to_s }
|
||||||
|
sink (a["a"]) # $ hasValueFlow=38.1 $ hasValueFlow=38.2
|
||||||
|
sink (a["b"]) # $ hasValueFlow=38.1 $ hasValueFlow=38.2
|
||||||
|
sink (a["c"]) # $ hasValueFlow=38.1 $ hasValueFlow=38.2
|
||||||
|
end
|
||||||
|
|
||||||
|
m38()
|
||||||
|
|
||||||
|
def m39()
|
||||||
|
hash = {
|
||||||
|
:a => taint(39.1),
|
||||||
|
:b => 1,
|
||||||
|
:c => taint(39.2)
|
||||||
|
}
|
||||||
|
hash.transform_keys! {|key| key.to_s }
|
||||||
|
sink (hash["a"]) # $ hasValueFlow=39.1 $ hasValueFlow=39.2
|
||||||
|
sink (hash["b"]) # $ hasValueFlow=39.1 $ hasValueFlow=39.2
|
||||||
|
sink (hash["c"]) # $ hasValueFlow=39.1 $ hasValueFlow=39.2
|
||||||
|
end
|
||||||
|
|
||||||
|
m39()
|
||||||
|
|
||||||
|
def m40()
|
||||||
|
hash = {
|
||||||
|
:a => taint(40.1),
|
||||||
|
:b => 1,
|
||||||
|
:c => taint(40.2)
|
||||||
|
}
|
||||||
|
b = hash.transform_values do |value|
|
||||||
|
sink value # $ hasValueFlow=40.1 $ hasValueFlow=40.2
|
||||||
|
taint(40.3)
|
||||||
|
end
|
||||||
|
sink (hash[:a]) # $ hasValueFlow=40.1
|
||||||
|
sink (b[:a]) # $ hasValueFlow=40.3
|
||||||
|
end
|
||||||
|
|
||||||
|
m40()
|
||||||
|
|
||||||
|
def m41()
|
||||||
|
hash = {
|
||||||
|
:a => taint(41.1),
|
||||||
|
:b => 1,
|
||||||
|
:c => taint(41.2)
|
||||||
|
}
|
||||||
|
hash.transform_values! do |value|
|
||||||
|
sink value # $ hasValueFlow=41.1 $ hasValueFlow=41.2
|
||||||
|
taint(41.3)
|
||||||
|
end
|
||||||
|
sink (hash[:a]) # $ hasValueFlow=41.3
|
||||||
|
end
|
||||||
|
|
||||||
|
m41()
|
||||||
|
|
||||||
|
def m42()
|
||||||
|
hash1 = {
|
||||||
|
:a => taint(42.1),
|
||||||
|
:b => 1,
|
||||||
|
:c => taint(42.2)
|
||||||
|
}
|
||||||
|
hash2 = {
|
||||||
|
:d => taint(42.3),
|
||||||
|
:e => 1,
|
||||||
|
:f => taint(42.4)
|
||||||
|
}
|
||||||
|
hash = hash1.update(hash2) do |key, old_value, new_value|
|
||||||
|
sink key
|
||||||
|
sink old_value # $ hasValueFlow=42.1 $ hasValueFlow=42.2 $ hasValueFlow=42.3 $ hasValueFlow=42.4
|
||||||
|
sink new_value # $ hasValueFlow=42.1 $ hasValueFlow=42.2 $ hasValueFlow=42.3 $ hasValueFlow=42.4
|
||||||
|
end
|
||||||
|
sink (hash[:a]) # $ hasValueFlow=42.1
|
||||||
|
sink (hash[:b])
|
||||||
|
sink (hash[:c]) # $ hasValueFlow=42.2
|
||||||
|
sink (hash[:d]) # $ hasValueFlow=42.3
|
||||||
|
sink (hash[:e])
|
||||||
|
sink (hash[:f]) # $ hasValueFlow=42.4
|
||||||
|
|
||||||
|
sink (hash1[:a]) # $ hasValueFlow=42.1
|
||||||
|
sink (hash1[:b])
|
||||||
|
sink (hash1[:c]) # $ hasValueFlow=42.2
|
||||||
|
sink (hash1[:d]) # $ hasValueFlow=42.3
|
||||||
|
sink (hash1[:e])
|
||||||
|
sink (hash1[:f]) # $ hasValueFlow=42.4
|
||||||
|
end
|
||||||
|
|
||||||
|
m42()
|
||||||
|
|
||||||
|
def m43()
|
||||||
|
hash = {
|
||||||
|
:a => taint(43.1),
|
||||||
|
:b => 1,
|
||||||
|
:c => taint(43.2)
|
||||||
|
}
|
||||||
|
a = hash.values
|
||||||
|
sink (a[0]) # $ hasValueFlow=43.1 # $ hasValueFlow=43.2
|
||||||
|
end
|
||||||
|
|
||||||
|
m43()
|
||||||
|
|
||||||
|
def m44(x)
|
||||||
|
hash = {
|
||||||
|
:a => taint(44.1),
|
||||||
|
:b => 1,
|
||||||
|
:c => taint(44.2)
|
||||||
|
}
|
||||||
|
b = hash.values_at(:a)
|
||||||
|
sink(b[0]) # $ hasValueFlow=44.1
|
||||||
|
b = hash.fetch_values(:a,x)
|
||||||
|
sink(b[1]) # $ hasValueFlow=44.1 $ hasValueFlow=44.2
|
||||||
|
end
|
||||||
|
|
||||||
|
m44(:c)
|
||||||
@@ -10,6 +10,8 @@ edges
|
|||||||
| params_flow.rb:21:27:21:34 | call to taint : | params_flow.rb:16:18:16:19 | p2 : |
|
| params_flow.rb:21:27:21:34 | call to taint : | params_flow.rb:16:18:16:19 | p2 : |
|
||||||
| params_flow.rb:22:13:22:20 | call to taint : | params_flow.rb:16:18:16:19 | p2 : |
|
| params_flow.rb:22:13:22:20 | call to taint : | params_flow.rb:16:18:16:19 | p2 : |
|
||||||
| params_flow.rb:22:27:22:34 | call to taint : | params_flow.rb:16:13:16:14 | p1 : |
|
| params_flow.rb:22:27:22:34 | call to taint : | params_flow.rb:16:13:16:14 | p1 : |
|
||||||
|
| params_flow.rb:23:16:23:23 | call to taint : | params_flow.rb:16:18:16:19 | p2 : |
|
||||||
|
| params_flow.rb:23:33:23:40 | call to taint : | params_flow.rb:16:13:16:14 | p1 : |
|
||||||
nodes
|
nodes
|
||||||
| params_flow.rb:9:16:9:17 | p1 : | semmle.label | p1 : |
|
| params_flow.rb:9:16:9:17 | p1 : | semmle.label | p1 : |
|
||||||
| params_flow.rb:9:20:9:21 | p2 : | semmle.label | p2 : |
|
| params_flow.rb:9:20:9:21 | p2 : | semmle.label | p2 : |
|
||||||
@@ -25,11 +27,15 @@ nodes
|
|||||||
| params_flow.rb:21:27:21:34 | call to taint : | semmle.label | call to taint : |
|
| params_flow.rb:21:27:21:34 | call to taint : | semmle.label | call to taint : |
|
||||||
| params_flow.rb:22:13:22:20 | call to taint : | semmle.label | call to taint : |
|
| params_flow.rb:22:13:22:20 | call to taint : | semmle.label | call to taint : |
|
||||||
| params_flow.rb:22:27:22:34 | call to taint : | semmle.label | call to taint : |
|
| params_flow.rb:22:27:22:34 | call to taint : | semmle.label | call to taint : |
|
||||||
|
| params_flow.rb:23:16:23:23 | call to taint : | semmle.label | call to taint : |
|
||||||
|
| params_flow.rb:23:33:23:40 | call to taint : | semmle.label | call to taint : |
|
||||||
subpaths
|
subpaths
|
||||||
#select
|
#select
|
||||||
| params_flow.rb:10:10:10:11 | p1 | params_flow.rb:14:12:14:19 | call to taint : | params_flow.rb:10:10:10:11 | p1 | $@ | params_flow.rb:14:12:14:19 | call to taint : | call to taint : |
|
| params_flow.rb:10:10:10:11 | p1 | params_flow.rb:14:12:14:19 | call to taint : | params_flow.rb:10:10:10:11 | p1 | $@ | params_flow.rb:14:12:14:19 | call to taint : | call to taint : |
|
||||||
| params_flow.rb:11:10:11:11 | p2 | params_flow.rb:14:22:14:29 | call to taint : | params_flow.rb:11:10:11:11 | p2 | $@ | params_flow.rb:14:22:14:29 | call to taint : | call to taint : |
|
| params_flow.rb:11:10:11:11 | p2 | params_flow.rb:14:22:14:29 | call to taint : | params_flow.rb:11:10:11:11 | p2 | $@ | params_flow.rb:14:22:14:29 | call to taint : | call to taint : |
|
||||||
| params_flow.rb:17:10:17:11 | p1 | params_flow.rb:21:13:21:20 | call to taint : | params_flow.rb:17:10:17:11 | p1 | $@ | params_flow.rb:21:13:21:20 | call to taint : | call to taint : |
|
| params_flow.rb:17:10:17:11 | p1 | params_flow.rb:21:13:21:20 | call to taint : | params_flow.rb:17:10:17:11 | p1 | $@ | params_flow.rb:21:13:21:20 | call to taint : | call to taint : |
|
||||||
| params_flow.rb:17:10:17:11 | p1 | params_flow.rb:22:27:22:34 | call to taint : | params_flow.rb:17:10:17:11 | p1 | $@ | params_flow.rb:22:27:22:34 | call to taint : | call to taint : |
|
| params_flow.rb:17:10:17:11 | p1 | params_flow.rb:22:27:22:34 | call to taint : | params_flow.rb:17:10:17:11 | p1 | $@ | params_flow.rb:22:27:22:34 | call to taint : | call to taint : |
|
||||||
|
| params_flow.rb:17:10:17:11 | p1 | params_flow.rb:23:33:23:40 | call to taint : | params_flow.rb:17:10:17:11 | p1 | $@ | params_flow.rb:23:33:23:40 | call to taint : | call to taint : |
|
||||||
| params_flow.rb:18:10:18:11 | p2 | params_flow.rb:21:27:21:34 | call to taint : | params_flow.rb:18:10:18:11 | p2 | $@ | params_flow.rb:21:27:21:34 | call to taint : | call to taint : |
|
| params_flow.rb:18:10:18:11 | p2 | params_flow.rb:21:27:21:34 | call to taint : | params_flow.rb:18:10:18:11 | p2 | $@ | params_flow.rb:21:27:21:34 | call to taint : | call to taint : |
|
||||||
| params_flow.rb:18:10:18:11 | p2 | params_flow.rb:22:13:22:20 | call to taint : | params_flow.rb:18:10:18:11 | p2 | $@ | params_flow.rb:22:13:22:20 | call to taint : | call to taint : |
|
| params_flow.rb:18:10:18:11 | p2 | params_flow.rb:22:13:22:20 | call to taint : | params_flow.rb:18:10:18:11 | p2 | $@ | params_flow.rb:22:13:22:20 | call to taint : | call to taint : |
|
||||||
|
| params_flow.rb:18:10:18:11 | p2 | params_flow.rb:23:16:23:23 | call to taint : | params_flow.rb:18:10:18:11 | p2 | $@ | params_flow.rb:23:16:23:23 | call to taint : | call to taint : |
|
||||||
|
|||||||
@@ -14,9 +14,10 @@ end
|
|||||||
positional(taint(1), taint(2))
|
positional(taint(1), taint(2))
|
||||||
|
|
||||||
def keyword(p1:, p2:)
|
def keyword(p1:, p2:)
|
||||||
sink p1 # $ hasValueFlow=3 $ hasValueFlow=6
|
sink p1 # $ hasValueFlow=3 $ hasValueFlow=6 $ hasValueFlow=8
|
||||||
sink p2 # $ hasValueFlow=4 $ hasValueFlow=5
|
sink p2 # $ hasValueFlow=4 $ hasValueFlow=5 $ hasValueFlow=7
|
||||||
end
|
end
|
||||||
|
|
||||||
keyword(p1: taint(3), p2: taint(4))
|
keyword(p1: taint(3), p2: taint(4))
|
||||||
keyword(p2: taint(5), p1: taint(6))
|
keyword(p2: taint(5), p1: taint(6))
|
||||||
|
keyword(:p2 => taint(7), :p1 => taint(8))
|
||||||
|
|||||||
@@ -249,7 +249,6 @@ edges
|
|||||||
| string_flow.rb:283:9:283:9 | a : | string_flow.rb:283:9:283:14 | ...[...] : |
|
| string_flow.rb:283:9:283:9 | a : | string_flow.rb:283:9:283:14 | ...[...] : |
|
||||||
| string_flow.rb:283:9:283:9 | a : | string_flow.rb:283:9:283:14 | ...[...] [element 0] : |
|
| string_flow.rb:283:9:283:9 | a : | string_flow.rb:283:9:283:14 | ...[...] [element 0] : |
|
||||||
| string_flow.rb:283:9:283:9 | a : | string_flow.rb:283:9:283:14 | ...[...] [element 1] : |
|
| string_flow.rb:283:9:283:9 | a : | string_flow.rb:283:9:283:14 | ...[...] [element 1] : |
|
||||||
| string_flow.rb:283:9:283:9 | a : | string_flow.rb:283:9:283:14 | ...[...] [element] : |
|
|
||||||
| string_flow.rb:283:9:283:9 | a [element 1] : | string_flow.rb:283:9:283:14 | ...[...] [element 0] : |
|
| string_flow.rb:283:9:283:9 | a [element 1] : | string_flow.rb:283:9:283:14 | ...[...] [element 0] : |
|
||||||
| string_flow.rb:283:9:283:9 | a [element 2] : | string_flow.rb:283:9:283:14 | ...[...] [element 1] : |
|
| string_flow.rb:283:9:283:9 | a [element 2] : | string_flow.rb:283:9:283:14 | ...[...] [element 1] : |
|
||||||
| string_flow.rb:283:9:283:9 | a [element] : | string_flow.rb:283:9:283:14 | ...[...] [element] : |
|
| string_flow.rb:283:9:283:9 | a [element] : | string_flow.rb:283:9:283:14 | ...[...] [element] : |
|
||||||
|
|||||||
@@ -14,6 +14,9 @@
|
|||||||
| tst-IncompleteHostnameRegExp.rb:17:14:17:30 | test.example.com$ | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.rb:17:13:17:31 | `test.example.com$` | here |
|
| tst-IncompleteHostnameRegExp.rb:17:14:17:30 | test.example.com$ | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.rb:17:13:17:31 | `test.example.com$` | here |
|
||||||
| tst-IncompleteHostnameRegExp.rb:19:14:19:30 | ^test.example.com | This string, which is used as a regular expression $@, has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.rb:20:13:20:26 | "#{...}$" | here |
|
| tst-IncompleteHostnameRegExp.rb:19:14:19:30 | ^test.example.com | This string, which is used as a regular expression $@, has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.rb:20:13:20:26 | "#{...}$" | here |
|
||||||
| tst-IncompleteHostnameRegExp.rb:20:14:20:31 | ^test.example.com$ | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.rb:20:13:20:26 | "#{...}$" | here |
|
| tst-IncompleteHostnameRegExp.rb:20:14:20:31 | ^test.example.com$ | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.rb:20:13:20:26 | "#{...}$" | here |
|
||||||
|
| tst-IncompleteHostnameRegExp.rb:22:24:22:40 | test.example.com$ | This string, which is used as a regular expression $@, has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.rb:23:13:23:29 | ...[...] | here |
|
||||||
|
| tst-IncompleteHostnameRegExp.rb:28:24:28:40 | test.example.com$ | This string, which is used as a regular expression $@, has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.rb:63:20:63:36 | ...[...] | here |
|
||||||
|
| tst-IncompleteHostnameRegExp.rb:30:27:30:43 | test.example.com$ | This string, which is used as a regular expression $@, has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.rb:66:20:66:36 | ...[...] | here |
|
||||||
| tst-IncompleteHostnameRegExp.rb:37:3:37:53 | ^(https?:)?\\/\\/((service\|www).)?example.com(?=$\|\\/) | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.rb:37:2:37:54 | /^(https?:)?\\/\\/((service\|www).../ | here |
|
| tst-IncompleteHostnameRegExp.rb:37:3:37:53 | ^(https?:)?\\/\\/((service\|www).)?example.com(?=$\|\\/) | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.rb:37:2:37:54 | /^(https?:)?\\/\\/((service\|www).../ | here |
|
||||||
| tst-IncompleteHostnameRegExp.rb:38:3:38:43 | ^(http\|https):\\/\\/www.example.com\\/p\\/f\\/ | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.rb:38:2:38:44 | /^(http\|https):\\/\\/www.example.../ | here |
|
| tst-IncompleteHostnameRegExp.rb:38:3:38:43 | ^(http\|https):\\/\\/www.example.com\\/p\\/f\\/ | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.rb:38:2:38:44 | /^(http\|https):\\/\\/www.example.../ | here |
|
||||||
| tst-IncompleteHostnameRegExp.rb:39:5:39:30 | http:\\/\\/sub.example.com\\/ | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.rb:39:2:39:33 | /^(http:\\/\\/sub.example.com\\/)/ | here |
|
| tst-IncompleteHostnameRegExp.rb:39:5:39:30 | http:\\/\\/sub.example.com\\/ | This regular expression has an unescaped '.' before 'example.com', so it might match more hosts than expected. | tst-IncompleteHostnameRegExp.rb:39:2:39:33 | /^(http:\\/\\/sub.example.com\\/)/ | here |
|
||||||
|
|||||||
Reference in New Issue
Block a user