Merge pull request #7442 from hvitved/ruby/dataflow/keyword-params

Ruby: Data flow for keyword arguments/parameters
This commit is contained in:
Tom Hvitved
2021-12-22 15:23:22 +01:00
committed by GitHub
14 changed files with 374 additions and 120 deletions

View File

@@ -23,10 +23,10 @@ module SummaryComponent {
predicate content = SC::content/1;
/** Gets a summary component that represents a qualifier. */
SummaryComponent qualifier() { result = argument(-1) }
SummaryComponent qualifier() { result = argument(any(ParameterPosition pos | pos.isSelf())) }
/** Gets a summary component that represents a block argument. */
SummaryComponent block() { result = argument(-2) }
SummaryComponent block() { result = argument(any(ParameterPosition pos | pos.isBlock())) }
/** Gets a summary component that represents the return value of a call. */
SummaryComponent return() { result = SC::return(any(NormalReturnKind rk)) }
@@ -102,10 +102,10 @@ abstract class SummarizedCallable extends LibraryCallable {
/**
* Holds if values stored inside `content` are cleared on objects passed as
* the `i`th argument to this callable.
* arguments at position `pos` to this callable.
*/
pragma[nomagic]
predicate clearsContent(int i, DataFlow::Content content) { none() }
predicate clearsContent(ParameterPosition pos, DataFlow::Content content) { none() }
}
private class SummarizedCallableAdapter extends Impl::Public::SummarizedCallable {
@@ -119,8 +119,8 @@ private class SummarizedCallableAdapter extends Impl::Public::SummarizedCallable
sc.propagatesFlow(input, output, preservesValue)
}
final override predicate clearsContent(ParameterPosition i, DataFlow::Content content) {
sc.clearsContent(i, content)
final override predicate clearsContent(ParameterPosition pos, DataFlow::Content content) {
sc.clearsContent(pos, content)
}
}

View File

@@ -4,6 +4,7 @@ private import DataFlowPrivate
private import codeql.ruby.typetracking.TypeTracker
private import codeql.ruby.ast.internal.Module
private import FlowSummaryImpl as FlowSummaryImpl
private import FlowSummaryImplSpecific as FlowSummaryImplSpecific
private import codeql.ruby.dataflow.FlowSummary
newtype TReturnKind =
@@ -230,6 +231,30 @@ private module Cached {
result = yieldCall(call)
)
}
cached
newtype TArgumentPosition =
TSelfArgumentPosition() or
TBlockArgumentPosition() or
TPositionalArgumentPosition(int pos) {
exists(Call c | exists(c.getArgument(pos)))
or
FlowSummaryImplSpecific::ParsePositions::isParsedParameterPosition(_, pos)
} or
TKeywordArgumentPosition(string name) { name = any(KeywordParameter kp).getName() }
cached
newtype TParameterPosition =
TSelfParameterPosition() or
TBlockParameterPosition() or
TPositionalParameterPosition(int pos) {
pos = any(Parameter p).getPosition()
or
pos in [0 .. 10] // TODO: remove once `Argument[_]` summaries are replaced with `Argument[i..]`
or
FlowSummaryImplSpecific::ParsePositions::isParsedArgumentPosition(_, pos)
} or
TKeywordParameterPosition(string name) { name = any(KeywordParameter kp).getName() }
}
import Cached
@@ -458,18 +483,66 @@ predicate exprNodeReturnedFrom(DataFlow::ExprNode e, Callable c) {
)
}
private int parameterPosition() { result in [-2 .. max([any(Parameter p).getPosition(), 10])] }
/** A parameter position. */
class ParameterPosition extends TParameterPosition {
/** Holds if this position represents a `self` parameter. */
predicate isSelf() { this = TSelfParameterPosition() }
/** A parameter position represented by an integer. */
class ParameterPosition extends int {
ParameterPosition() { this = parameterPosition() }
/** Holds if this position represents a block parameter. */
predicate isBlock() { this = TBlockParameterPosition() }
/** Holds if this position represents a positional parameter at position `pos`. */
predicate isPositional(int pos) { this = TPositionalParameterPosition(pos) }
/** Holds if this position represents a keyword parameter named `name`. */
predicate isKeyword(string name) { this = TKeywordParameterPosition(name) }
/** Gets a textual representation of this position. */
string toString() {
this.isSelf() and result = "self"
or
this.isBlock() and result = "block"
or
exists(int pos | this.isPositional(pos) and result = "position " + pos)
or
exists(string name | this.isKeyword(name) and result = "keyword " + name)
}
}
/** An argument position represented by an integer. */
class ArgumentPosition extends int {
ArgumentPosition() { this = parameterPosition() }
/** An argument position. */
class ArgumentPosition extends TArgumentPosition {
/** Holds if this position represents a `self` argument. */
predicate isSelf() { this = TSelfArgumentPosition() }
/** Holds if this position represents a block argument. */
predicate isBlock() { this = TBlockArgumentPosition() }
/** Holds if this position represents a positional argument at position `pos`. */
predicate isPositional(int pos) { this = TPositionalArgumentPosition(pos) }
/** Holds if this position represents a keyword argument named `name`. */
predicate isKeyword(string name) { this = TKeywordArgumentPosition(name) }
/** Gets a textual representation of this position. */
string toString() {
this.isSelf() and result = "self"
or
this.isBlock() and result = "block"
or
exists(int pos | this.isPositional(pos) and result = "position " + pos)
or
exists(string name | this.isKeyword(name) and result = "keyword " + name)
}
}
/** Holds if arguments at position `apos` match parameters at position `ppos`. */
pragma[inline]
predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { ppos = apos }
predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) {
ppos.isSelf() and apos.isSelf()
or
ppos.isBlock() and apos.isBlock()
or
exists(int pos | ppos.isPositional(pos) and apos.isPositional(pos))
or
exists(string name | ppos.isKeyword(name) and apos.isKeyword(name))
}

View File

@@ -161,17 +161,29 @@ module LocalFlow {
/** An argument of a call (including qualifier arguments, excluding block arguments). */
private class Argument extends CfgNodes::ExprCfgNode {
private CfgNodes::ExprNodes::CallCfgNode call;
private int arg;
private ArgumentPosition arg;
Argument() {
this = call.getArgument(arg) and
not this.getExpr() instanceof BlockArgument
exists(int i |
this = call.getArgument(i) and
not this.getExpr() instanceof BlockArgument and
not exists(this.getExpr().(Pair).getKey().getValueText()) and
arg.isPositional(i)
)
or
this = call.getReceiver() and arg = -1
exists(CfgNodes::ExprNodes::PairCfgNode p |
p = call.getArgument(_) and
this = p.getValue() and
arg.isKeyword(p.getKey().getValueText())
)
or
this = call.getReceiver() and arg.isSelf()
}
/** Holds if this expression is the `i`th argument of `c`. */
predicate isArgumentOf(CfgNodes::ExprNodes::CallCfgNode c, int i) { c = call and i = arg }
predicate isArgumentOf(CfgNodes::ExprNodes::CallCfgNode c, ArgumentPosition pos) {
c = call and pos = arg
}
}
/** A collection of cached types and predicates to be evaluated in the same stage. */
@@ -188,7 +200,11 @@ private module Cached {
)
} or
TSsaDefinitionNode(Ssa::Definition def) or
TNormalParameterNode(Parameter p) { not p instanceof BlockParameter } or
TNormalParameterNode(Parameter p) {
p instanceof SimpleParameter or
p instanceof OptionalParameter or
p instanceof KeywordParameter
} or
TSelfParameterNode(MethodBase m) or
TBlockParameterNode(MethodBase m) or
TExprPostUpdateNode(CfgNodes::ExprCfgNode n) { n instanceof Argument } or
@@ -198,8 +214,8 @@ private module Cached {
) {
FlowSummaryImpl::Private::summaryNodeRange(c, state)
} or
TSummaryParameterNode(FlowSummaryImpl::Public::SummarizedCallable c, int i) {
FlowSummaryImpl::Private::summaryParameterNodeRange(c, i)
TSummaryParameterNode(FlowSummaryImpl::Public::SummarizedCallable c, ParameterPosition pos) {
FlowSummaryImpl::Private::summaryParameterNodeRange(c, pos)
}
class TParameterNode =
@@ -340,10 +356,10 @@ private module ParameterNodes {
abstract class ParameterNodeImpl extends NodeImpl {
abstract Parameter getParameter();
abstract predicate isSourceParameterOf(Callable c, int i);
abstract predicate isSourceParameterOf(Callable c, ParameterPosition pos);
predicate isParameterOf(DataFlowCallable c, int i) {
this.isSourceParameterOf(c.asCallable(), i)
predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) {
this.isSourceParameterOf(c.asCallable(), pos)
}
}
@@ -358,10 +374,18 @@ private module ParameterNodes {
override Parameter getParameter() { result = parameter }
override predicate isSourceParameterOf(Callable c, int i) { c.getParameter(i) = parameter }
override predicate isParameterOf(DataFlowCallable c, int i) {
this.isSourceParameterOf(c.asCallable(), i)
override predicate isSourceParameterOf(Callable c, ParameterPosition pos) {
exists(int i | pos.isPositional(i) and c.getParameter(i) = parameter |
parameter instanceof SimpleParameter
or
parameter instanceof OptionalParameter
)
or
parameter =
any(KeywordParameter kp |
c.getAParameter() = kp and
pos.isKeyword(kp.getName())
)
}
override CfgScope getCfgScope() { result = parameter.getCallable() }
@@ -384,10 +408,8 @@ private module ParameterNodes {
override Parameter getParameter() { none() }
override predicate isSourceParameterOf(Callable c, int i) { method = c and i = -1 }
override predicate isParameterOf(DataFlowCallable c, int i) {
this.isSourceParameterOf(c.asCallable(), i)
override predicate isSourceParameterOf(Callable c, ParameterPosition pos) {
method = c and pos.isSelf()
}
override CfgScope getCfgScope() { result = method }
@@ -412,10 +434,8 @@ private module ParameterNodes {
result = method.getAParameter() and result instanceof BlockParameter
}
override predicate isSourceParameterOf(Callable c, int i) { c = method and i = -2 }
override predicate isParameterOf(DataFlowCallable c, int i) {
this.isSourceParameterOf(c.asCallable(), i)
override predicate isSourceParameterOf(Callable c, ParameterPosition pos) {
c = method and pos.isBlock()
}
override CfgScope getCfgScope() { result = method }
@@ -436,15 +456,17 @@ private module ParameterNodes {
/** A parameter for a library callable with a flow summary. */
class SummaryParameterNode extends ParameterNodeImpl, TSummaryParameterNode {
private FlowSummaryImpl::Public::SummarizedCallable sc;
private int pos;
private ParameterPosition pos_;
SummaryParameterNode() { this = TSummaryParameterNode(sc, pos) }
SummaryParameterNode() { this = TSummaryParameterNode(sc, pos_) }
override Parameter getParameter() { none() }
override predicate isSourceParameterOf(Callable c, int i) { none() }
override predicate isSourceParameterOf(Callable c, ParameterPosition pos) { none() }
override predicate isParameterOf(DataFlowCallable c, int i) { sc = c and i = pos }
override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) {
sc = c and pos = pos_
}
override CfgScope getCfgScope() { none() }
@@ -452,7 +474,7 @@ private module ParameterNodes {
override EmptyLocation getLocationImpl() { any() }
override string toStringImpl() { result = "parameter " + pos + " of " + sc }
override string toStringImpl() { result = "parameter " + pos_ + " of " + sc }
}
}
@@ -477,9 +499,9 @@ class SummaryNode extends NodeImpl, TSummaryNode {
/** A data-flow node that represents a call argument. */
abstract class ArgumentNode extends Node {
/** Holds if this argument occurs at the given position in the given call. */
abstract predicate argumentOf(DataFlowCall call, int pos);
abstract predicate argumentOf(DataFlowCall call, ArgumentPosition pos);
abstract predicate sourceArgumentOf(CfgNodes::ExprNodes::CallCfgNode call, int pos);
abstract predicate sourceArgumentOf(CfgNodes::ExprNodes::CallCfgNode call, ArgumentPosition pos);
/** Gets the call in which this node is an argument. */
final DataFlowCall getCall() { this.argumentOf(result, _) }
@@ -492,18 +514,18 @@ private module ArgumentNodes {
ExplicitArgumentNode() { this.asExpr() = arg }
override predicate argumentOf(DataFlowCall call, int pos) {
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
this.sourceArgumentOf(call.asCall(), pos)
}
override predicate sourceArgumentOf(CfgNodes::ExprNodes::CallCfgNode call, int pos) {
override predicate sourceArgumentOf(CfgNodes::ExprNodes::CallCfgNode call, ArgumentPosition pos) {
arg.isArgumentOf(call, pos)
}
}
/** A data-flow node that represents the `self` argument of a call. */
class SelfArgumentNode extends ExplicitArgumentNode {
SelfArgumentNode() { arg.isArgumentOf(_, -1) }
SelfArgumentNode() { arg.isArgumentOf(_, any(ArgumentPosition pos | pos.isSelf())) }
}
/** A data-flow node that represents a block argument. */
@@ -513,12 +535,12 @@ private module ArgumentNodes {
exists(CfgNodes::ExprNodes::CallCfgNode c | c.getBlock() = this.asExpr())
}
override predicate argumentOf(DataFlowCall call, int pos) {
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
this.sourceArgumentOf(call.asCall(), pos)
}
override predicate sourceArgumentOf(CfgNodes::ExprNodes::CallCfgNode call, int pos) {
pos = -2 and
override predicate sourceArgumentOf(CfgNodes::ExprNodes::CallCfgNode call, ArgumentPosition pos) {
pos.isBlock() and
(
this.asExpr() = call.getBlock()
or
@@ -534,9 +556,11 @@ private module ArgumentNodes {
private class SummaryArgumentNode extends SummaryNode, ArgumentNode {
SummaryArgumentNode() { FlowSummaryImpl::Private::summaryArgumentNode(_, this, _) }
override predicate sourceArgumentOf(CfgNodes::ExprNodes::CallCfgNode call, int pos) { none() }
override predicate sourceArgumentOf(CfgNodes::ExprNodes::CallCfgNode call, ArgumentPosition pos) {
none()
}
override predicate argumentOf(DataFlowCall call, int pos) {
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
FlowSummaryImpl::Private::summaryArgumentNode(call, this, pos)
}
}
@@ -847,7 +871,7 @@ predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) {
)
or
receiver = call.(SummaryCall).getReceiver() and
if receiver.(ParameterNodeImpl).isParameterOf(_, -2)
if receiver.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pos | pos.isBlock()))
then kind = TYieldCallKind()
else kind = TLambdaCallKind()
}

View File

@@ -736,10 +736,17 @@ module Private {
}
pragma[nomagic]
private ParamNode summaryArgParam(ArgNode arg, ReturnKindExt rk, OutNodeExt out) {
exists(DataFlowCall call, ParameterPosition ppos, SummarizedCallable sc |
private ParamNode summaryArgParam0(DataFlowCall call, ArgNode arg) {
exists(ParameterPosition ppos, SummarizedCallable sc |
argumentPositionMatch(call, arg, ppos) and
viableParam(call, sc, ppos, result) and
viableParam(call, sc, ppos, result)
)
}
pragma[nomagic]
private ParamNode summaryArgParam(ArgNode arg, ReturnKindExt rk, OutNodeExt out) {
exists(DataFlowCall call |
result = summaryArgParam0(call, arg) and
out = rk.getAnOutNode(call)
)
}

View File

@@ -12,7 +12,7 @@ private import FlowSummaryImpl::Public
private import codeql.ruby.dataflow.FlowSummary as FlowSummary
/** Gets the parameter position of the instance parameter. */
int instanceParameterPosition() { none() } // disables implicit summary flow to `self` for callbacks
ArgumentPosition instanceParameterPosition() { none() } // disables implicit summary flow to `self` for callbacks
/** Gets the synthesized summary data-flow node for the given values. */
Node summaryNode(SummarizedCallable c, SummaryNodeState state) { result = TSummaryNode(c, state) }
@@ -31,8 +31,8 @@ DataFlowType getReturnType(SummarizedCallable c, ReturnKind rk) { any() }
* Gets the type of the `i`th parameter in a synthesized call that targets a
* callback of type `t`.
*/
bindingset[t, i]
DataFlowType getCallbackParameterType(DataFlowType t, int i) { any() }
bindingset[t, pos]
DataFlowType getCallbackParameterType(DataFlowType t, ArgumentPosition pos) { any() }
/**
* Gets the return type of kind `rk` in a synthesized call that targets a
@@ -63,12 +63,13 @@ SummaryComponent interpretComponentSpecific(string c) {
result = FlowSummary::SummaryComponent::block()
or
c = "Argument[_]" and
result = FlowSummary::SummaryComponent::argument(any(int i | i >= 0))
result = FlowSummary::SummaryComponent::argument(any(ParameterPosition pos | pos.isPositional(_)))
}
/** Gets the textual representation of a summary component in the format used for flow summaries. */
string getComponentSpecificCsv(SummaryComponent sc) {
sc = TArgumentSummaryComponent(-2) and result = "BlockArgument"
sc = TArgumentSummaryComponent(any(ParameterPosition pos | pos.isBlock())) and
result = "BlockArgument"
}
/** Gets the textual representation of a parameter position in the format used for flow summaries. */
@@ -127,21 +128,57 @@ private module UnusedSourceSinkInterpretation {
import UnusedSourceSinkInterpretation
bindingset[s]
private int parsePosition(string s) {
result = s.regexpCapture("([-0-9]+)", 1).toInt()
or
exists(int n1, int n2 |
s.regexpCapture("([-0-9]+)\\.\\.([0-9]+)", 1).toInt() = n1 and
s.regexpCapture("([-0-9]+)\\.\\.([0-9]+)", 2).toInt() = n2 and
result in [n1 .. n2]
)
module ParsePositions {
private import FlowSummaryImpl
private predicate isParamBody(string body) {
exists(string c |
Private::External::specSplit(_, c, _) and
body = c.regexpCapture("Parameter\\[([^\\]]*)\\]", 1)
)
}
private predicate isArgBody(string body) {
exists(string c |
Private::External::specSplit(_, c, _) and
body = c.regexpCapture("Argument\\[([^\\]]*)\\]", 1)
)
}
bindingset[s]
private int parsePosition(string s) {
result = s.regexpCapture("([-0-9]+)", 1).toInt()
or
exists(int n1, int n2 |
s.regexpCapture("([-0-9]+)\\.\\.([0-9]+)", 1).toInt() = n1 and
s.regexpCapture("([-0-9]+)\\.\\.([0-9]+)", 2).toInt() = n2 and
result in [n1 .. n2]
)
}
predicate isParsedParameterPosition(string c, int i) {
isParamBody(c) and
i = parsePosition(c)
}
predicate isParsedArgumentPosition(string c, int i) {
isArgBody(c) and
i = parsePosition(c)
}
}
/** Gets the argument position obtained by parsing `X` in `Parameter[X]`. */
bindingset[s]
ArgumentPosition parseParamBody(string s) { result = parsePosition(s) }
ArgumentPosition parseParamBody(string s) {
exists(int i |
ParsePositions::isParsedParameterPosition(s, i) and
result.isPositional(i)
)
}
/** Gets the parameter position obtained by parsing `X` in `Argument[X]`. */
bindingset[s]
ParameterPosition parseArgBody(string s) { result = parsePosition(s) }
ParameterPosition parseArgBody(string s) {
exists(int i |
ParsePositions::isParsedArgumentPosition(s, i) and
result.isPositional(i)
)
}

View File

@@ -23,6 +23,28 @@ predicate jumpStep = DataFlowPrivate::jumpStep/2;
*/
string getPossibleContentName() { result = getSetterCallAttributeName(_) }
pragma[noinline]
private predicate argumentPositionMatch(
ExprNodes::CallCfgNode call, DataFlowPrivate::ArgumentNode arg,
DataFlowDispatch::ParameterPosition ppos
) {
exists(DataFlowDispatch::ArgumentPosition apos |
arg.sourceArgumentOf(call, apos) and
DataFlowDispatch::parameterMatch(ppos, apos)
)
}
pragma[noinline]
private predicate viableParam(
ExprNodes::CallCfgNode call, DataFlowPrivate::ParameterNodeImpl p,
DataFlowDispatch::ParameterPosition ppos
) {
exists(CFG::CfgScope callable |
DataFlowDispatch::getTarget(call) = callable and
p.isSourceParameterOf(callable, ppos)
)
}
/**
* Holds if `nodeFrom` steps to `nodeTo` by being passed as a parameter in a call.
*
@@ -31,10 +53,9 @@ string getPossibleContentName() { result = getSetterCallAttributeName(_) }
* methods is done using API graphs (which uses type tracking).
*/
predicate callStep(Node nodeFrom, Node nodeTo) {
exists(ExprNodes::CallCfgNode call, CFG::CfgScope callable, int i |
DataFlowDispatch::getTarget(call) = callable and
nodeFrom.(DataFlowPrivate::ArgumentNode).sourceArgumentOf(call, i) and
nodeTo.(DataFlowPrivate::ParameterNodeImpl).isSourceParameterOf(callable, i)
exists(ExprNodes::CallCfgNode call, DataFlowDispatch::ParameterPosition pos |
argumentPositionMatch(call, nodeFrom, pos) and
viableParam(call, nodeTo, pos)
)
or
// In normal data-flow, this will be a local flow step. But for type tracking

View File

@@ -14,38 +14,38 @@ ret
| local_dataflow.rb:52:3:52:10 | "normal" |
| local_dataflow.rb:69:3:76:5 | ... = ... |
arg
| local_dataflow.rb:3:8:3:10 | self | local_dataflow.rb:3:8:3:10 | call to p | -1 |
| local_dataflow.rb:3:10:3:10 | a | local_dataflow.rb:3:8:3:10 | call to p | 0 |
| local_dataflow.rb:6:8:6:8 | a | local_dataflow.rb:6:10:6:11 | ... + ... | -1 |
| local_dataflow.rb:6:13:6:13 | b | local_dataflow.rb:6:10:6:11 | ... + ... | 0 |
| local_dataflow.rb:9:9:9:15 | Array | local_dataflow.rb:9:9:9:15 | call to [] | -1 |
| local_dataflow.rb:9:10:9:10 | 1 | local_dataflow.rb:9:9:9:15 | call to [] | 0 |
| local_dataflow.rb:9:12:9:12 | 2 | local_dataflow.rb:9:9:9:15 | call to [] | 1 |
| local_dataflow.rb:9:14:9:14 | 3 | local_dataflow.rb:9:9:9:15 | call to [] | 2 |
| local_dataflow.rb:10:5:13:3 | { ... } | local_dataflow.rb:10:5:13:3 | call to each | -2 |
| local_dataflow.rb:10:14:10:18 | array | local_dataflow.rb:10:5:13:3 | call to each | -1 |
| local_dataflow.rb:11:1:11:2 | self | local_dataflow.rb:11:1:11:2 | call to do | -1 |
| local_dataflow.rb:12:3:12:5 | self | local_dataflow.rb:12:3:12:5 | call to p | -1 |
| local_dataflow.rb:12:5:12:5 | x | local_dataflow.rb:12:3:12:5 | call to p | 0 |
| local_dataflow.rb:15:1:17:3 | { ... } | local_dataflow.rb:15:1:17:3 | call to each | -2 |
| local_dataflow.rb:15:10:15:14 | array | local_dataflow.rb:15:1:17:3 | call to each | -1 |
| local_dataflow.rb:19:1:21:3 | { ... } | local_dataflow.rb:19:1:21:3 | call to each | -2 |
| local_dataflow.rb:19:10:19:14 | array | local_dataflow.rb:19:1:21:3 | call to each | -1 |
| local_dataflow.rb:20:6:20:6 | x | local_dataflow.rb:20:6:20:10 | ... > ... | -1 |
| local_dataflow.rb:20:10:20:10 | 1 | local_dataflow.rb:20:6:20:10 | ... > ... | 0 |
| local_dataflow.rb:35:6:35:6 | x | local_dataflow.rb:35:6:35:11 | ... == ... | -1 |
| local_dataflow.rb:35:11:35:11 | 4 | local_dataflow.rb:35:6:35:11 | ... == ... | 0 |
| local_dataflow.rb:42:6:42:6 | x | local_dataflow.rb:42:6:42:11 | ... == ... | -1 |
| local_dataflow.rb:42:11:42:11 | 4 | local_dataflow.rb:42:6:42:11 | ... == ... | 0 |
| local_dataflow.rb:49:1:53:3 | self | local_dataflow.rb:49:1:53:3 | call to m | -1 |
| local_dataflow.rb:49:3:53:3 | do ... end | local_dataflow.rb:49:1:53:3 | call to m | -2 |
| local_dataflow.rb:50:18:50:18 | x | local_dataflow.rb:50:18:50:22 | ... < ... | -1 |
| local_dataflow.rb:50:22:50:22 | 4 | local_dataflow.rb:50:18:50:22 | ... < ... | 0 |
| local_dataflow.rb:51:20:51:20 | x | local_dataflow.rb:51:20:51:24 | ... < ... | -1 |
| local_dataflow.rb:51:24:51:24 | 9 | local_dataflow.rb:51:20:51:24 | ... < ... | 0 |
| local_dataflow.rb:55:1:55:14 | self | local_dataflow.rb:55:1:55:14 | call to foo | -1 |
| local_dataflow.rb:55:5:55:13 | Array | local_dataflow.rb:55:5:55:13 | call to [] | -1 |
| local_dataflow.rb:55:5:55:13 | call to [] | local_dataflow.rb:55:1:55:14 | call to foo | 0 |
| local_dataflow.rb:55:6:55:6 | 1 | local_dataflow.rb:55:5:55:13 | call to [] | 0 |
| local_dataflow.rb:55:9:55:9 | 2 | local_dataflow.rb:55:5:55:13 | call to [] | 1 |
| local_dataflow.rb:55:12:55:12 | 3 | local_dataflow.rb:55:5:55:13 | call to [] | 2 |
| local_dataflow.rb:3:8:3:10 | self | local_dataflow.rb:3:8:3:10 | call to p | self |
| local_dataflow.rb:3:10:3:10 | a | local_dataflow.rb:3:8:3:10 | call to p | position 0 |
| local_dataflow.rb:6:8:6:8 | a | local_dataflow.rb:6:10:6:11 | ... + ... | self |
| local_dataflow.rb:6:13:6:13 | b | local_dataflow.rb:6:10:6:11 | ... + ... | position 0 |
| local_dataflow.rb:9:9:9:15 | Array | local_dataflow.rb:9:9:9:15 | call to [] | self |
| local_dataflow.rb:9:10:9:10 | 1 | local_dataflow.rb:9:9:9:15 | call to [] | position 0 |
| local_dataflow.rb:9:12:9:12 | 2 | local_dataflow.rb:9:9:9:15 | call to [] | position 1 |
| local_dataflow.rb:9:14:9:14 | 3 | local_dataflow.rb:9:9:9:15 | call to [] | position 2 |
| local_dataflow.rb:10:5:13:3 | { ... } | local_dataflow.rb:10:5:13:3 | call to each | block |
| local_dataflow.rb:10:14:10:18 | array | local_dataflow.rb:10:5:13:3 | call to each | self |
| local_dataflow.rb:11:1:11:2 | self | local_dataflow.rb:11:1:11:2 | call to do | self |
| local_dataflow.rb:12:3:12:5 | self | local_dataflow.rb:12:3:12:5 | call to p | self |
| local_dataflow.rb:12:5:12:5 | x | local_dataflow.rb:12:3:12:5 | call to p | position 0 |
| local_dataflow.rb:15:1:17:3 | { ... } | local_dataflow.rb:15:1:17:3 | call to each | block |
| local_dataflow.rb:15:10:15:14 | array | local_dataflow.rb:15:1:17:3 | call to each | self |
| local_dataflow.rb:19:1:21:3 | { ... } | local_dataflow.rb:19:1:21:3 | call to each | block |
| local_dataflow.rb:19:10:19:14 | array | local_dataflow.rb:19:1:21:3 | call to each | self |
| local_dataflow.rb:20:6:20:6 | x | local_dataflow.rb:20:6:20:10 | ... > ... | self |
| local_dataflow.rb:20:10:20:10 | 1 | local_dataflow.rb:20:6:20:10 | ... > ... | position 0 |
| local_dataflow.rb:35:6:35:6 | x | local_dataflow.rb:35:6:35:11 | ... == ... | self |
| local_dataflow.rb:35:11:35:11 | 4 | local_dataflow.rb:35:6:35:11 | ... == ... | position 0 |
| local_dataflow.rb:42:6:42:6 | x | local_dataflow.rb:42:6:42:11 | ... == ... | self |
| local_dataflow.rb:42:11:42:11 | 4 | local_dataflow.rb:42:6:42:11 | ... == ... | position 0 |
| local_dataflow.rb:49:1:53:3 | self | local_dataflow.rb:49:1:53:3 | call to m | self |
| local_dataflow.rb:49:3:53:3 | do ... end | local_dataflow.rb:49:1:53:3 | call to m | block |
| local_dataflow.rb:50:18:50:18 | x | local_dataflow.rb:50:18:50:22 | ... < ... | self |
| local_dataflow.rb:50:22:50:22 | 4 | local_dataflow.rb:50:18:50:22 | ... < ... | position 0 |
| local_dataflow.rb:51:20:51:20 | x | local_dataflow.rb:51:20:51:24 | ... < ... | self |
| local_dataflow.rb:51:24:51:24 | 9 | local_dataflow.rb:51:20:51:24 | ... < ... | position 0 |
| local_dataflow.rb:55:1:55:14 | self | local_dataflow.rb:55:1:55:14 | call to foo | self |
| local_dataflow.rb:55:5:55:13 | Array | local_dataflow.rb:55:5:55:13 | call to [] | self |
| local_dataflow.rb:55:5:55:13 | call to [] | local_dataflow.rb:55:1:55:14 | call to foo | position 0 |
| local_dataflow.rb:55:6:55:6 | 1 | local_dataflow.rb:55:5:55:13 | call to [] | position 0 |
| local_dataflow.rb:55:9:55:9 | 2 | local_dataflow.rb:55:5:55:13 | call to [] | position 1 |
| local_dataflow.rb:55:12:55:12 | 3 | local_dataflow.rb:55:5:55:13 | call to [] | position 2 |

View File

@@ -4,4 +4,6 @@ import codeql.ruby.dataflow.internal.DataFlowDispatch
query predicate ret(ReturningNode node) { any() }
query predicate arg(ArgumentNode n, DataFlowCall call, int pos) { n.argumentOf(call, pos) }
query predicate arg(ArgumentNode n, DataFlowCall call, ArgumentPosition pos) {
n.argumentOf(call, pos)
}

View File

@@ -0,0 +1,35 @@
failures
edges
| params_flow.rb:9:16:9:17 | p1 : | params_flow.rb:10:10:10:11 | p1 |
| params_flow.rb:9:20:9:21 | p2 : | params_flow.rb:11:10:11:11 | p2 |
| params_flow.rb:14:12:14:19 | call to taint : | params_flow.rb:9:16:9:17 | p1 : |
| params_flow.rb:14:22:14:29 | call to taint : | params_flow.rb:9:20:9:21 | p2 : |
| params_flow.rb:16:13:16:14 | p1 : | params_flow.rb:17:10:17:11 | p1 |
| params_flow.rb:16:18:16:19 | p2 : | params_flow.rb:18:10:18:11 | p2 |
| params_flow.rb:21:13:21:20 | call to taint : | params_flow.rb:16:13:16:14 | p1 : |
| 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:27:22:34 | call to taint : | params_flow.rb:16:13:16:14 | p1 : |
nodes
| 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:10:10:10:11 | p1 | semmle.label | p1 |
| params_flow.rb:11:10:11:11 | p2 | semmle.label | p2 |
| params_flow.rb:14:12:14:19 | call to taint : | semmle.label | call to taint : |
| params_flow.rb:14:22:14:29 | call to taint : | semmle.label | call to taint : |
| params_flow.rb:16:13:16:14 | p1 : | semmle.label | p1 : |
| params_flow.rb:16:18:16:19 | p2 : | semmle.label | p2 : |
| params_flow.rb:17:10:17:11 | p1 | semmle.label | p1 |
| params_flow.rb:18:10:18:11 | p2 | semmle.label | p2 |
| params_flow.rb:21:13:21:20 | 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:27:22:34 | call to taint : | semmle.label | call to taint : |
subpaths
#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: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: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: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 : |

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,22 @@
def taint x
x
end
def sink x
puts x
end
def positional(p1, p2)
sink p1 # $ hasValueFlow=1
sink p2 # $ hasValueFlow=2
end
positional(taint(1), taint(2))
def keyword(p1:, p2:)
sink p1 # $ hasValueFlow=3 $ hasValueFlow=6
sink p2 # $ hasValueFlow=4 $ hasValueFlow=5
end
keyword(p1: taint(3), p2: taint(4))
keyword(p2: taint(5), p1: taint(6))

View File

@@ -1,5 +1,6 @@
edges
| HardcodedCredentials.rb:12:19:12:64 | "4NQX/CqB5Ae98zFUmwj1DMpF7azsh..." : | HardcodedCredentials.rb:1:23:1:30 | password |
| HardcodedCredentials.rb:15:30:15:75 | "WLC17dLQ9P8YlQvqm77qplOMm5pd1..." : | HardcodedCredentials.rb:1:33:1:36 | cert |
| HardcodedCredentials.rb:18:19:18:72 | ... + ... : | HardcodedCredentials.rb:1:23:1:30 | password |
| HardcodedCredentials.rb:18:27:18:72 | "ogH6qSYWGdbR/2WOGYa7eZ/tObL+G..." : | HardcodedCredentials.rb:18:19:18:72 | ... + ... : |
| HardcodedCredentials.rb:20:11:20:76 | "3jOe7sXKX6Tx52qHWUVqh2t9LNsE+..." : | HardcodedCredentials.rb:23:19:23:20 | pw : |
@@ -10,10 +11,12 @@ edges
| HardcodedCredentials.rb:43:57:43:70 | "abcdef123456" : | HardcodedCredentials.rb:43:46:43:53 | password |
nodes
| HardcodedCredentials.rb:1:23:1:30 | password | semmle.label | password |
| HardcodedCredentials.rb:1:33:1:36 | cert | semmle.label | cert |
| HardcodedCredentials.rb:4:20:4:65 | "xwjVWdfzfRlbcgKkbSfG/xSrUeHYq..." | semmle.label | "xwjVWdfzfRlbcgKkbSfG/xSrUeHYq..." |
| HardcodedCredentials.rb:8:30:8:75 | "X6BLgRWSAtAWG/GaHS+WGGW2K7zZF..." | semmle.label | "X6BLgRWSAtAWG/GaHS+WGGW2K7zZF..." |
| HardcodedCredentials.rb:12:19:12:64 | "4NQX/CqB5Ae98zFUmwj1DMpF7azsh..." : | semmle.label | "4NQX/CqB5Ae98zFUmwj1DMpF7azsh..." : |
| HardcodedCredentials.rb:15:30:15:75 | "WLC17dLQ9P8YlQvqm77qplOMm5pd1..." | semmle.label | "WLC17dLQ9P8YlQvqm77qplOMm5pd1..." |
| HardcodedCredentials.rb:15:30:15:75 | "WLC17dLQ9P8YlQvqm77qplOMm5pd1..." : | semmle.label | "WLC17dLQ9P8YlQvqm77qplOMm5pd1..." : |
| HardcodedCredentials.rb:18:19:18:72 | ... + ... : | semmle.label | ... + ... : |
| HardcodedCredentials.rb:18:27:18:72 | "ogH6qSYWGdbR/2WOGYa7eZ/tObL+G..." : | semmle.label | "ogH6qSYWGdbR/2WOGYa7eZ/tObL+G..." : |
| HardcodedCredentials.rb:20:11:20:76 | "3jOe7sXKX6Tx52qHWUVqh2t9LNsE+..." : | semmle.label | "3jOe7sXKX6Tx52qHWUVqh2t9LNsE+..." : |
@@ -31,6 +34,7 @@ subpaths
| HardcodedCredentials.rb:8:30:8:75 | "X6BLgRWSAtAWG/GaHS+WGGW2K7zZF..." | HardcodedCredentials.rb:8:30:8:75 | "X6BLgRWSAtAWG/GaHS+WGGW2K7zZF..." | HardcodedCredentials.rb:8:30:8:75 | "X6BLgRWSAtAWG/GaHS+WGGW2K7zZF..." | Use of $@. | HardcodedCredentials.rb:8:30:8:75 | "X6BLgRWSAtAWG/GaHS+WGGW2K7zZF..." | hardcoded credentials |
| HardcodedCredentials.rb:12:19:12:64 | "4NQX/CqB5Ae98zFUmwj1DMpF7azsh..." | HardcodedCredentials.rb:12:19:12:64 | "4NQX/CqB5Ae98zFUmwj1DMpF7azsh..." : | HardcodedCredentials.rb:1:23:1:30 | password | Use of $@. | HardcodedCredentials.rb:12:19:12:64 | "4NQX/CqB5Ae98zFUmwj1DMpF7azsh..." | hardcoded credentials |
| HardcodedCredentials.rb:15:30:15:75 | "WLC17dLQ9P8YlQvqm77qplOMm5pd1..." | HardcodedCredentials.rb:15:30:15:75 | "WLC17dLQ9P8YlQvqm77qplOMm5pd1..." | HardcodedCredentials.rb:15:30:15:75 | "WLC17dLQ9P8YlQvqm77qplOMm5pd1..." | Use of $@. | HardcodedCredentials.rb:15:30:15:75 | "WLC17dLQ9P8YlQvqm77qplOMm5pd1..." | hardcoded credentials |
| HardcodedCredentials.rb:15:30:15:75 | "WLC17dLQ9P8YlQvqm77qplOMm5pd1..." | HardcodedCredentials.rb:15:30:15:75 | "WLC17dLQ9P8YlQvqm77qplOMm5pd1..." : | HardcodedCredentials.rb:1:33:1:36 | cert | Use of $@. | HardcodedCredentials.rb:15:30:15:75 | "WLC17dLQ9P8YlQvqm77qplOMm5pd1..." | hardcoded credentials |
| HardcodedCredentials.rb:18:27:18:72 | "ogH6qSYWGdbR/2WOGYa7eZ/tObL+G..." | HardcodedCredentials.rb:18:27:18:72 | "ogH6qSYWGdbR/2WOGYa7eZ/tObL+G..." : | HardcodedCredentials.rb:1:23:1:30 | password | Use of $@. | HardcodedCredentials.rb:18:27:18:72 | "ogH6qSYWGdbR/2WOGYa7eZ/tObL+G..." | hardcoded credentials |
| HardcodedCredentials.rb:20:11:20:76 | "3jOe7sXKX6Tx52qHWUVqh2t9LNsE+..." | HardcodedCredentials.rb:20:11:20:76 | "3jOe7sXKX6Tx52qHWUVqh2t9LNsE+..." : | HardcodedCredentials.rb:1:23:1:30 | password | Use of $@. | HardcodedCredentials.rb:20:11:20:76 | "3jOe7sXKX6Tx52qHWUVqh2t9LNsE+..." | hardcoded credentials |
| HardcodedCredentials.rb:21:12:21:37 | "4fQuzXef4f2yow8KWvIJTA==" | HardcodedCredentials.rb:21:12:21:37 | "4fQuzXef4f2yow8KWvIJTA==" : | HardcodedCredentials.rb:1:23:1:30 | password | Use of $@. | HardcodedCredentials.rb:21:12:21:37 | "4fQuzXef4f2yow8KWvIJTA==" | hardcoded credentials |