Skip to content

Commit 71d703f

Browse files
committed
Ruby: Add ActiveSupport extensions
1 parent cb37a0e commit 71d703f

File tree

3 files changed

+145
-0
lines changed

3 files changed

+145
-0
lines changed

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

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,74 @@ module ActiveSupport {
8383
}
8484
}
8585

86+
/**
87+
* Extensions to the `Hash` class.
88+
*/
89+
module Hash {
90+
private class WithIndifferentAccessSummary extends SimpleSummarizedCallable {
91+
WithIndifferentAccessSummary() { this = "with_indifferent_access" }
92+
93+
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
94+
input = "Argument[self].Element[any]" and
95+
output = "ReturnValue.Element[any]" and
96+
preservesValue = true
97+
}
98+
}
99+
100+
private class TransformSummary extends SimpleSummarizedCallable {
101+
TransformSummary() {
102+
this =
103+
[
104+
"stringify_keys", "to_options", "symbolize_keys", "deep_stringify_keys",
105+
"deep_symbolize_keys", "with_indifferent_access"
106+
]
107+
}
108+
109+
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
110+
input = "Argument[self].Element[any]" and
111+
output = "ReturnValue.Element[?]" and
112+
preservesValue = true
113+
}
114+
}
115+
116+
private string getExtractComponent(MethodCall mc, int i) {
117+
mc.getMethodName() = ["extract!"] and
118+
result = DataFlow::Content::getKnownElementIndex(mc.getArgument(i)).serialize()
119+
}
120+
121+
private class ExtractSummary extends SummarizedCallable {
122+
MethodCall mc;
123+
124+
ExtractSummary() {
125+
mc.getMethodName() = "extract!" and
126+
this =
127+
"extract!(" +
128+
concat(int i, string s | s = getExtractComponent(mc, i) | s, "," order by i) + ")"
129+
}
130+
131+
final override MethodCall getACall() { result = mc }
132+
133+
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
134+
(
135+
exists(string s | s = getExtractComponent(mc, _) |
136+
input = "Argument[self].Element[" + s + "!]" and
137+
output = "ReturnValue.Element[" + s + "!]"
138+
)
139+
or
140+
input =
141+
"Argument[self]" +
142+
concat(int i, string s |
143+
s = getExtractComponent(mc, i)
144+
|
145+
".WithoutElement[" + s + "!]" order by i
146+
) + ".WithElement[any]" and
147+
output = "Argument[self]"
148+
) and
149+
preservesValue = true
150+
}
151+
}
152+
}
153+
86154
/**
87155
* Extensions to the `Enumerable` module.
88156
*/

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import codeql.ruby.AST
66
import TestUtilities.InlineFlowTest
7+
import codeql.ruby.Frameworks
78
import PathGraph
89

910
from DataFlow::PathNode source, DataFlow::PathNode sink, DefaultValueFlowConf conf
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
def m_deep_merge
2+
h1 = { a: source "a" }
3+
h2 = { b: source "b" }
4+
x = h1.deep_merge(h2)
5+
6+
sink x[:a] # $ hasValueFlow=a
7+
sink x[:b] # $ hasValueFlow=b
8+
end
9+
10+
def m_deep_merge!
11+
h1 = { a: source "a" }
12+
h2 = { b: source "b" }
13+
x = h1.deep_merge!(h2)
14+
15+
sink x[:a] # $ hasValueFlow=a
16+
sink x[:b] # $ hasValueFlow=b
17+
18+
sink h1[:a] # $ hasValueFlow=a
19+
sink h1[:b] # $ hasValueFlow=b
20+
21+
sink h2[:a]
22+
sink h2[:b] # $ hasValueFlow=b
23+
end
24+
25+
def m_stringify_keys
26+
h = { a: source "a" }
27+
x = h.stringify_keys
28+
sink x[:a] # $hasValueFlow=a
29+
end
30+
31+
def m_to_options
32+
h = { a: taint "a" }
33+
x = h.to_options
34+
sink x[:a] # $hasTaintFlow=a
35+
end
36+
37+
def m_symbolize_keys
38+
h = { a: taint "a" }
39+
x = h.symbolize_keys
40+
sink x[:a] # $hasTaintFlow=a
41+
end
42+
43+
def m_deep_stringify_keys
44+
h = { a: taint "a" }
45+
x = h.deep_stringify_keys
46+
sink x[:a] # $hasTaintFlow=a
47+
end
48+
49+
def m_deep_symbolize_keys
50+
h = { a: taint "a" }
51+
x = h.deep_symbolize_keys
52+
sink x[:a] # $hasTaintFlow=a
53+
end
54+
55+
def m_with_indifferent_access
56+
h = { a: taint "a" }
57+
x = h.with_indifferent_access
58+
sink x[:a] # $hasTaintFlow=a
59+
end
60+
61+
def m_extract!(x)
62+
h = { a: taint "a", b: taint "b", c: "c", d: taint "d" }
63+
x = h.extract!(:a, x, :b)
64+
65+
sink h[:a]
66+
sink h[:b]
67+
sink h[:c]
68+
sink h[:d] # $ hasValueFlow=d
69+
70+
sink x[:a] # $ hasValueFlow=a
71+
sink x[:b] # $ hasValueFlow=b
72+
sink x[:c]
73+
sink x[:d]
74+
end
75+
76+
m_extract!(:c)

0 commit comments

Comments
 (0)