-
Notifications
You must be signed in to change notification settings - Fork 1.7k
/
Copy pathInlineFlowTest.qll
137 lines (116 loc) · 4.65 KB
/
InlineFlowTest.qll
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/**
* Provides a simple base test for flow-related tests using inline expectations.
*
* Example for a test.ql:
* ```ql
* import swift
* import utils.test.InlineFlowTest
* import DefaultFlowTest
* import PathGraph
*
* from PathNode source, PathNode sink
* where flowPath(source, sink)
* select sink, source, sink, "$@", source, source.toString()
* ```
*
* To declare expectations, you can use the $hasTaintFlow or $hasValueFlow comments within the test source files.
* Example of the corresponding test file, e.g. Test.java
* ```swift
* func source(_ label: String) -> Any { return nil }
* func taint(_ label: String) -> Any { return nil }
* func sink(_ o: Any) { }
*
* func test() {
* let s = source("mySource")
* sink(s) // $ hasValueFlow=mySource
* let t = "foo" + taint("myTaint")
* sink(t); // $ hasTaintFlow=myTaint
* }
* ```
*
* If you are only interested in value flow, then instead of importing `DefaultFlowTest`, you can import
* `ValueFlowTest<DefaultFlowConfig>`. Similarly, if you are only interested in taint flow, then instead of
* importing `DefaultFlowTest`, you can import `TaintFlowTest<DefaultFlowConfig>`. In both cases
* `DefaultFlowConfig` can be replaced by another implementation of `DataFlow::ConfigSig`.
*
* If you need more fine-grained tuning, consider implementing a test using `InlineExpectationsTest`.
*/
import codeql.swift.dataflow.DataFlow
import codeql.swift.dataflow.ExternalFlow
import codeql.swift.dataflow.TaintTracking
import utils.test.InlineExpectationsTest
private predicate defaultSource(DataFlow::Node source) {
source
.asExpr()
.(CallExpr)
.getStaticTarget()
.(Function)
.getShortName()
.matches(["source%", "taint"])
}
private predicate defaultSink(DataFlow::Node sink) {
exists(CallExpr ca | ca.getStaticTarget().(Function).getShortName().matches("sink%") |
sink.asExpr() = ca.getAnArgument().getExpr()
)
}
module DefaultFlowConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { defaultSource(source) }
predicate isSink(DataFlow::Node sink) { defaultSink(sink) }
}
module NoFlowConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { none() }
predicate isSink(DataFlow::Node sink) { none() }
}
private signature string valueFlowTagSig();
private signature string taintFlowTagSig();
string defaultValueFlowTag() { result = "hasValueFlow" }
string defaultTaintFlowTag() { result = "hasTaintFlow" }
private string getSourceArgString(DataFlow::Node src) {
defaultSource(src) and
src.asExpr().(CallExpr).getAnArgument().getExpr().(StringLiteralExpr).getValue() = result
}
module FlowTest<
DataFlow::ConfigSig ValueFlowConfig, DataFlow::ConfigSig TaintFlowConfig,
valueFlowTagSig/0 valueFlowTag, taintFlowTagSig/0 taintFlowTag>
{
module ValueFlow = DataFlow::Global<ValueFlowConfig>;
module TaintFlow = TaintTracking::Global<TaintFlowConfig>;
private module InlineTest implements TestSig {
string getARelevantTag() { result = [valueFlowTag(), taintFlowTag()] }
predicate hasActualResult(Location location, string element, string tag, string value) {
tag = valueFlowTag() and
exists(DataFlow::Node src, DataFlow::Node sink | ValueFlow::flow(src, sink) |
sink.getLocation() = location and
element = sink.toString() and
if exists(getSourceArgString(src))
then value = getSourceArgString(src)
else value = src.getLocation().getStartLine().toString()
)
or
tag = taintFlowTag() and
exists(DataFlow::Node src, DataFlow::Node sink |
TaintFlow::flow(src, sink) and not ValueFlow::flow(src, sink)
|
sink.getLocation() = location and
element = sink.toString() and
if exists(getSourceArgString(src))
then value = getSourceArgString(src)
else value = src.getLocation().getStartLine().toString()
)
}
}
import MakeTest<InlineTest>
import DataFlow::MergePathGraph<ValueFlow::PathNode, TaintFlow::PathNode, ValueFlow::PathGraph, TaintFlow::PathGraph>
predicate flowPath(PathNode source, PathNode sink) {
ValueFlow::flowPath(source.asPathNode1(), sink.asPathNode1()) or
TaintFlow::flowPath(source.asPathNode2(), sink.asPathNode2())
}
}
module DefaultFlowTest =
FlowTest<DefaultFlowConfig, DefaultFlowConfig, defaultValueFlowTag/0, defaultTaintFlowTag/0>;
module ValueFlowTest<DataFlow::ConfigSig ValueFlowConfig> {
import FlowTest<ValueFlowConfig, NoFlowConfig, defaultValueFlowTag/0, defaultTaintFlowTag/0>
}
module TaintFlowTest<DataFlow::ConfigSig TaintFlowConfig> {
import FlowTest<NoFlowConfig, TaintFlowConfig, defaultValueFlowTag/0, defaultTaintFlowTag/0>
}