Skip to content

Commit 3dea1d6

Browse files
committed
Ruby: Add flow summary for Hash#except!
1 parent 0454642 commit 3dea1d6

File tree

3 files changed

+74
-6
lines changed

3 files changed

+74
-6
lines changed

ruby/ql/lib/codeql/ruby/frameworks/core/Hash.qll

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -244,18 +244,20 @@ module Hash {
244244
}
245245

246246
private string getExceptComponent(MethodCall mc, int i) {
247-
mc.getMethodName() = "except" and
247+
mc.getMethodName() = ["except", "except!"] and
248248
result = DataFlow::Content::getKnownElementIndex(mc.getArgument(i)).serialize()
249249
}
250250

251251
private class ExceptSummary extends SummarizedCallable {
252252
MethodCall mc;
253253

254254
ExceptSummary() {
255-
mc.getMethodName() = "except" and
255+
// except! is an ActiveSupport extension
256+
// https://api.rubyonrails.org/classes/Hash.html#method-i-except-21
257+
mc.getMethodName() = ["except", "except!"] and
256258
this =
257-
"except(" + concat(int i, string s | s = getExceptComponent(mc, i) | s, "," order by i) +
258-
")"
259+
mc.getMethodName() + "(" +
260+
concat(int i, string s | s = getExceptComponent(mc, i) | s, "," order by i) + ")"
259261
}
260262

261263
final override MethodCall getACallSimple() { result = mc }
@@ -268,7 +270,11 @@ module Hash {
268270
|
269271
".WithoutElement[" + s + "!]" order by i
270272
) + ".WithElement[any]" and
271-
output = "ReturnValue" and
273+
(
274+
if mc.getMethodName() = "except!"
275+
then output = ["ReturnValue", "Argument[self]"]
276+
else output = "ReturnValue"
277+
) and
272278
preservesValue = true
273279
}
274280
}

ruby/ql/test/library-tests/dataflow/hash-flow/hash-flow.expected

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,19 @@ edges
523523
| hash_flow.rb:750:10:750:13 | hash [element :d] : | hash_flow.rb:750:10:750:17 | ...[...] |
524524
| hash_flow.rb:752:10:752:13 | hash [element :f] : | hash_flow.rb:752:10:752:17 | ...[...] |
525525
| hash_flow.rb:753:10:753:13 | hash [element :g] : | hash_flow.rb:753:10:753:17 | ...[...] |
526+
| hash_flow.rb:761:15:761:25 | call to taint : | hash_flow.rb:767:10:767:13 | hash [element :a] : |
527+
| hash_flow.rb:763:15:763:25 | call to taint : | hash_flow.rb:769:10:769:13 | hash [element :c] : |
528+
| hash_flow.rb:763:15:763:25 | call to taint : | hash_flow.rb:772:9:772:12 | hash [element :c] : |
529+
| hash_flow.rb:764:15:764:25 | call to taint : | hash_flow.rb:770:10:770:13 | hash [element :d] : |
530+
| hash_flow.rb:767:10:767:13 | hash [element :a] : | hash_flow.rb:767:10:767:17 | ...[...] |
531+
| hash_flow.rb:769:10:769:13 | hash [element :c] : | hash_flow.rb:769:10:769:17 | ...[...] |
532+
| hash_flow.rb:770:10:770:13 | hash [element :d] : | hash_flow.rb:770:10:770:17 | ...[...] |
533+
| hash_flow.rb:772:9:772:12 | [post] hash [element :c] : | hash_flow.rb:781:10:781:13 | hash [element :c] : |
534+
| hash_flow.rb:772:9:772:12 | hash [element :c] : | hash_flow.rb:772:9:772:12 | [post] hash [element :c] : |
535+
| hash_flow.rb:772:9:772:12 | hash [element :c] : | hash_flow.rb:772:9:772:31 | call to except! [element :c] : |
536+
| hash_flow.rb:772:9:772:31 | call to except! [element :c] : | hash_flow.rb:776:10:776:10 | x [element :c] : |
537+
| hash_flow.rb:776:10:776:10 | x [element :c] : | hash_flow.rb:776:10:776:14 | ...[...] |
538+
| hash_flow.rb:781:10:781:13 | hash [element :c] : | hash_flow.rb:781:10:781:17 | ...[...] |
526539
nodes
527540
| hash_flow.rb:11:15:11:24 | call to taint : | semmle.label | call to taint : |
528541
| hash_flow.rb:13:12:13:21 | call to taint : | semmle.label | call to taint : |
@@ -1105,6 +1118,22 @@ nodes
11051118
| hash_flow.rb:752:10:752:17 | ...[...] | semmle.label | ...[...] |
11061119
| hash_flow.rb:753:10:753:13 | hash [element :g] : | semmle.label | hash [element :g] : |
11071120
| hash_flow.rb:753:10:753:17 | ...[...] | semmle.label | ...[...] |
1121+
| hash_flow.rb:761:15:761:25 | call to taint : | semmle.label | call to taint : |
1122+
| hash_flow.rb:763:15:763:25 | call to taint : | semmle.label | call to taint : |
1123+
| hash_flow.rb:764:15:764:25 | call to taint : | semmle.label | call to taint : |
1124+
| hash_flow.rb:767:10:767:13 | hash [element :a] : | semmle.label | hash [element :a] : |
1125+
| hash_flow.rb:767:10:767:17 | ...[...] | semmle.label | ...[...] |
1126+
| hash_flow.rb:769:10:769:13 | hash [element :c] : | semmle.label | hash [element :c] : |
1127+
| hash_flow.rb:769:10:769:17 | ...[...] | semmle.label | ...[...] |
1128+
| hash_flow.rb:770:10:770:13 | hash [element :d] : | semmle.label | hash [element :d] : |
1129+
| hash_flow.rb:770:10:770:17 | ...[...] | semmle.label | ...[...] |
1130+
| hash_flow.rb:772:9:772:12 | [post] hash [element :c] : | semmle.label | [post] hash [element :c] : |
1131+
| hash_flow.rb:772:9:772:12 | hash [element :c] : | semmle.label | hash [element :c] : |
1132+
| hash_flow.rb:772:9:772:31 | call to except! [element :c] : | semmle.label | call to except! [element :c] : |
1133+
| hash_flow.rb:776:10:776:10 | x [element :c] : | semmle.label | x [element :c] : |
1134+
| hash_flow.rb:776:10:776:14 | ...[...] | semmle.label | ...[...] |
1135+
| hash_flow.rb:781:10:781:13 | hash [element :c] : | semmle.label | hash [element :c] : |
1136+
| hash_flow.rb:781:10:781:17 | ...[...] | semmle.label | ...[...] |
11081137
subpaths
11091138
#select
11101139
| hash_flow.rb:22:10:22:17 | ...[...] | hash_flow.rb:11:15:11:24 | call to taint : | hash_flow.rb:22:10:22:17 | ...[...] | $@ | hash_flow.rb:11:15:11:24 | call to taint : | call to taint : |
@@ -1279,3 +1308,8 @@ subpaths
12791308
| hash_flow.rb:750:10:750:17 | ...[...] | hash_flow.rb:742:15:742:25 | call to taint : | hash_flow.rb:750:10:750:17 | ...[...] | $@ | hash_flow.rb:742:15:742:25 | call to taint : | call to taint : |
12801309
| hash_flow.rb:752:10:752:17 | ...[...] | hash_flow.rb:744:15:744:25 | call to taint : | hash_flow.rb:752:10:752:17 | ...[...] | $@ | hash_flow.rb:744:15:744:25 | call to taint : | call to taint : |
12811310
| hash_flow.rb:753:10:753:17 | ...[...] | hash_flow.rb:746:29:746:39 | call to taint : | hash_flow.rb:753:10:753:17 | ...[...] | $@ | hash_flow.rb:746:29:746:39 | call to taint : | call to taint : |
1311+
| hash_flow.rb:767:10:767:17 | ...[...] | hash_flow.rb:761:15:761:25 | call to taint : | hash_flow.rb:767:10:767:17 | ...[...] | $@ | hash_flow.rb:761:15:761:25 | call to taint : | call to taint : |
1312+
| hash_flow.rb:769:10:769:17 | ...[...] | hash_flow.rb:763:15:763:25 | call to taint : | hash_flow.rb:769:10:769:17 | ...[...] | $@ | hash_flow.rb:763:15:763:25 | call to taint : | call to taint : |
1313+
| hash_flow.rb:770:10:770:17 | ...[...] | hash_flow.rb:764:15:764:25 | call to taint : | hash_flow.rb:770:10:770:17 | ...[...] | $@ | hash_flow.rb:764:15:764:25 | call to taint : | call to taint : |
1314+
| hash_flow.rb:776:10:776:14 | ...[...] | hash_flow.rb:763:15:763:25 | call to taint : | hash_flow.rb:776:10:776:14 | ...[...] | $@ | hash_flow.rb:763:15:763:25 | call to taint : | call to taint : |
1315+
| hash_flow.rb:781:10:781:17 | ...[...] | hash_flow.rb:763:15:763:25 | call to taint : | hash_flow.rb:781:10:781:17 | ...[...] | $@ | hash_flow.rb:763:15:763:25 | call to taint : | call to taint : |

ruby/ql/test/library-tests/dataflow/hash-flow/hash_flow.rb

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -754,4 +754,32 @@ def m45()
754754
sink(hash[:h])
755755
end
756756

757-
m45()
757+
m45()
758+
759+
def m46(x)
760+
hash = {
761+
:a => taint(46.1),
762+
:b => 1,
763+
:c => taint(46.2),
764+
:d => taint(46.3)
765+
}
766+
767+
sink(hash[:a]) # $ hasValueFlow=46.1
768+
sink(hash[:b])
769+
sink(hash[:c]) # $ hasValueFlow=46.2
770+
sink(hash[:d]) # $ hasValueFlow=46.3
771+
772+
x = hash.except!(:a, x, :d)
773+
774+
sink(x[:a])
775+
sink(x[:b])
776+
sink(x[:c]) # $ hasValueFlow=46.2
777+
sink(x[:d])
778+
779+
sink(hash[:a])
780+
sink(hash[:b])
781+
sink(hash[:c]) # $ hasValueFlow=46.2
782+
sink(hash[:d])
783+
end
784+
785+
m46(:c)

0 commit comments

Comments
 (0)