Skip to content

Commit c0e600c

Browse files
authored
Merge pull request #12672 from hvitved/ruby/implicit-array-reads-at-sinks
Ruby: Allow for implicit array reads at all sinks during taint tracking
2 parents 61bfc4e + e258324 commit c0e600c

File tree

23 files changed

+1452
-1396
lines changed

23 files changed

+1452
-1396
lines changed

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ predicate defaultTaintSanitizer(DataFlow::Node node) { none() }
1717
* of `c` at sinks and inputs to additional taint steps.
1818
*/
1919
bindingset[node]
20-
predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::ContentSet c) { none() }
20+
predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::ContentSet c) {
21+
exists(node) and
22+
c.isElementOfTypeOrUnknown("int")
23+
}
2124

2225
private CfgNodes::ExprNodes::VariableWriteAccessCfgNode variablesInPattern(
2326
CfgNodes::ExprNodes::CasePatternCfgNode p

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

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -201,11 +201,8 @@ private predicate sqlFragmentArgumentInner(DataFlow::CallNode call, DataFlow::No
201201
}
202202

203203
private predicate sqlFragmentArgument(DataFlow::CallNode call, DataFlow::Node sink) {
204-
exists(DataFlow::Node arg |
205-
sqlFragmentArgumentInner(call, arg) and
206-
sink = [arg, arg.(DataFlow::ArrayLiteralNode).getElement(0)] and
207-
unsafeSqlExpr(sink.asExpr().getExpr())
208-
)
204+
sqlFragmentArgumentInner(call, sink) and
205+
unsafeSqlExpr(sink.asExpr().getExpr())
209206
}
210207

211208
// An expression that, if tainted by unsanitized input, should not be used as

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

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -210,9 +210,28 @@ module Array {
210210
}
211211
}
212212

213+
private predicate isKnownRange(RangeLiteral rl, int start, int end) {
214+
(
215+
// Either an explicit, positive beginning index...
216+
start = rl.getBegin().getConstantValue().getInt() and start >= 0
217+
or
218+
// Or a begin-less one, since `..n` is equivalent to `0..n`
219+
not exists(rl.getBegin()) and start = 0
220+
) and
221+
// There must be an explicit end. An end-less range like `2..` is not
222+
// treated as a known range, since we don't track the length of the array.
223+
exists(int e | e = rl.getEnd().getConstantValue().getInt() and e >= 0 |
224+
rl.isInclusive() and end = e
225+
or
226+
rl.isExclusive() and end = e - 1
227+
)
228+
}
229+
213230
/**
214231
* A call to `[]` with an unknown argument, which could be either an index or
215-
* a range.
232+
* a range. To avoid spurious flow, we are going to ignore the possibility
233+
* that the argument might be a range (unless it is an explicit range literal,
234+
* see `ElementReferenceRangeReadUnknownSummary`).
216235
*/
217236
private class ElementReferenceReadUnknownSummary extends ElementReferenceReadSummary {
218237
ElementReferenceReadUnknownSummary() {
@@ -223,7 +242,7 @@ module Array {
223242

224243
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
225244
input = "Argument[self].Element[any]" and
226-
output = ["ReturnValue", "ReturnValue.Element[?]"] and
245+
output = "ReturnValue" and
227246
preservesValue = true
228247
}
229248
}
@@ -242,24 +261,8 @@ module Array {
242261
)
243262
or
244263
mc.getNumberOfArguments() = 1 and
245-
exists(RangeLiteral rl |
246-
rl = mc.getArgument(0) and
247-
(
248-
// Either an explicit, positive beginning index...
249-
start = rl.getBegin().getConstantValue().getInt() and start >= 0
250-
or
251-
// Or a begin-less one, since `..n` is equivalent to `0..n`
252-
not exists(rl.getBegin()) and start = 0
253-
) and
254-
// There must be an explicit end. An end-less range like `2..` is not
255-
// treated as a known range, since we don't track the length of the array.
256-
exists(int e | e = rl.getEnd().getConstantValue().getInt() and e >= 0 |
257-
rl.isInclusive() and end = e
258-
or
259-
rl.isExclusive() and end = e - 1
260-
) and
261-
this = methodName + "(" + start + ".." + end + ")"
262-
)
264+
isKnownRange(mc.getArgument(0), start, end) and
265+
this = methodName + "(" + start + ".." + end + ")"
263266
}
264267

265268
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
@@ -291,12 +294,7 @@ module Array {
291294
)
292295
or
293296
mc.getNumberOfArguments() = 1 and
294-
exists(RangeLiteral rl | rl = mc.getArgument(0) |
295-
exists(rl.getBegin()) and
296-
not exists(int b | b = rl.getBegin().getConstantValue().getInt() and b >= 0)
297-
or
298-
not exists(int e | e = rl.getEnd().getConstantValue().getInt() and e >= 0)
299-
)
297+
mc.getArgument(0) = any(RangeLiteral range | not isKnownRange(range, _, _))
300298
)
301299
}
302300

ruby/ql/lib/codeql/ruby/security/UnsafeCodeConstructionQuery.qll

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,6 @@ deprecated class Configuration extends TaintTracking::Configuration {
3232
override DataFlow::FlowFeature getAFeature() {
3333
result instanceof DataFlow::FeatureHasSourceCallContext
3434
}
35-
36-
override predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet set) {
37-
// allow implicit reads of array elements
38-
this.isSink(node) and
39-
set.isElementOfTypeOrUnknown("int")
40-
}
4135
}
4236

4337
private module UnsafeCodeConstructionConfig implements DataFlow::ConfigSig {

ruby/ql/lib/codeql/ruby/security/UnsafeShellCommandConstructionQuery.qll

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,6 @@ deprecated class Configuration extends TaintTracking::Configuration {
3434
override DataFlow::FlowFeature getAFeature() {
3535
result instanceof DataFlow::FeatureHasSourceCallContext
3636
}
37-
38-
override predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet set) {
39-
// allow implicit reads of array elements
40-
this.isSink(node) and
41-
set.isElementOfTypeOrUnknown("int")
42-
}
4337
}
4438

4539
private module UnsafeShellCommandConstructionConfig implements DataFlow::ConfigSig {

0 commit comments

Comments
 (0)