Skip to content

Commit 207ba86

Browse files
committed
Ruby: add flow summary for Enumerable#pick
1 parent 4e88b84 commit 207ba86

File tree

3 files changed

+145
-1
lines changed

3 files changed

+145
-1
lines changed

ruby/ql/lib/codeql/ruby/frameworks/ActiveSupport.qll

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,65 @@ module ActiveSupport {
308308
preservesValue = true
309309
}
310310
}
311-
// TODO: pick, pluck (they require Hash dataflow)
311+
312+
private string getKeyArgument(MethodCall mc, int i) {
313+
mc.getMethodName() = ["pick", "pluck"] and
314+
result = DataFlow::Content::getKnownElementIndex(mc.getArgument(i)).serialize()
315+
}
316+
317+
private class PickSingleSummary extends SummarizedCallable {
318+
private MethodCall mc;
319+
private string key;
320+
321+
PickSingleSummary() {
322+
key = getKeyArgument(mc, 0) and
323+
this = "Enumerable.pick(" + key + ")" and
324+
mc.getMethodName() = "pick" and
325+
mc.getNumberOfArguments() = 1
326+
}
327+
328+
override MethodCall getACall() { result = mc }
329+
330+
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
331+
input = "Argument[self].Element[0].Element[" + key + "!]" and
332+
output = "ReturnValue" and
333+
preservesValue = true
334+
}
335+
}
336+
337+
private class PickMultipleSummary extends SummarizedCallable {
338+
private MethodCall mc;
339+
340+
PickMultipleSummary() {
341+
mc.getMethodName() = "pick" and
342+
mc.getNumberOfArguments() > 1 and
343+
exists(int maxKey |
344+
maxKey = max(int j | exists(getKeyArgument(mc, j))) and
345+
this =
346+
"Enumerable.pick(" +
347+
concat(int i, string key |
348+
key = getKeyArgument(mc, i)
349+
or
350+
key = "_" and
351+
not exists(getKeyArgument(mc, i)) and
352+
i in [0 .. maxKey]
353+
|
354+
key, "," order by i
355+
) + ")"
356+
)
357+
}
358+
359+
override MethodCall getACall() { result = mc }
360+
361+
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
362+
exists(string s, int i |
363+
s = getKeyArgument(mc, i) and
364+
input = "Argument[self].Element[0].Element[" + s + "!]" and
365+
output = "ReturnValue.Element[" + i + "!]"
366+
) and
367+
preservesValue = true
368+
}
369+
}
312370
}
313371
}
314372

ruby/ql/test/library-tests/frameworks/active_support/ActiveSupportDataFlow.expected

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,38 @@ edges
320320
| hash_extensions.rb:91:10:91:10 | j [element] : | hash_extensions.rb:91:10:91:16 | ...[...] |
321321
| hash_extensions.rb:92:10:92:10 | j [element] : | hash_extensions.rb:92:10:92:16 | ...[...] |
322322
| hash_extensions.rb:92:10:92:10 | j [element] : | hash_extensions.rb:92:10:92:16 | ...[...] |
323+
| hash_extensions.rb:98:21:98:31 | call to source : | hash_extensions.rb:99:10:99:15 | values [element 0, element :id] : |
324+
| hash_extensions.rb:98:21:98:31 | call to source : | hash_extensions.rb:99:10:99:15 | values [element 0, element :id] : |
325+
| hash_extensions.rb:98:21:98:31 | call to source : | hash_extensions.rb:101:10:101:15 | values [element 0, element :id] : |
326+
| hash_extensions.rb:98:21:98:31 | call to source : | hash_extensions.rb:101:10:101:15 | values [element 0, element :id] : |
327+
| hash_extensions.rb:98:21:98:31 | call to source : | hash_extensions.rb:104:10:104:15 | values [element 0, element :id] : |
328+
| hash_extensions.rb:98:21:98:31 | call to source : | hash_extensions.rb:104:10:104:15 | values [element 0, element :id] : |
329+
| hash_extensions.rb:98:40:98:54 | call to source : | hash_extensions.rb:100:10:100:15 | values [element 0, element :name] : |
330+
| hash_extensions.rb:98:40:98:54 | call to source : | hash_extensions.rb:100:10:100:15 | values [element 0, element :name] : |
331+
| hash_extensions.rb:98:40:98:54 | call to source : | hash_extensions.rb:102:10:102:15 | values [element 0, element :name] : |
332+
| hash_extensions.rb:98:40:98:54 | call to source : | hash_extensions.rb:102:10:102:15 | values [element 0, element :name] : |
333+
| hash_extensions.rb:98:40:98:54 | call to source : | hash_extensions.rb:103:10:103:15 | values [element 0, element :name] : |
334+
| hash_extensions.rb:98:40:98:54 | call to source : | hash_extensions.rb:103:10:103:15 | values [element 0, element :name] : |
335+
| hash_extensions.rb:99:10:99:15 | values [element 0, element :id] : | hash_extensions.rb:99:10:99:25 | call to pick |
336+
| hash_extensions.rb:99:10:99:15 | values [element 0, element :id] : | hash_extensions.rb:99:10:99:25 | call to pick |
337+
| hash_extensions.rb:100:10:100:15 | values [element 0, element :name] : | hash_extensions.rb:100:10:100:27 | call to pick |
338+
| hash_extensions.rb:100:10:100:15 | values [element 0, element :name] : | hash_extensions.rb:100:10:100:27 | call to pick |
339+
| hash_extensions.rb:101:10:101:15 | values [element 0, element :id] : | hash_extensions.rb:101:10:101:32 | call to pick [element 0] : |
340+
| hash_extensions.rb:101:10:101:15 | values [element 0, element :id] : | hash_extensions.rb:101:10:101:32 | call to pick [element 0] : |
341+
| hash_extensions.rb:101:10:101:32 | call to pick [element 0] : | hash_extensions.rb:101:10:101:35 | ...[...] |
342+
| hash_extensions.rb:101:10:101:32 | call to pick [element 0] : | hash_extensions.rb:101:10:101:35 | ...[...] |
343+
| hash_extensions.rb:102:10:102:15 | values [element 0, element :name] : | hash_extensions.rb:102:10:102:32 | call to pick [element 1] : |
344+
| hash_extensions.rb:102:10:102:15 | values [element 0, element :name] : | hash_extensions.rb:102:10:102:32 | call to pick [element 1] : |
345+
| hash_extensions.rb:102:10:102:32 | call to pick [element 1] : | hash_extensions.rb:102:10:102:35 | ...[...] |
346+
| hash_extensions.rb:102:10:102:32 | call to pick [element 1] : | hash_extensions.rb:102:10:102:35 | ...[...] |
347+
| hash_extensions.rb:103:10:103:15 | values [element 0, element :name] : | hash_extensions.rb:103:10:103:32 | call to pick [element 0] : |
348+
| hash_extensions.rb:103:10:103:15 | values [element 0, element :name] : | hash_extensions.rb:103:10:103:32 | call to pick [element 0] : |
349+
| hash_extensions.rb:103:10:103:32 | call to pick [element 0] : | hash_extensions.rb:103:10:103:35 | ...[...] |
350+
| hash_extensions.rb:103:10:103:32 | call to pick [element 0] : | hash_extensions.rb:103:10:103:35 | ...[...] |
351+
| hash_extensions.rb:104:10:104:15 | values [element 0, element :id] : | hash_extensions.rb:104:10:104:32 | call to pick [element 1] : |
352+
| hash_extensions.rb:104:10:104:15 | values [element 0, element :id] : | hash_extensions.rb:104:10:104:32 | call to pick [element 1] : |
353+
| hash_extensions.rb:104:10:104:32 | call to pick [element 1] : | hash_extensions.rb:104:10:104:35 | ...[...] |
354+
| hash_extensions.rb:104:10:104:32 | call to pick [element 1] : | hash_extensions.rb:104:10:104:35 | ...[...] |
323355
nodes
324356
| active_support.rb:10:9:10:18 | call to source : | semmle.label | call to source : |
325357
| active_support.rb:11:10:11:10 | x : | semmle.label | x : |
@@ -722,6 +754,42 @@ nodes
722754
| hash_extensions.rb:92:10:92:10 | j [element] : | semmle.label | j [element] : |
723755
| hash_extensions.rb:92:10:92:16 | ...[...] | semmle.label | ...[...] |
724756
| hash_extensions.rb:92:10:92:16 | ...[...] | semmle.label | ...[...] |
757+
| hash_extensions.rb:98:21:98:31 | call to source : | semmle.label | call to source : |
758+
| hash_extensions.rb:98:21:98:31 | call to source : | semmle.label | call to source : |
759+
| hash_extensions.rb:98:40:98:54 | call to source : | semmle.label | call to source : |
760+
| hash_extensions.rb:98:40:98:54 | call to source : | semmle.label | call to source : |
761+
| hash_extensions.rb:99:10:99:15 | values [element 0, element :id] : | semmle.label | values [element 0, element :id] : |
762+
| hash_extensions.rb:99:10:99:15 | values [element 0, element :id] : | semmle.label | values [element 0, element :id] : |
763+
| hash_extensions.rb:99:10:99:25 | call to pick | semmle.label | call to pick |
764+
| hash_extensions.rb:99:10:99:25 | call to pick | semmle.label | call to pick |
765+
| hash_extensions.rb:100:10:100:15 | values [element 0, element :name] : | semmle.label | values [element 0, element :name] : |
766+
| hash_extensions.rb:100:10:100:15 | values [element 0, element :name] : | semmle.label | values [element 0, element :name] : |
767+
| hash_extensions.rb:100:10:100:27 | call to pick | semmle.label | call to pick |
768+
| hash_extensions.rb:100:10:100:27 | call to pick | semmle.label | call to pick |
769+
| hash_extensions.rb:101:10:101:15 | values [element 0, element :id] : | semmle.label | values [element 0, element :id] : |
770+
| hash_extensions.rb:101:10:101:15 | values [element 0, element :id] : | semmle.label | values [element 0, element :id] : |
771+
| hash_extensions.rb:101:10:101:32 | call to pick [element 0] : | semmle.label | call to pick [element 0] : |
772+
| hash_extensions.rb:101:10:101:32 | call to pick [element 0] : | semmle.label | call to pick [element 0] : |
773+
| hash_extensions.rb:101:10:101:35 | ...[...] | semmle.label | ...[...] |
774+
| hash_extensions.rb:101:10:101:35 | ...[...] | semmle.label | ...[...] |
775+
| hash_extensions.rb:102:10:102:15 | values [element 0, element :name] : | semmle.label | values [element 0, element :name] : |
776+
| hash_extensions.rb:102:10:102:15 | values [element 0, element :name] : | semmle.label | values [element 0, element :name] : |
777+
| hash_extensions.rb:102:10:102:32 | call to pick [element 1] : | semmle.label | call to pick [element 1] : |
778+
| hash_extensions.rb:102:10:102:32 | call to pick [element 1] : | semmle.label | call to pick [element 1] : |
779+
| hash_extensions.rb:102:10:102:35 | ...[...] | semmle.label | ...[...] |
780+
| hash_extensions.rb:102:10:102:35 | ...[...] | semmle.label | ...[...] |
781+
| hash_extensions.rb:103:10:103:15 | values [element 0, element :name] : | semmle.label | values [element 0, element :name] : |
782+
| hash_extensions.rb:103:10:103:15 | values [element 0, element :name] : | semmle.label | values [element 0, element :name] : |
783+
| hash_extensions.rb:103:10:103:32 | call to pick [element 0] : | semmle.label | call to pick [element 0] : |
784+
| hash_extensions.rb:103:10:103:32 | call to pick [element 0] : | semmle.label | call to pick [element 0] : |
785+
| hash_extensions.rb:103:10:103:35 | ...[...] | semmle.label | ...[...] |
786+
| hash_extensions.rb:103:10:103:35 | ...[...] | semmle.label | ...[...] |
787+
| hash_extensions.rb:104:10:104:15 | values [element 0, element :id] : | semmle.label | values [element 0, element :id] : |
788+
| hash_extensions.rb:104:10:104:15 | values [element 0, element :id] : | semmle.label | values [element 0, element :id] : |
789+
| hash_extensions.rb:104:10:104:32 | call to pick [element 1] : | semmle.label | call to pick [element 1] : |
790+
| hash_extensions.rb:104:10:104:32 | call to pick [element 1] : | semmle.label | call to pick [element 1] : |
791+
| hash_extensions.rb:104:10:104:35 | ...[...] | semmle.label | ...[...] |
792+
| hash_extensions.rb:104:10:104:35 | ...[...] | semmle.label | ...[...] |
725793
subpaths
726794
#select
727795
| active_support.rb:182:10:182:13 | ...[...] | active_support.rb:180:10:180:17 | call to source : | active_support.rb:182:10:182:13 | ...[...] | $@ | active_support.rb:180:10:180:17 | call to source : | call to source : |
@@ -766,3 +834,9 @@ subpaths
766834
| hash_extensions.rb:87:10:87:16 | ...[...] | hash_extensions.rb:83:9:83:19 | call to source : | hash_extensions.rb:87:10:87:16 | ...[...] | $@ | hash_extensions.rb:83:9:83:19 | call to source : | call to source : |
767835
| hash_extensions.rb:91:10:91:16 | ...[...] | hash_extensions.rb:89:27:89:37 | call to source : | hash_extensions.rb:91:10:91:16 | ...[...] | $@ | hash_extensions.rb:89:27:89:37 | call to source : | call to source : |
768836
| hash_extensions.rb:92:10:92:16 | ...[...] | hash_extensions.rb:89:27:89:37 | call to source : | hash_extensions.rb:92:10:92:16 | ...[...] | $@ | hash_extensions.rb:89:27:89:37 | call to source : | call to source : |
837+
| hash_extensions.rb:99:10:99:25 | call to pick | hash_extensions.rb:98:21:98:31 | call to source : | hash_extensions.rb:99:10:99:25 | call to pick | $@ | hash_extensions.rb:98:21:98:31 | call to source : | call to source : |
838+
| hash_extensions.rb:100:10:100:27 | call to pick | hash_extensions.rb:98:40:98:54 | call to source : | hash_extensions.rb:100:10:100:27 | call to pick | $@ | hash_extensions.rb:98:40:98:54 | call to source : | call to source : |
839+
| hash_extensions.rb:101:10:101:35 | ...[...] | hash_extensions.rb:98:21:98:31 | call to source : | hash_extensions.rb:101:10:101:35 | ...[...] | $@ | hash_extensions.rb:98:21:98:31 | call to source : | call to source : |
840+
| hash_extensions.rb:102:10:102:35 | ...[...] | hash_extensions.rb:98:40:98:54 | call to source : | hash_extensions.rb:102:10:102:35 | ...[...] | $@ | hash_extensions.rb:98:40:98:54 | call to source : | call to source : |
841+
| hash_extensions.rb:103:10:103:35 | ...[...] | hash_extensions.rb:98:40:98:54 | call to source : | hash_extensions.rb:103:10:103:35 | ...[...] | $@ | hash_extensions.rb:98:40:98:54 | call to source : | call to source : |
842+
| hash_extensions.rb:104:10:104:35 | ...[...] | hash_extensions.rb:98:21:98:31 | call to source : | hash_extensions.rb:104:10:104:35 | ...[...] | $@ | hash_extensions.rb:98:21:98:31 | call to source : | call to source : |

ruby/ql/test/library-tests/frameworks/active_support/hash_extensions.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,15 @@ def m_index_with
9393
end
9494

9595
m_index_with()
96+
97+
def m_pick
98+
values = [{ id: source("1"), name: source("David") }, { id: source("2"), name: source("Rafael") }]
99+
sink(values.pick(:id)) # $ hasValueFlow=1
100+
sink(values.pick(:name)) # $ hasValueFlow=David
101+
sink(values.pick(:id, :name)[0]) # $ hasValueFlow=1
102+
sink(values.pick(:id, :name)[1]) # $ hasValueFlow=David
103+
sink(values.pick(:name, :id)[0]) # $ hasValueFlow=David
104+
sink(values.pick(:name, :id)[1]) # $ hasValueFlow=1
105+
end
106+
107+
m_pick()

0 commit comments

Comments
 (0)