Ruby: Data-flow through hashes

This commit is contained in:
Tom Hvitved
2022-04-20 13:37:22 +02:00
parent 6781a76b96
commit faf24a4f18
16 changed files with 2992 additions and 202 deletions

View File

@@ -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)) }
} }

View File

@@ -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())
)
)
} }
/** /**

View File

@@ -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
)
} }
} }

View File

@@ -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]`. */

View File

@@ -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

View File

@@ -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
} }

View 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
}
}

View File

@@ -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 : |

View File

@@ -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)

File diff suppressed because it is too large Load Diff

View 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()

View 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)

View File

@@ -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 : |

View File

@@ -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))

View File

@@ -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] : |

View File

@@ -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 |