-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathLanguage.qll
More file actions
292 lines (234 loc) · 9.97 KB
/
Language.qll
File metadata and controls
292 lines (234 loc) · 9.97 KB
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
overlay[local?]
module;
private import java as Language
private import semmle.code.java.security.InsecureRandomnessQuery
private import semmle.code.java.security.RandomQuery
private import semmle.code.java.dataflow.DataFlow
private import semmle.code.java.dataflow.FlowSources
private import codeql.quantum.experimental.Model
private class UnknownLocation extends Language::Location {
UnknownLocation() { this.getFile().getAbsolutePath() = "" }
}
/**
* A dummy location which is used when something doesn't have a location in
* the source code but needs to have a `Location` associated with it. There
* may be several distinct kinds of unknown locations. For example: one for
* expressions, one for statements and one for other program elements.
*/
private class UnknownDefaultLocation extends UnknownLocation {
UnknownDefaultLocation() { locations_default(this, _, 0, 0, 0, 0) }
}
module CryptoInput implements InputSig<Language::Location> {
class DataFlowNode = DataFlow::Node;
class LocatableElement = Language::Element;
class UnknownLocation = UnknownDefaultLocation;
string locationToFileBaseNameAndLineNumberString(Location location) {
result = location.toString()
}
LocatableElement dfn_to_element(DataFlow::Node node) {
result = node.asExpr() or
result = node.asParameter()
}
predicate artifactOutputFlowsToGenericInput(
DataFlow::Node artifactOutput, DataFlow::Node otherInput
) {
ArtifactFlow::flow(artifactOutput, otherInput)
}
}
// Instantiate the `CryptographyBase` module
module Crypto = CryptographyBase<Language::Location, CryptoInput>;
// Definitions of various generic sources
final class DefaultFlowSource = SourceNode;
final class DefaultRemoteFlowSource = RemoteFlowSource;
private class GenericUnreferencedParameterSource extends Crypto::GenericUnreferencedParameterSource {
GenericUnreferencedParameterSource() {
exists(Parameter p |
this = p and
not exists(p.getAnArgument())
// or
// // TODO: this is test code which causes regression in unit tests, but will
// // find sources where ordinarily a source might be missing
// // If all calls to a function occur in a test file, ignore those calls
// // and consider the parameter to the function a potential source as well.
// forall(Call testCall | testCall.getCallee() = p.getCallable() |
// testCall.getFile().getBaseName().toUpperCase().matches("%TEST%")
// )
)
}
override predicate flowsTo(Crypto::FlowAwareElement other) {
GenericDataSourceFlow::flow(this.getOutputNode(), other.getInputNode())
}
override DataFlow::Node getOutputNode() { result.asParameter() = this }
override string getAdditionalDescription() { result = this.toString() }
}
private class GenericLocalDataSource extends Crypto::GenericLocalDataSource {
GenericLocalDataSource() {
any(DefaultFlowSource src | not src instanceof DefaultRemoteFlowSource).asExpr() = this
}
override DataFlow::Node getOutputNode() { result.asExpr() = this }
override predicate flowsTo(Crypto::FlowAwareElement other) {
GenericDataSourceFlow::flow(this.getOutputNode(), other.getInputNode())
}
override string getAdditionalDescription() { result = this.toString() }
}
private class GenericRemoteDataSource extends Crypto::GenericRemoteDataSource {
GenericRemoteDataSource() { any(DefaultRemoteFlowSource src).asExpr() = this }
override DataFlow::Node getOutputNode() { result.asExpr() = this }
override predicate flowsTo(Crypto::FlowAwareElement other) {
GenericDataSourceFlow::flow(this.getOutputNode(), other.getInputNode())
}
override string getAdditionalDescription() { result = this.toString() }
}
import semmle.code.java.frameworks.Properties
private import semmle.code.configfiles.ConfigFiles
/**
* A class to represent constants in Java code, either literals or
* values retrieved from properties files.
* Java CodeQL does not consider the values of known properties to be literals,
* hence we need to model both literals and property calls.
*/
class JavaConstant extends Expr {
string value;
JavaConstant() {
// If arg 0 in a getProperty call, consider it a literal only if
// we haven't resolved it to a known property value, otherwise
// use the resolved config value.
// If getProperty is used, always assume the default value is potentially used.
// CAVEAT/ASSUMPTION: this assumes the literal is immediately known at arg0
// of a getProperty call.
// also if the properties file is reloaded in a way where the reloaded file
// wouldn't have the property but the original does, we would erroneously
// consider the literal to be mapped to that property value.
exists(ConfigPair p, PropertiesGetPropertyMethodCall c |
c.getArgument(0).(Literal).getValue() = p.getNameElement().getName() and
value = p.getValueElement().getValue() and
this = c
)
or
// in this case, the property value is not known, use the literal property name as the value
exists(PropertiesGetPropertyMethodCall c |
value = c.getArgument(0).(Literal).getValue() and
not exists(ConfigPair p |
c.getArgument(0).(Literal).getValue() = p.getNameElement().getName()
) and
this = c
)
or
// in this case, there is not propery getter, we just have a literal
not exists(PropertiesGetPropertyMethodCall c | c.getArgument(0) = this) and
value = this.(Literal).getValue()
}
string getValue() { result = value }
}
private class ConstantDataSourceLiteral extends Crypto::GenericConstantSourceInstance instanceof JavaConstant
{
ConstantDataSourceLiteral() {
// TODO: this is an API specific workaround for JCA, as 'EC' is a constant that may be used
// where typical algorithms are specified, but EC specifically means set up a
// default curve container, that will later be specified explicitly (or if not a default)
// curve is used.
this.getValue() != "EC"
}
override DataFlow::Node getOutputNode() { result.asExpr() = this }
override predicate flowsTo(Crypto::FlowAwareElement other) {
// TODO: separate config to avoid blowing up data-flow analysis
GenericDataSourceFlow::flow(this.getOutputNode(), other.getInputNode())
}
override string getAdditionalDescription() { result = this.toString() }
}
private class ConstantDataSourceArrayInitializer extends Crypto::GenericConstantSourceInstance instanceof ArrayInit
{
ConstantDataSourceArrayInitializer() { this.getAnInit() instanceof Literal }
override DataFlow::Node getOutputNode() { result.asExpr() = this }
override predicate flowsTo(Crypto::FlowAwareElement other) {
// TODO: separate config to avoid blowing up data-flow analysis
GenericDataSourceFlow::flow(this.getOutputNode(), other.getInputNode())
}
override string getAdditionalDescription() { result = this.toString() }
}
/**
* An instance of random number generation, modeled as the expression
* tied to an output node (i.e., the result of the source of randomness)
*/
abstract class RandomnessInstance extends Crypto::RandomNumberGenerationInstance {
override DataFlow::Node getOutputNode() { result.asExpr() = this }
}
class SecureRandomnessInstance extends RandomnessInstance {
RandomDataSource source;
SecureRandomnessInstance() {
this = source.getOutput() and
source.getSourceOfRandomness() instanceof SecureRandomNumberGenerator
}
override string getGeneratorName() { result = source.getSourceOfRandomness().getQualifiedName() }
}
class InsecureRandomnessInstance extends RandomnessInstance {
RandomDataSource source;
InsecureRandomnessInstance() {
any(InsecureRandomnessSource src).asExpr() = this and source.getOutput() = this
}
override string getGeneratorName() { result = source.getSourceOfRandomness().getQualifiedName() }
}
/**
* An additional flow step in generic data-flow configurations.
* Where a step is an edge between nodes `n1` and `n2`,
* `this` = `n1` and `getOutput()` = `n2`.
*
* FOR INTERNAL MODELING USE ONLY.
*/
abstract class AdditionalFlowInputStep extends DataFlow::Node {
abstract DataFlow::Node getOutput();
final DataFlow::Node getInput() { result = this }
}
/**
* Generic data source to node input configuration
*/
module GenericDataSourceFlowConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
source = any(Crypto::GenericSourceInstance i).getOutputNode()
}
predicate isSink(DataFlow::Node sink) {
sink = any(Crypto::FlowAwareElement other).getInputNode()
}
predicate isBarrierOut(DataFlow::Node node) {
node = any(Crypto::FlowAwareElement element).getInputNode()
}
predicate isBarrierIn(DataFlow::Node node) {
node = any(Crypto::FlowAwareElement element).getOutputNode()
}
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
node1.(AdditionalFlowInputStep).getOutput() = node2
or
exists(MethodCall m |
m.getMethod().hasQualifiedName("java.lang", "String", "getBytes") and
node1.asExpr() = m.getQualifier() and
node2.asExpr() = m
)
}
}
module ArtifactFlowConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
source = any(Crypto::ArtifactInstance artifact).getOutputNode()
}
predicate isSink(DataFlow::Node sink) {
sink = any(Crypto::FlowAwareElement other).getInputNode()
}
predicate isBarrierOut(DataFlow::Node node) {
node = any(Crypto::FlowAwareElement element).getInputNode()
}
predicate isBarrierIn(DataFlow::Node node) {
node = any(Crypto::FlowAwareElement element).getOutputNode()
}
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
node1.(AdditionalFlowInputStep).getOutput() = node2
or
exists(MethodCall m |
m.getMethod().hasQualifiedName("java.lang", "String", "getBytes") and
node1.asExpr() = m.getQualifier() and
node2.asExpr() = m
)
}
}
module GenericDataSourceFlow = TaintTracking::Global<GenericDataSourceFlowConfig>;
module ArtifactFlow = TaintTracking::Global<ArtifactFlowConfig>;
// Import library-specific modeling
import JCA