33 *
44 * Example for a test.ql:
55 * ```ql
6- * import java
6+ * import go
77 * import TestUtilities.InlineFlowTest
8+ * import DefaultFlowTest
89 * ```
910 *
1011 * To declare expectations, you can use the $hasTaintFlow or $hasValueFlow comments within the test source files.
11- * Example of the corresponding test file, e.g. Test.java
12+ * Example of the corresponding test file, e.g. Test.go
1213 * ```go
13- * public class Test {
14- *
15- * Object source() { return null; }
16- * String taint() { return null; }
17- * void sink(Object o) { }
18- *
19- * public void test() {
20- * Object s = source();
21- * sink(s); //$hasValueFlow
22- * String t = "foo" + taint();
23- * sink(t); //$hasTaintFlow
24- * }
14+ * func source() string { return ""; }
15+ * func taint() string { return ""; }
16+ * func sink(s string) { }
2517 *
18+ * func test() {
19+ * s := source()
20+ * sink(s) // $ hasValueFlow="s"
21+ * t := "foo" + taint()
22+ * sink(t) // $ hasTaintFlow="t"
2623 * }
2724 * ```
2825 *
29- * If you're not interested in a specific flow type, you can disable either value or taint flow expectations as follows:
30- * ```ql
31- * class HasFlowTest extends InlineFlowTest {
32- * override DataFlow::Configuration getTaintFlowConfig() { none() }
33- *
34- * override DataFlow::Configuration getValueFlowConfig() { none() }
35- * }
36- * ```
26+ * If you are only interested in value flow, then instead of importing `DefaultFlowTest`, you can import
27+ * `ValueFlowTest<DefaultFlowConfig>`. Similarly, if you are only interested in taint flow, then instead of
28+ * importing `DefaultFlowTest`, you can import `TaintFlowTest<DefaultFlowConfig>`. In both cases
29+ * `DefaultFlowConfig` can be replaced by another implementation of `DataFlow::ConfigSig`.
3730 *
3831 * If you need more fine-grained tuning, consider implementing a test using `InlineExpectationsTest`.
3932 */
@@ -47,57 +40,62 @@ private predicate defaultSource(DataFlow::Node source) {
4740 )
4841}
4942
50- class DefaultValueFlowConf extends DataFlow:: Configuration {
51- DefaultValueFlowConf ( ) { this = "qltest:defaultValueFlowConf" }
43+ private predicate defaultSink ( DataFlow:: Node sink ) {
44+ exists ( Function fn | fn .hasQualifiedName ( _, "sink" ) | sink = fn .getACall ( ) .getAnArgument ( ) )
45+ }
46+
47+ module DefaultFlowConfig implements DataFlow:: ConfigSig {
48+ predicate isSource ( DataFlow:: Node source ) { defaultSource ( source ) }
5249
53- override predicate isSource ( DataFlow:: Node source ) { defaultSource ( source ) }
50+ predicate isSink ( DataFlow:: Node sink ) { defaultSink ( sink ) }
5451
55- override predicate isSink ( DataFlow:: Node sink ) {
56- exists ( Function fn | fn .hasQualifiedName ( _, "sink" ) | sink = fn .getACall ( ) .getAnArgument ( ) )
57- }
52+ int fieldFlowBranchLimit ( ) { result = 1000 }
53+ }
54+
55+ private module NoFlowConfig implements DataFlow:: ConfigSig {
56+ predicate isSource ( DataFlow:: Node source ) { none ( ) }
5857
59- override int fieldFlowBranchLimit ( ) { result = 1000 }
58+ predicate isSink ( DataFlow :: Node sink ) { none ( ) }
6059}
6160
62- class DefaultTaintFlowConf extends TaintTracking :: Configuration {
63- DefaultTaintFlowConf ( ) { this = "qltest:defaultTaintFlowConf" }
61+ module FlowTest < DataFlow :: ConfigSig ValueFlowConfig , DataFlow :: ConfigSig TaintFlowConfig > {
62+ module ValueFlow = DataFlow :: Global < ValueFlowConfig > ;
6463
65- override predicate isSource ( DataFlow :: Node source ) { defaultSource ( source ) }
64+ module TaintFlow = TaintTracking :: Global < TaintFlowConfig > ;
6665
67- override predicate isSink ( DataFlow:: Node sink ) {
68- exists ( Function fn | fn .hasQualifiedName ( _, "sink" ) | sink = fn .getACall ( ) .getAnArgument ( ) )
66+ private module InlineTest implements TestSig {
67+ string getARelevantTag ( ) { result = [ "hasValueFlow" , "hasTaintFlow" ] }
68+
69+ predicate hasActualResult ( Location location , string element , string tag , string value ) {
70+ tag = "hasValueFlow" and
71+ exists ( DataFlow:: Node sink | ValueFlow:: flowTo ( sink ) |
72+ sink .hasLocationInfo ( location .getFile ( ) .getAbsolutePath ( ) , location .getStartLine ( ) ,
73+ location .getStartColumn ( ) , location .getEndLine ( ) , location .getEndColumn ( ) ) and
74+ element = sink .toString ( ) and
75+ value = "\"" + sink .toString ( ) + "\""
76+ )
77+ or
78+ tag = "hasTaintFlow" and
79+ exists ( DataFlow:: Node src , DataFlow:: Node sink |
80+ TaintFlow:: flow ( src , sink ) and not ValueFlow:: flow ( src , sink )
81+ |
82+ sink .hasLocationInfo ( location .getFile ( ) .getAbsolutePath ( ) , location .getStartLine ( ) ,
83+ location .getStartColumn ( ) , location .getEndLine ( ) , location .getEndColumn ( ) ) and
84+ element = sink .toString ( ) and
85+ value = "\"" + sink .toString ( ) + "\""
86+ )
87+ }
6988 }
7089
71- override int fieldFlowBranchLimit ( ) { result = 1000 }
90+ import MakeTest < InlineTest >
7291}
7392
74- class InlineFlowTest extends InlineExpectationsTest {
75- InlineFlowTest ( ) { this = "HasFlowTest" }
76-
77- override string getARelevantTag ( ) { result = [ "hasValueFlow" , "hasTaintFlow" ] }
78-
79- override predicate hasActualResult ( Location location , string element , string tag , string value ) {
80- tag = "hasValueFlow" and
81- exists ( DataFlow:: Node sink | this .getValueFlowConfig ( ) .hasFlowTo ( sink ) |
82- sink .hasLocationInfo ( location .getFile ( ) .getAbsolutePath ( ) , location .getStartLine ( ) ,
83- location .getStartColumn ( ) , location .getEndLine ( ) , location .getEndColumn ( ) ) and
84- element = sink .toString ( ) and
85- value = "\"" + sink .toString ( ) + "\""
86- )
87- or
88- tag = "hasTaintFlow" and
89- exists ( DataFlow:: Node src , DataFlow:: Node sink |
90- this .getTaintFlowConfig ( ) .hasFlow ( src , sink ) and
91- not this .getValueFlowConfig ( ) .hasFlow ( src , sink )
92- |
93- sink .hasLocationInfo ( location .getFile ( ) .getAbsolutePath ( ) , location .getStartLine ( ) ,
94- location .getStartColumn ( ) , location .getEndLine ( ) , location .getEndColumn ( ) ) and
95- element = sink .toString ( ) and
96- value = "\"" + sink .toString ( ) + "\""
97- )
98- }
93+ module DefaultFlowTest = FlowTest< DefaultFlowConfig , DefaultFlowConfig > ;
9994
100- DataFlow:: Configuration getValueFlowConfig ( ) { result = any ( DefaultValueFlowConf config ) }
95+ module ValueFlowTest< DataFlow:: ConfigSig ValueFlowConfig> {
96+ import FlowTest< ValueFlowConfig , NoFlowConfig >
97+ }
10198
102- DataFlow:: Configuration getTaintFlowConfig ( ) { result = any ( DefaultTaintFlowConf config ) }
99+ module TaintFlowTest< DataFlow:: ConfigSig TaintFlowConfig> {
100+ import FlowTest< NoFlowConfig , TaintFlowConfig >
103101}
0 commit comments