This repository was archived by the owner on Jan 5, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 128
Expand file tree
/
Copy pathCleartextLoggingCustomizations.qll
More file actions
209 lines (185 loc) · 6.61 KB
/
CleartextLoggingCustomizations.qll
File metadata and controls
209 lines (185 loc) · 6.61 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
/**
* Provides default sources, sinks, and sanitizers for reasoning about
* clear-text logging of sensitive information, as well as extension
* points for adding your own.
*/
import go
private import semmle.go.security.SensitiveActions::HeuristicNames
private import semmle.go.security.SensitiveActions::PasswordHeuristics
/**
* Provides extension points for customizing the data-flow tracking configuration for reasoning
* about clear-text logging of sensitive information.
*/
module CleartextLogging {
/**
* A data-flow source for clear-text logging of sensitive information.
*/
abstract class Source extends DataFlow::Node {
Source() {
// hard-coded strings are uninteresting
not exists(getStringValue())
}
/** Gets a string that describes the type of this data-flow source. */
abstract string describe();
}
/**
* A data-flow sink for clear-text logging of sensitive information.
*/
abstract class Sink extends DataFlow::Node { }
/**
* A barrier for clear-text logging of sensitive information.
*/
abstract class Barrier extends DataFlow::Node { }
/**
* An argument to a logging mechanism.
*/
class LoggerSink extends Sink {
LoggerSink() { this = any(LoggerCall log).getAMessageComponent() }
}
/**
* A data-flow node that does not contain a clear-text password, according to its syntactic name.
*/
private class NameGuidedNonCleartextPassword extends NonCleartextPassword {
NameGuidedNonCleartextPassword() {
exists(string name |
name.regexpMatch(notSensitive())
or
name.regexpMatch("(?i).*err(or)?.*")
|
this.asExpr().(Ident).getName() = name
or
this.(DataFlow::SsaNode).getSourceVariable().getName() = name
or
this.(DataFlow::FieldReadNode).getFieldName() = name
or
this.(DataFlow::CallNode).getCalleeName() = name
)
or
// avoid i18n strings
this.(DataFlow::FieldReadNode)
.getBase()
.asExpr()
.(Ident)
.getName()
.regexpMatch("(?is).*(messages|strings).*")
or
// avoid dummy passwords
isDummyPassword(this.getStringValue())
}
}
/**
* A data-flow node that receives flow that is not a clear-text password.
*/
private class NonCleartextPasswordFlow extends NonCleartextPassword {
NonCleartextPasswordFlow() { this = any(NonCleartextPassword other).getASuccessor*() }
}
/**
* A call that might obfuscate a password, for example through hashing.
*/
private class ObfuscatorCall extends Barrier, DataFlow::CallNode {
ObfuscatorCall() { this.getCalleeName().regexpMatch(notSensitive()) }
}
/**
* Read of a non-sensitive header, considered as a barrier for clear-text logging.
*/
private class NonSensitiveHeaderGet extends Barrier {
NonSensitiveHeaderGet() {
exists(string headerName |
exists(DataFlow::MethodCallNode c | c = this |
c.getTarget().hasQualifiedName("net/http", "Header", "Get") and
headerName = c.getArgument(0).getStringValue()
)
or
exists(DataFlow::ElementReadNode e | e = this |
e.getBase().getType().hasQualifiedName("net/http", "Header") and
headerName = e.getIndex().getStringValue()
)
|
not headerName.toLowerCase() in ["authorization", "cookie"]
)
}
}
/**
* A data-flow node that does not contain a clear-text password.
*/
abstract private class NonCleartextPassword extends DataFlow::Node { }
/**
* A value assigned to a struct field that may contain password information.
*
* This is a source since `log.Print(obj)` will often show the fields of `obj`.
*/
private class StructPasswordFieldSource extends DataFlow::Node, Source {
string name;
StructPasswordFieldSource() {
exists(Write write, Field f | write.writesField(_, f, this) |
name = f.getName() and
name.regexpMatch(maybePassword()) and
not name.regexpMatch(notSensitive()) and
// avoid safe values assigned to presumably unsafe names
not this instanceof NonCleartextPassword
)
}
override string describe() { result = "an access to " + name }
}
/** An access to a variable or property that might contain a password. */
private class ReadPasswordSource extends DataFlow::Node, Source {
string name;
ReadPasswordSource() {
// avoid safe values assigned to presumably unsafe names
not this instanceof NonCleartextPassword and
name.regexpMatch(maybePassword()) and
(
this.(DataFlow::SsaNode).getSourceVariable().getName() = name
or
exists(DataFlow::FieldReadNode fn |
fn = this and
fn.getFieldName() = name and
// avoid safe values assigned to presumably unsafe names
not exists(Write write, NonCleartextPassword ncp |
write.writesField(fn.getBase().getAPredecessor*(), fn.getField(), ncp)
)
)
)
}
override string describe() { result = "an access to " + name }
}
/** A call that might return a password. */
private class CallPasswordSource extends DataFlow::CallNode, Source {
string name;
CallPasswordSource() {
name = getCalleeName() and
name.regexpMatch("(?is)getPassword")
}
override string describe() { result = "a call to " + name }
}
/** The headers of an HTTP request, considered as a source of sensitive information. */
private class RequestHeaderSource extends Source {
RequestHeaderSource() {
exists(Field hdr |
hdr.hasQualifiedName("net/http", "Request", "Header") and
this = hdr.getARead()
)
}
override string describe() { result = "HTTP request headers" }
}
private class KubernetesSecretInterfaceSource extends Source, K8sIoClientGo::SecretInterfaceSource {
override string describe() { result = "Kubernetes Secret" }
}
/**
* The first element of a split by ' ' or ':', often sanitizing a username/password pair
* or the "Method value" syntax used in the HTTP Authorization header.
*/
private class NonSensitiveAuthorizationElement extends Barrier, DataFlow::ElementReadNode {
NonSensitiveAuthorizationElement() {
exists(DataFlow::CallNode splitCall, DataFlow::Node splitAlias |
splitCall
.getTarget()
.hasQualifiedName("strings", ["Split", "SplitN", "SplitAfter", "SplitAfterN"]) and
splitCall.getArgument(1).getStringValue() = [" ", ":"] and
DataFlow::localFlow(splitCall.getResult(), splitAlias) and
this.getBase() = splitAlias
) and
this.getIndex().getIntValue() = 0
}
}
}