Skip to content

Commit 6d84baf

Browse files
committed
Ruby: Support self,block in Argument/Parameter tokens
1 parent 95122b2 commit 6d84baf

File tree

4 files changed

+71
-43
lines changed

4 files changed

+71
-43
lines changed

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

Lines changed: 34 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -473,36 +473,6 @@ module API {
473473
/** Gets a data flow node that flows to the RHS of a def-node. */
474474
private DataFlow::LocalSourceNode defCand() { result = defCand(TypeBackTracker::end()) }
475475

476-
private Label::ApiLabel getLabelFromArgumentPosition(DataFlowDispatch::ArgumentPosition pos) {
477-
exists(int n |
478-
pos.isPositional(n) and
479-
result = Label::parameter(n)
480-
)
481-
or
482-
exists(string name |
483-
pos.isKeyword(name) and
484-
result = Label::keywordParameter(name)
485-
)
486-
or
487-
pos.isBlock() and
488-
result = Label::blockParameter()
489-
}
490-
491-
private Label::ApiLabel getLabelFromParameterPosition(DataFlowDispatch::ParameterPosition pos) {
492-
exists(int n |
493-
pos.isPositional(n) and
494-
result = Label::parameter(n)
495-
)
496-
or
497-
exists(string name |
498-
pos.isKeyword(name) and
499-
result = Label::keywordParameter(name)
500-
)
501-
or
502-
pos.isBlock() and
503-
result = Label::blockParameter()
504-
}
505-
506476
/**
507477
* Holds if there should be a `lbl`-edge from the given call to an argument.
508478
*/
@@ -512,7 +482,7 @@ module API {
512482
) {
513483
exists(DataFlowDispatch::ArgumentPosition argPos |
514484
argument.sourceArgumentOf(call.asExpr(), argPos) and
515-
lbl = getLabelFromArgumentPosition(argPos)
485+
lbl = Label::getLabelFromArgumentPosition(argPos)
516486
)
517487
}
518488

@@ -525,7 +495,7 @@ module API {
525495
) {
526496
exists(DataFlowDispatch::ParameterPosition paramPos |
527497
paramNode.isSourceParameterOf(callable.asExpr().getExpr(), paramPos) and
528-
lbl = getLabelFromParameterPosition(paramPos)
498+
lbl = Label::getLabelFromParameterPosition(paramPos)
529499
)
530500
}
531501

@@ -803,5 +773,37 @@ module API {
803773

804774
/** Gets the label for the edge from the root node to a custom entry point of the given name. */
805775
LabelEntryPoint entryPoint(API::EntryPoint name) { result.getName() = name }
776+
777+
/** Gets the API graph label corresponding to the given argument position. */
778+
Label::ApiLabel getLabelFromArgumentPosition(DataFlowDispatch::ArgumentPosition pos) {
779+
exists(int n |
780+
pos.isPositional(n) and
781+
result = Label::parameter(n)
782+
)
783+
or
784+
exists(string name |
785+
pos.isKeyword(name) and
786+
result = Label::keywordParameter(name)
787+
)
788+
or
789+
pos.isBlock() and
790+
result = Label::blockParameter()
791+
}
792+
793+
/** Gets the API graph label corresponding to the given parameter position. */
794+
Label::ApiLabel getLabelFromParameterPosition(DataFlowDispatch::ParameterPosition pos) {
795+
exists(int n |
796+
pos.isPositional(n) and
797+
result = Label::parameter(n)
798+
)
799+
or
800+
exists(string name |
801+
pos.isKeyword(name) and
802+
result = Label::keywordParameter(name)
803+
)
804+
or
805+
pos.isBlock() and
806+
result = Label::blockParameter()
807+
}
806808
}
807809
}

ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImplSpecific.qll

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,15 +55,14 @@ predicate summaryElement(DataFlowCallable c, string input, string output, string
5555
/**
5656
* Gets the summary component for specification component `c`, if any.
5757
*
58-
* This covers all the Ruby-specific components of a flow summary, and
59-
* is currently restricted to `"BlockArgument"`.
58+
* This covers all the Ruby-specific components of a flow summary.
6059
*/
6160
bindingset[c]
6261
SummaryComponent interpretComponentSpecific(AccessPathToken c) {
63-
c = "Receiver" and
62+
c = "Receiver" and // TODO: replace with Argument[self]
6463
result = FlowSummary::SummaryComponent::receiver()
6564
or
66-
c = "BlockArgument" and
65+
c = "BlockArgument" and // TODO: replace with Argument[block]
6766
result = FlowSummary::SummaryComponent::block()
6867
or
6968
c = "Argument[_]" and
@@ -85,7 +84,7 @@ SummaryComponent interpretComponentSpecific(AccessPathToken c) {
8584
/** Gets the textual representation of a summary component in the format used for flow summaries. */
8685
string getComponentSpecificCsv(SummaryComponent sc) {
8786
sc = TArgumentSummaryComponent(any(ParameterPosition pos | pos.isBlock())) and
88-
result = "BlockArgument"
87+
result = "BlockArgument" // TODO: replace with Argument[block]
8988
}
9089

9190
/** Gets the textual representation of a parameter position in the format used for flow summaries. */
@@ -184,6 +183,12 @@ ArgumentPosition parseParamBody(string s) {
184183
ParsePositions::isParsedParameterPosition(s, i) and
185184
result.isPositional(i)
186185
)
186+
or
187+
s = "self" and
188+
result.isSelf()
189+
or
190+
s = "block" and
191+
result.isBlock()
187192
}
188193

189194
/** Gets the parameter position obtained by parsing `X` in `Argument[X]`. */
@@ -192,4 +197,10 @@ ParameterPosition parseArgBody(string s) {
192197
ParsePositions::isParsedArgumentPosition(s, i) and
193198
result.isPositional(i)
194199
)
200+
or
201+
s = "self" and
202+
result.isSelf()
203+
or
204+
s = "block" and
205+
result.isBlock()
195206
}

ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModelsSpecific.qll

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ class Unit = DataFlowPrivate::Unit;
2626
import codeql.ruby.ApiGraphs
2727
import codeql.ruby.dataflow.internal.AccessPathSyntax as AccessPathSyntax
2828
private import AccessPathSyntax
29+
private import codeql.ruby.dataflow.internal.FlowSummaryImplSpecific as FlowSummaryImplSpecific
30+
private import codeql.ruby.dataflow.internal.DataFlowDispatch as DataFlowDispatch
2931

3032
/**
3133
* Holds if models describing `package` may be relevant for the analysis of this database.
@@ -107,8 +109,13 @@ API::Node getExtraSuccessorFromNode(API::Node node, AccessPathToken token) {
107109
token.getName() = "Instance" and
108110
result = node.getInstance()
109111
or
110-
token.getName() = "BlockArgument" and
112+
token.getName() = "BlockArgument" and // TODO: replace with Argument[block]
111113
result = node.getBlock()
114+
or
115+
token.getName() = "Parameter" and
116+
result =
117+
node.getASuccessor(API::Label::getLabelFromArgumentPosition(FlowSummaryImplSpecific::parseParamBody(token
118+
.getAnArgument())))
112119
// Note: The "ArrayElement" token is not implemented yet, as it ultimately requires type-tracking and
113120
// API graphs to be aware of the steps involving ArrayElement contributed by the standard library model.
114121
// Type-tracking cannot summarize function calls on its own, so it doesn't benefit from synthesized callables.
@@ -118,7 +125,12 @@ API::Node getExtraSuccessorFromNode(API::Node node, AccessPathToken token) {
118125
* Gets a Ruby-specific API graph successor of `node` reachable by resolving `token`.
119126
*/
120127
bindingset[token]
121-
API::Node getExtraSuccessorFromInvoke(InvokeNode node, AccessPathToken token) { none() }
128+
API::Node getExtraSuccessorFromInvoke(InvokeNode node, AccessPathToken token) {
129+
token.getName() = "Argument" and
130+
result =
131+
node.getASuccessor(API::Label::getLabelFromParameterPosition(FlowSummaryImplSpecific::parseArgBody(token
132+
.getAnArgument())))
133+
}
122134

123135
/**
124136
* Holds if `invoke` matches the Ruby-specific call site filter in `token`.
@@ -165,4 +177,7 @@ bindingset[name, argument]
165177
predicate isExtraValidTokenArgumentInIdentifyingAccessPath(string name, string argument) {
166178
name = ["Member", "Method"] and
167179
exists(argument)
180+
or
181+
name = ["Argument", "Parameter"] and
182+
argument = ["self", "block"]
168183
}

ruby/ql/test/library-tests/dataflow/summaries/Summaries.ql

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@ private class SummarizedCallableApplyBlock extends SummarizedCallable {
4242

4343
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
4444
input = "Argument[0]" and
45-
output = "BlockArgument.Parameter[0]" and
45+
output = "Argument[block].Parameter[0]" and
4646
preservesValue = true
4747
or
48-
input = "BlockArgument.ReturnValue" and
48+
input = "Argument[block].ReturnValue" and
4949
output = "ReturnValue" and
5050
preservesValue = true
5151
}
@@ -75,9 +75,9 @@ private class StepsFromModel extends ModelInput::SummaryModelCsv {
7575
";;Member[Foo].Method[secondArg];Argument[1];ReturnValue;taint",
7676
";;Member[Foo].Method[onlyWithoutBlock].WithoutBlock;Argument[0];ReturnValue;taint",
7777
";;Member[Foo].Method[onlyWithBlock].WithBlock;Argument[0];ReturnValue;taint",
78-
";;Member[Foo].Method[blockArg].BlockArgument.Parameter[0].Method[preserveTaint];Argument[0];ReturnValue;taint",
78+
";;Member[Foo].Method[blockArg].Argument[block].Parameter[0].Method[preserveTaint];Argument[0];ReturnValue;taint",
7979
";any;Method[matchedByName];Argument[0];ReturnValue;taint",
80-
";any;Method[matchedByNameRcv];Receiver;ReturnValue;taint",
80+
";any;Method[matchedByNameRcv];Argument[self];ReturnValue;taint",
8181
]
8282
}
8383
}

0 commit comments

Comments
 (0)