|
1 | 1 | /** |
2 | | - * Provides a simple base test for flow-related tests using inline expectations. |
3 | | - * |
4 | | - * Example for a test.ql: |
5 | | - * ```ql |
6 | | - * import go |
7 | | - * import TestUtilities.InlineFlowTest |
8 | | - * import DefaultFlowTest |
9 | | - * import PathGraph |
10 | | - * |
11 | | - * from PathNode source, PathNode sink |
12 | | - * where flowPath(source, sink) |
13 | | - * select sink, source, sink, "$@", source, source.toString() |
14 | | - * ``` |
15 | | - * |
16 | | - * To declare expectations, you can use the $hasTaintFlow or $hasValueFlow comments within the test source files. |
17 | | - * Example of the corresponding test file, e.g. Test.go |
18 | | - * ```go |
19 | | - * func source() string { return ""; } |
20 | | - * func taint() string { return ""; } |
21 | | - * func sink(s string) { } |
22 | | - * |
23 | | - * func test() { |
24 | | - * s := source() |
25 | | - * sink(s) // $ hasValueFlow="s" |
26 | | - * t := "foo" + taint() |
27 | | - * sink(t) // $ hasTaintFlow="t" |
28 | | - * } |
29 | | - * ``` |
30 | | - * |
31 | | - * If you are only interested in value flow, then instead of importing `DefaultFlowTest`, you can import |
32 | | - * `ValueFlowTest<DefaultFlowConfig>`. Similarly, if you are only interested in taint flow, then instead of |
33 | | - * importing `DefaultFlowTest`, you can import `TaintFlowTest<DefaultFlowConfig>`. In both cases |
34 | | - * `DefaultFlowConfig` can be replaced by another implementation of `DataFlow::ConfigSig`. |
35 | | - * |
36 | | - * If you need more fine-grained tuning, consider implementing a test using `InlineExpectationsTest`. |
| 2 | + * Inline flow tests for Go. |
| 3 | + * See `shared/util/codeql/dataflow/test/InlineFlowTest.qll` |
37 | 4 | */ |
38 | 5 |
|
39 | 6 | import go |
40 | | -import TestUtilities.InlineExpectationsTest |
41 | | - |
42 | | -private predicate defaultSource(DataFlow::Node source) { |
43 | | - exists(Function fn | fn.hasQualifiedName(_, ["source", "taint"]) | |
44 | | - source = fn.getACall().getResult() |
45 | | - ) |
46 | | -} |
47 | | - |
48 | | -private predicate defaultSink(DataFlow::Node sink) { |
49 | | - exists(Function fn | fn.hasQualifiedName(_, "sink") | sink = fn.getACall().getAnArgument()) |
50 | | -} |
51 | | - |
52 | | -module DefaultFlowConfig implements DataFlow::ConfigSig { |
53 | | - predicate isSource(DataFlow::Node source) { defaultSource(source) } |
54 | | - |
55 | | - predicate isSink(DataFlow::Node sink) { defaultSink(sink) } |
56 | | - |
57 | | - int fieldFlowBranchLimit() { result = 1000 } |
58 | | -} |
59 | | - |
60 | | -private module NoFlowConfig implements DataFlow::ConfigSig { |
61 | | - predicate isSource(DataFlow::Node source) { none() } |
62 | | - |
63 | | - predicate isSink(DataFlow::Node sink) { none() } |
64 | | -} |
65 | | - |
66 | | -module FlowTest<DataFlow::ConfigSig ValueFlowConfig, DataFlow::ConfigSig TaintFlowConfig> { |
67 | | - module ValueFlow = DataFlow::Global<ValueFlowConfig>; |
68 | | - |
69 | | - module TaintFlow = TaintTracking::Global<TaintFlowConfig>; |
70 | | - |
71 | | - private module InlineTest implements TestSig { |
72 | | - string getARelevantTag() { result = ["hasValueFlow", "hasTaintFlow"] } |
73 | | - |
74 | | - predicate hasActualResult(Location location, string element, string tag, string value) { |
75 | | - tag = "hasValueFlow" and |
76 | | - exists(DataFlow::Node sink | ValueFlow::flowTo(sink) | |
77 | | - sink.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), |
78 | | - location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and |
79 | | - element = sink.toString() and |
80 | | - value = "\"" + sink.toString() + "\"" |
81 | | - ) |
82 | | - or |
83 | | - tag = "hasTaintFlow" and |
84 | | - exists(DataFlow::Node src, DataFlow::Node sink | |
85 | | - TaintFlow::flow(src, sink) and not ValueFlow::flow(src, sink) |
86 | | - | |
87 | | - sink.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), |
88 | | - location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and |
89 | | - element = sink.toString() and |
90 | | - value = "\"" + sink.toString() + "\"" |
91 | | - ) |
92 | | - } |
| 7 | +private import codeql.dataflow.test.InlineFlowTest |
| 8 | +private import semmle.go.dataflow.internal.DataFlowImplSpecific |
| 9 | +private import semmle.go.dataflow.internal.TaintTrackingImplSpecific |
| 10 | +private import internal.InlineExpectationsTestImpl |
| 11 | + |
| 12 | +private module FlowTestImpl implements InputSig<GoDataFlow> { |
| 13 | + predicate defaultSource(DataFlow::Node source) { |
| 14 | + exists(Function fn | fn.hasQualifiedName(_, ["source", "taint"]) | |
| 15 | + source = fn.getACall().getResult() |
| 16 | + ) |
93 | 17 | } |
94 | 18 |
|
95 | | - import MakeTest<InlineTest> |
96 | | - import DataFlow::MergePathGraph<ValueFlow::PathNode, TaintFlow::PathNode, ValueFlow::PathGraph, TaintFlow::PathGraph> |
97 | | - |
98 | | - predicate flowPath(PathNode source, PathNode sink) { |
99 | | - ValueFlow::flowPath(source.asPathNode1(), sink.asPathNode1()) or |
100 | | - TaintFlow::flowPath(source.asPathNode2(), sink.asPathNode2()) |
| 19 | + predicate defaultSink(DataFlow::Node sink) { |
| 20 | + exists(Function fn | fn.hasQualifiedName(_, "sink") | sink = fn.getACall().getAnArgument()) |
101 | 21 | } |
102 | | -} |
103 | | - |
104 | | -module DefaultFlowTest = FlowTest<DefaultFlowConfig, DefaultFlowConfig>; |
105 | 22 |
|
106 | | -module ValueFlowTest<DataFlow::ConfigSig ValueFlowConfig> { |
107 | | - import FlowTest<ValueFlowConfig, NoFlowConfig> |
| 23 | + string getArgString(DataFlow::Node src, DataFlow::Node sink) { |
| 24 | + exists(src) and |
| 25 | + result = "\"" + sink.toString() + "\"" |
| 26 | + } |
108 | 27 | } |
109 | 28 |
|
110 | | -module TaintFlowTest<DataFlow::ConfigSig TaintFlowConfig> { |
111 | | - import FlowTest<NoFlowConfig, TaintFlowConfig> |
112 | | -} |
| 29 | +import InlineFlowTestMake<GoDataFlow, GoTaintTracking, Impl, FlowTestImpl> |
0 commit comments