Skip to content
This repository was archived by the owner on Jan 5, 2023. It is now read-only.

Commit 21d107e

Browse files
committed
Check for suspected feature-flags more uniformly
These are now checked of all source *and* sink nodes, and the checks are factored with similar paths for is-insecure and is-old flags.
1 parent 7d294c5 commit 21d107e

File tree

2 files changed

+41
-44
lines changed

2 files changed

+41
-44
lines changed

ql/src/experimental/CWE-327/InsecureTLS.ql

Lines changed: 8 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -45,37 +45,6 @@ predicate isInsecureTlsVersion(int val, string name, string fieldName) {
4545
)
4646
}
4747

48-
/**
49-
* Holds of string literals or named constants matching `isLegacyFlagName`
50-
*/
51-
predicate exprSuggestsOldVersion(Expr node) {
52-
isLegacyFlagName(node.getStringValue()) or
53-
isLegacyFlagName(node.(Name).getTarget().getName())
54-
}
55-
56-
/**
57-
* Holds if `node` suggests an old TLS version according to `isLegacyFlagName`
58-
*/
59-
predicate nodeSuggestsOldVersion(AstNode node) {
60-
// Map literal old: value or "old": value
61-
exprSuggestsOldVersion(node.(KeyValueExpr).getKey())
62-
or
63-
// Variable initialisation old := value
64-
exists(ValueSpec valueSpec, int childIdx | isLegacyFlagName(valueSpec.getName(childIdx)) |
65-
node = valueSpec.getInit(childIdx)
66-
)
67-
or
68-
// Assignment old = value
69-
exists(Assignment assignment, int childIdx |
70-
isLegacyFlagName(assignment.getLhs(childIdx).(Ident).getName())
71-
|
72-
node = assignment.getRhs(childIdx)
73-
)
74-
or
75-
// Case clause 'case old:' or 'case "old":'
76-
exprSuggestsOldVersion(node.(CaseClause).getAnExpr())
77-
}
78-
7948
/**
8049
* Holds if `node` refers to a value returned alongside a non-nil error value.
8150
*
@@ -163,9 +132,6 @@ DataFlow::Node nodeOrDeref(DataFlow::Node node) {
163132
/**
164133
* Holds if an insecure TLS version flows from `source` to `sink`, which is in turn written
165134
* to a field of `base`. `message` describes the specific problem found.
166-
*
167-
* Contexts suggesting an intentionally insecure or legacy configuration are excluded (see
168-
* `nodeSuggestsOldVersion`), as are fields that may conditionally receive a modern TLS version.
169135
*/
170136
predicate isInsecureTlsVersionFlow(
171137
DataFlow::PathNode source, DataFlow::PathNode sink, string message, DataFlow::Node base
@@ -175,7 +141,6 @@ predicate isInsecureTlsVersionFlow(
175141
cfg.isSource(source.getNode(), version) and
176142
cfg.isSink(sink.getNode(), fld, base, _) and
177143
isInsecureTlsVersion(version, _, fld.getName()) and
178-
not nodeSuggestsOldVersion(base.asExpr().getParent*()) and
179144
// Exclude cases where a secure TLS version can also flow to the same
180145
// sink, or to different sinks that refer to the same base and field,
181146
// which suggests a configurable security mode.
@@ -215,8 +180,7 @@ class TlsInsecureCipherSuitesFlowConfig extends TaintTracking::Configuration {
215180
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", "TLS_ECDHE_RSA_WITH_RC4_128_SHA",
216181
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256"]
217182
|
218-
source = val.getARead() and
219-
not nodeSuggestsOldVersion(source.asExpr().getParent*())
183+
source = val.getARead()
220184
)
221185
}
222186

@@ -239,14 +203,12 @@ class TlsInsecureCipherSuitesFlowConfig extends TaintTracking::Configuration {
239203
}
240204

241205
/**
242-
* Holds if `fieldWrite` writes `sink` to `base`.`fld`, and `fld` is `tls.Config.CipherSuites`,
243-
* and no parent of `base` is named suggesting an intentionally insecure configuration.
206+
* Holds if `fieldWrite` writes `sink` to `base`.`fld`, and `fld` is `tls.Config.CipherSuites`.
244207
*/
245208
predicate isSink(DataFlow::Node sink, Field fld, DataFlow::Node base, Write fieldWrite) {
246209
fld.hasQualifiedName("crypto/tls", "Config", "CipherSuites") and
247210
fieldWrite = fld.getAWrite() and
248-
fieldWrite.writesField(base, fld, sink) and
249-
not nodeSuggestsOldVersion(base.asExpr().getParent*())
211+
fieldWrite.writesField(base, fld, sink)
250212
}
251213

252214
override predicate isSink(DataFlow::Node sink) { isSink(sink, _, _, _) }
@@ -283,9 +245,11 @@ where
283245
isInsecureTlsVersionFlow(source, sink, message, _) or
284246
isInsecureTlsCipherFlow(source, sink, message)
285247
) and
286-
// Exclude sinks guarded by a feature flag
287-
not getAFeatureFlagCheck().dominatesNode(sink.getNode().asInstruction()) and
288-
not getALegacyVersionCheck().dominatesNode(sink.getNode().asInstruction()) and
248+
// Exclude sources or sinks guarded by a feature or legacy flag
249+
not [getAFeatureFlagCheck(), getALegacyVersionCheck()]
250+
.dominatesNode([source, sink].getNode().asInstruction()) and
251+
// Exclude sources or sinks that occur lexically within a block related to a feature or legacy flag
252+
not astNodeIsFlag([source, sink].getNode().asExpr().getParent*(), [featureFlag(), legacyFlag()]) and
289253
// Exclude results in functions whose name documents insecurity
290254
not exists(FuncDef fn | fn = sink.getNode().asInstruction().getRoot() |
291255
isFeatureFlagName(fn.getEnclosingFunction*().getName()) or

ql/src/semmle/go/security/InsecureFeatureFlag.qll

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,39 @@ module InsecureFeatureFlag {
115115
)
116116
}
117117

118+
/**
119+
* Holds of string literals or named values matching `flagKind` and their fields.
120+
*/
121+
predicate exprIsFlag(Expr node, FlagKind flagKind) {
122+
node.getStringValue() = flagKind.getAFlagName() or
123+
node.(Name).getTarget().getName() = flagKind.getAFlagName() or
124+
exprIsFlag(node.(SelectorExpr).getBase(), flagKind) or
125+
exprIsFlag(node.(SelectorExpr).getSelector(), flagKind)
126+
}
127+
128+
/**
129+
* Holds if `node` suggests an old TLS version according to `flagKind`.
130+
*/
131+
predicate astNodeIsFlag(AstNode node, FlagKind flagKind) {
132+
// Map literal flag: value or "flag": value
133+
exprIsFlag(node.(KeyValueExpr).getKey(), flagKind)
134+
or
135+
// Variable initialisation flag := value
136+
exists(ValueSpec valueSpec, int childIdx |
137+
valueSpec.getName(childIdx) = flagKind.getAFlagName()
138+
|
139+
node = valueSpec.getInit(childIdx)
140+
)
141+
or
142+
// Assignment flag = value
143+
exists(Assignment assignment, int childIdx | exprIsFlag(assignment.getLhs(childIdx), flagKind) |
144+
node = assignment.getRhs(childIdx)
145+
)
146+
or
147+
// Case clause 'case flag:' or 'case "flag":'
148+
exprIsFlag(node.(CaseClause).getAnExpr(), flagKind)
149+
}
150+
118151
/**
119152
* Gets a control-flow node that represents a (likely) feature-flag check for certificate checking.
120153
*/

0 commit comments

Comments
 (0)