Skip to content

Commit 5e350a0

Browse files
committed
Ruby: Derive edge labels from {Argument,Parameter}Position
1 parent 040e566 commit 5e350a0

File tree

2 files changed

+43
-29
lines changed

2 files changed

+43
-29
lines changed

ruby/ql/lib/codeql/ruby/ApiGraphs.qll

Lines changed: 38 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import codeql.ruby.DataFlow
1111
import codeql.ruby.typetracking.TypeTracker
1212
import codeql.ruby.ast.internal.Module
1313
private import codeql.ruby.controlflow.CfgNodes
14+
private import codeql.ruby.dataflow.internal.DataFlowPrivate as DataFlowPrivate
15+
private import codeql.ruby.dataflow.internal.DataFlowDispatch as DataFlowDispatch
1416

1517
/**
1618
* Provides classes and predicates for working with APIs used in a database.
@@ -423,49 +425,56 @@ module API {
423425
/** Gets a data flow node that flows to the RHS of a def-node. */
424426
private DataFlow::LocalSourceNode defCand() { result = defCand(TypeBackTracker::end()) }
425427

426-
/**
427-
* Holds if there should be a `lbl`-edge from the given call to an argument.
428-
*/
429-
pragma[nomagic]
430-
private predicate argumentStep(string lbl, DataFlow::CallNode call, DataFlow::Node argument) {
428+
private string getLabelFromArgumentPosition(DataFlowDispatch::ArgumentPosition pos) {
429+
exists(int n |
430+
pos.isPositional(n) and
431+
result = Label::parameter(n)
432+
)
433+
or
434+
exists(string name |
435+
pos.isKeyword(name) and
436+
result = Label::keywordParameter(name)
437+
)
438+
or
439+
pos.isBlock() and
440+
result = Label::blockParameter()
441+
}
442+
443+
private string getLabelFromParameterPosition(DataFlowDispatch::ParameterPosition pos) {
431444
exists(int n |
432-
argument = call.getArgument(n) and
433-
lbl = Label::parameter(n)
445+
pos.isPositional(n) and
446+
result = Label::parameter(n)
434447
)
435448
or
436449
exists(string name |
437-
argument = call.getKeywordArgument(name) and
438-
lbl = Label::keywordParameter(name)
450+
pos.isKeyword(name) and
451+
result = Label::keywordParameter(name)
439452
)
440453
or
441-
argument = call.getBlock() and
442-
lbl = Label::blockParameter()
454+
pos.isBlock() and
455+
result = Label::blockParameter()
443456
}
444457

445458
/**
446-
* Holds if there should be a `lbl`-edge from the given callable to a parameter.
459+
* Holds if there should be a `lbl`-edge from the given call to an argument.
447460
*/
448461
pragma[nomagic]
449-
private predicate parameterStep(string lbl, DataFlow::Node callable, DataFlow::Node paramNode) {
450-
exists(Parameter param |
451-
paramNode.asParameter() = param and
452-
callable.asExpr().getExpr().(Callable).getAParameter() = param and
453-
lbl = getLabelFromParameter(param)
462+
private predicate argumentStep(string lbl, DataFlow::CallNode call, DataFlowPrivate::ArgumentNode argument) {
463+
exists(DataFlowDispatch::ArgumentPosition argPos |
464+
argument.sourceArgumentOf(call.asExpr(), argPos) and
465+
lbl = getLabelFromArgumentPosition(argPos)
454466
)
455467
}
456468

457-
private string getLabelFromParameter(Parameter param) {
458-
result = Label::keywordParameter(param.(KeywordParameter).getName())
459-
or
460-
param instanceof BlockParameter and
461-
result = Label::blockParameter()
462-
or
463-
(
464-
param instanceof SimpleParameter
465-
or
466-
param instanceof OptionalParameter
467-
) and
468-
result = Label::parameter(param.getPosition())
469+
/**
470+
* Holds if there should be a `lbl`-edge from the given callable to a parameter.
471+
*/
472+
pragma[nomagic]
473+
private predicate parameterStep(string lbl, DataFlow::Node callable, DataFlowPrivate::ParameterNodeImpl paramNode) {
474+
exists(DataFlowDispatch::ParameterPosition paramPos |
475+
paramNode.isSourceParameterOf(callable.asExpr().getExpr(), paramPos) and
476+
lbl = getLabelFromParameterPosition(paramPos)
477+
)
469478
}
470479

471480
/**

ruby/ql/test/library-tests/dataflow/api-graphs/callbacks.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,8 @@ def getCallback()
3232
}
3333
end
3434
Something.indirectCallback(getCallback()) #$ use=getMember("Something").getMethod("indirectCallback").getReturn()
35+
36+
Something.withMixed do |a, *args, b| #$ use=getMember("Something").getMethod("withMixed").getReturn()
37+
a.something #$ use=getMember("Something").getMethod("withMixed").getBlock().getParameter(0).getMethod("something").getReturn()
38+
b.something #$ use=getMember("Something").getMethod("withMixed").getBlock().getParameter(1).getMethod("something").getReturn()
39+
end

0 commit comments

Comments
 (0)