Skip to content

Commit d82c3ce

Browse files
committed
Ruby: Rewrite InlineFlowTest as a parameterized module
1 parent 742eb8d commit d82c3ce

40 files changed

+189
-141
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: deprecated
3+
---
4+
* The `Configuration` taint flow configuration class from `codeql.ruby.security.InsecureDownloadQuery` has been deprecated. Use the `Flow` module instead.

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

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import InsecureDownloadCustomizations::InsecureDownload
1313
/**
1414
* A taint tracking configuration for download of sensitive file through insecure connection.
1515
*/
16-
class Configuration extends DataFlow::Configuration {
16+
deprecated class Configuration extends DataFlow::Configuration {
1717
Configuration() { this = "InsecureDownload" }
1818

1919
override predicate isSource(DataFlow::Node source, DataFlow::FlowState label) {
@@ -29,3 +29,30 @@ class Configuration extends DataFlow::Configuration {
2929
node instanceof Sanitizer
3030
}
3131
}
32+
33+
/**
34+
* A taint tracking configuration for download of sensitive file through insecure connection.
35+
*/
36+
module Config implements DataFlow::StateConfigSig {
37+
class FlowState = string;
38+
39+
predicate isSource(DataFlow::Node source, DataFlow::FlowState label) {
40+
source.(Source).getALabel() = label
41+
}
42+
43+
predicate isSink(DataFlow::Node sink, DataFlow::FlowState label) {
44+
sink.(Sink).getALabel() = label
45+
}
46+
47+
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
48+
49+
predicate isBarrier(DataFlow::Node node, FlowState state) { none() }
50+
51+
predicate isAdditionalFlowStep(
52+
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
53+
) {
54+
none()
55+
}
56+
}
57+
58+
module Flow = DataFlow::GlobalWithState<Config>;

ruby/ql/src/queries/security/cwe-829/InsecureDownload.ql

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@
1414
import codeql.ruby.AST
1515
import codeql.ruby.DataFlow
1616
import codeql.ruby.security.InsecureDownloadQuery
17-
import DataFlow::PathGraph
17+
import Flow::PathGraph
1818

19-
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
20-
where cfg.hasFlowPath(source, sink)
19+
from Flow::PathNode source, Flow::PathNode sink
20+
where Flow::flowPath(source, sink)
2121
select sink.getNode(), source, sink, "$@ of sensitive file from $@.",
2222
sink.getNode().(Sink).getDownloadCall(), "Download", source.getNode(), "HTTP source"

ruby/ql/test/TestUtilities/InlineFlowTest.qll

Lines changed: 51 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44
* Example for a test.ql:
55
* ```ql
66
* import TestUtilities.InlineFlowTest
7+
* import DefaultFlowTest
78
* import PathGraph
89
*
9-
* from DataFlow::PathNode source, DataFlow::PathNode sink, DefaultValueFlowConf conf
10-
* where conf.hasFlowPath(source, sink)
10+
* from ValueFlow::PathNode source, ValueFlow::PathNode sink
11+
* where ValueFlow::flowPath(source, sink)
1112
* select sink, source, sink, "$@", source, source.toString()
1213
* ```
1314
*
@@ -20,14 +21,10 @@
2021
* sink(t); // $ hasTaintFlow=2
2122
* ```
2223
*
23-
* If you're not interested in a specific flow type, you can disable either value or taint flow expectations as follows:
24-
* ```ql
25-
* class HasFlowTest extends InlineFlowTest {
26-
* override DataFlow::Configuration getTaintFlowConfig() { none() }
27-
*
28-
* override DataFlow::Configuration getValueFlowConfig() { none() }
29-
* }
30-
* ```
24+
* If you are only interested in value flow, then instead of importing `DefaultFlowTest`, you can import
25+
* `ValueFlowTest<DefaultFlowConfig>`. Similarly, if you are only interested in taint flow, then instead of
26+
* importing `DefaultFlowTest`, you can import `TaintFlowTest<DefaultFlowConfig>`. In both cases
27+
* `DefaultFlowConfig` can be replaced by another implementation of `DataFlow::ConfigSig`.
3128
*
3229
* If you need more fine-grained tuning, consider implementing a test using `InlineExpectationsTest`.
3330
*/
@@ -38,72 +35,62 @@ import codeql.ruby.TaintTracking
3835
import TestUtilities.InlineExpectationsTest
3936
import TestUtilities.InlineFlowTestUtil
4037

41-
class DefaultValueFlowConf extends DataFlow::Configuration {
42-
DefaultValueFlowConf() { this = "qltest:defaultValueFlowConf" }
43-
44-
override predicate isSource(DataFlow::Node n) { defaultSource(n) }
38+
module DefaultFlowConfig implements DataFlow::ConfigSig {
39+
predicate isSource(DataFlow::Node source) { defaultSource(source) }
4540

46-
override predicate isSink(DataFlow::Node n) { defaultSink(n) }
41+
predicate isSink(DataFlow::Node sink) { defaultSink(sink) }
4742

48-
override int fieldFlowBranchLimit() { result = 1000 }
43+
int fieldFlowBranchLimit() { result = 1000 }
4944
}
5045

51-
class DefaultTaintFlowConf extends TaintTracking::Configuration {
52-
DefaultTaintFlowConf() { this = "qltest:defaultTaintFlowConf" }
46+
private module NoFlowConfig implements DataFlow::ConfigSig {
47+
predicate isSource(DataFlow::Node source) { none() }
5348

54-
override predicate isSource(DataFlow::Node n) { defaultSource(n) }
55-
56-
override predicate isSink(DataFlow::Node n) { defaultSink(n) }
57-
58-
override int fieldFlowBranchLimit() { result = 1000 }
49+
predicate isSink(DataFlow::Node sink) { none() }
5950
}
6051

61-
class InlineFlowTest extends InlineExpectationsTest {
62-
InlineFlowTest() { this = "HasFlowTest" }
63-
64-
override string getARelevantTag() { result = ["hasValueFlow", "hasTaintFlow"] }
65-
66-
override predicate hasActualResult(Location location, string element, string tag, string value) {
67-
tag = "hasValueFlow" and
68-
exists(DataFlow::Node src, DataFlow::Node sink | this.getValueFlowConfig().hasFlow(src, sink) |
69-
sink.getLocation() = location and
70-
element = sink.toString() and
71-
if exists(getSourceArgString(src)) then value = getSourceArgString(src) else value = ""
72-
)
73-
or
74-
tag = "hasTaintFlow" and
75-
exists(DataFlow::Node src, DataFlow::Node sink |
76-
this.getTaintFlowConfig().hasFlow(src, sink) and
77-
not this.getValueFlowConfig().hasFlow(src, sink)
78-
|
79-
sink.getLocation() = location and
80-
element = sink.toString() and
81-
if exists(getSourceArgString(src)) then value = getSourceArgString(src) else value = ""
82-
)
52+
module FlowTest<DataFlow::ConfigSig ValueFlowConfig, DataFlow::ConfigSig TaintFlowConfig> {
53+
module ValueFlow = DataFlow::Global<ValueFlowConfig>;
54+
55+
module TaintFlow = TaintTracking::Global<TaintFlowConfig>;
56+
57+
private module InlineTest implements TestSig {
58+
string getARelevantTag() { result = ["hasValueFlow", "hasTaintFlow"] }
59+
60+
predicate hasActualResult(Location location, string element, string tag, string value) {
61+
tag = "hasValueFlow" and
62+
exists(DataFlow::Node src, DataFlow::Node sink | ValueFlow::flow(src, sink) |
63+
sink.getLocation() = location and
64+
element = sink.toString() and
65+
if exists(getSourceArgString(src)) then value = getSourceArgString(src) else value = ""
66+
)
67+
or
68+
tag = "hasTaintFlow" and
69+
exists(DataFlow::Node src, DataFlow::Node sink |
70+
TaintFlow::flow(src, sink) and not ValueFlow::flow(src, sink)
71+
|
72+
sink.getLocation() = location and
73+
element = sink.toString() and
74+
if exists(getSourceArgString(src)) then value = getSourceArgString(src) else value = ""
75+
)
76+
}
8377
}
8478

85-
DataFlow::Configuration getValueFlowConfig() { result = any(DefaultValueFlowConf config) }
86-
87-
DataFlow::Configuration getTaintFlowConfig() { result = any(DefaultTaintFlowConf config) }
88-
}
89-
90-
module PathGraph {
91-
private import DataFlow::PathGraph as PG
79+
import MakeTest<InlineTest>
80+
import DataFlow::MergePathGraph<ValueFlow::PathNode, TaintFlow::PathNode, ValueFlow::PathGraph, TaintFlow::PathGraph>
9281

93-
private class PathNode extends DataFlow::PathNode {
94-
PathNode() {
95-
this.getConfiguration() =
96-
[any(InlineFlowTest t).getValueFlowConfig(), any(InlineFlowTest t).getTaintFlowConfig()]
97-
}
82+
predicate flowPath(PathNode source, PathNode sink) {
83+
ValueFlow::flowPath(source.asPathNode1(), sink.asPathNode1()) or
84+
TaintFlow::flowPath(source.asPathNode2(), sink.asPathNode2())
9885
}
86+
}
9987

100-
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
101-
query predicate edges(PathNode a, PathNode b) { PG::edges(a, b) }
88+
module DefaultFlowTest = FlowTest<DefaultFlowConfig, DefaultFlowConfig>;
10289

103-
/** Holds if `n` is a node in the graph of data flow path explanations. */
104-
query predicate nodes(PathNode n, string key, string val) { PG::nodes(n, key, val) }
90+
module ValueFlowTest<DataFlow::ConfigSig ValueFlowConfig> {
91+
import FlowTest<ValueFlowConfig, NoFlowConfig>
92+
}
10593

106-
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
107-
PG::subpaths(arg, par, ret, out)
108-
}
94+
module TaintFlowTest<DataFlow::ConfigSig TaintFlowConfig> {
95+
import FlowTest<NoFlowConfig, TaintFlowConfig>
10996
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
failures
2+
testFailures
23
edges
34
| array_flow.rb:2:5:2:5 | a [element 0] | array_flow.rb:3:10:3:10 | a [element 0] |
45
| array_flow.rb:2:5:2:5 | a [element 0] | array_flow.rb:3:10:3:10 | a [element 0] |

ruby/ql/test/library-tests/dataflow/array-flow/array-flow.ql

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44

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

9-
from DataFlow::PathNode source, DataFlow::PathNode sink, DefaultValueFlowConf conf
10-
where conf.hasFlowPath(source, sink)
10+
from ValueFlow::PathNode source, ValueFlow::PathNode sink
11+
where ValueFlow::flowPath(source, sink)
1112
select sink, source, sink, "$@", source, source.toString()

ruby/ql/test/library-tests/dataflow/call-sensitivity/call-sensitivity.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
failures
2+
testFailures
23
edges
34
| call_sensitivity.rb:9:7:9:13 | call to taint | call_sensitivity.rb:9:6:9:14 | ( ... ) |
45
| call_sensitivity.rb:9:7:9:13 | call to taint | call_sensitivity.rb:9:6:9:14 | ( ... ) |

ruby/ql/test/library-tests/dataflow/call-sensitivity/call-sensitivity.ql

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@
55
import codeql.ruby.AST
66
import codeql.ruby.DataFlow
77
import TestUtilities.InlineFlowTest
8-
import DataFlow::PathGraph
8+
import DefaultFlowTest
9+
import PathGraph
910
import codeql.ruby.dataflow.internal.DataFlowDispatch as DataFlowDispatch
1011

1112
query predicate mayBenefitFromCallContext = DataFlowDispatch::mayBenefitFromCallContext/2;
1213

1314
query predicate viableImplInCallContext = DataFlowDispatch::viableImplInCallContext/2;
1415

15-
from DataFlow::PathNode source, DataFlow::PathNode sink, DefaultTaintFlowConf conf
16-
where conf.hasFlowPath(source, sink)
16+
from TaintFlow::PathNode source, TaintFlow::PathNode sink
17+
where TaintFlow::flowPath(source, sink)
1718
select sink, source, sink, "$@", source, source.toString()

ruby/ql/test/library-tests/dataflow/flow-summaries/semantics.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
failures
2+
testFailures
23
edges
34
| semantics.rb:2:5:2:5 | a | semantics.rb:3:9:3:9 | a |
45
| semantics.rb:2:5:2:5 | a | semantics.rb:3:9:3:9 | a |

ruby/ql/test/library-tests/dataflow/flow-summaries/semantics.ql

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

66
import codeql.ruby.AST
77
import TestUtilities.InlineFlowTest
8+
import DefaultFlowTest
89
import PathGraph
910
private import codeql.ruby.dataflow.FlowSummary
1011

0 commit comments

Comments
 (0)