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

Commit 7d294c5

Browse files
committed
Factor and generalise InsecureFeatureFlag
The same path is now used to classify flags relating to old/legacy versions.
1 parent 34c8cc5 commit 7d294c5

File tree

2 files changed

+76
-25
lines changed

2 files changed

+76
-25
lines changed

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

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* man-in-the-middle and other attacks.
55
* @kind path-problem
66
* @problem.severity warning
7+
* @precision very-high
78
* @id go/insecure-tls
89
* @tags security
910
* external/cwe/cwe-327
@@ -13,17 +14,6 @@ import go
1314
import DataFlow::PathGraph
1415
import semmle.go.security.InsecureFeatureFlag::InsecureFeatureFlag
1516

16-
/**
17-
* Holds if `name` suggests an old or legacy version.
18-
*
19-
* We accept 'intermediate' because it appears to be common for TLS users
20-
* to define three profiles: modern, intermediate, legacy/old, perhaps based
21-
* on https://wiki.mozilla.org/Security/Server_Side_TLS (though note the
22-
* 'intermediate' used there would now pass muster according to this query)
23-
*/
24-
bindingset[name]
25-
predicate isOldVersionName(string name) { name.regexpMatch("(?i).*(old|intermediate|legacy).*") }
26-
2717
/**
2818
* Check whether the file where the node is located is a test file.
2919
*/
@@ -56,28 +46,28 @@ predicate isInsecureTlsVersion(int val, string name, string fieldName) {
5646
}
5747

5848
/**
59-
* Holds of string literals or named constants matching `isOldVersionName`
49+
* Holds of string literals or named constants matching `isLegacyFlagName`
6050
*/
6151
predicate exprSuggestsOldVersion(Expr node) {
62-
isOldVersionName(node.getStringValue()) or
63-
isOldVersionName(node.(Name).getTarget().getName())
52+
isLegacyFlagName(node.getStringValue()) or
53+
isLegacyFlagName(node.(Name).getTarget().getName())
6454
}
6555

6656
/**
67-
* Holds if `node` suggests an old TLS version according to `isOldVersionName`
57+
* Holds if `node` suggests an old TLS version according to `isLegacyFlagName`
6858
*/
6959
predicate nodeSuggestsOldVersion(AstNode node) {
7060
// Map literal old: value or "old": value
7161
exprSuggestsOldVersion(node.(KeyValueExpr).getKey())
7262
or
7363
// Variable initialisation old := value
74-
exists(ValueSpec valueSpec, int childIdx | isOldVersionName(valueSpec.getName(childIdx)) |
64+
exists(ValueSpec valueSpec, int childIdx | isLegacyFlagName(valueSpec.getName(childIdx)) |
7565
node = valueSpec.getInit(childIdx)
7666
)
7767
or
7868
// Assignment old = value
7969
exists(Assignment assignment, int childIdx |
80-
isOldVersionName(assignment.getLhs(childIdx).(Ident).getName())
70+
isLegacyFlagName(assignment.getLhs(childIdx).(Ident).getName())
8171
|
8272
node = assignment.getRhs(childIdx)
8373
)
@@ -295,10 +285,11 @@ where
295285
) and
296286
// Exclude sinks guarded by a feature flag
297287
not getAFeatureFlagCheck().dominatesNode(sink.getNode().asInstruction()) and
288+
not getALegacyVersionCheck().dominatesNode(sink.getNode().asInstruction()) and
298289
// Exclude results in functions whose name documents insecurity
299290
not exists(FuncDef fn | fn = sink.getNode().asInstruction().getRoot() |
300291
isFeatureFlagName(fn.getEnclosingFunction*().getName()) or
301-
isOldVersionName(fn.getEnclosingFunction*().getName())
292+
isLegacyFlagName(fn.getEnclosingFunction*().getName())
302293
) and
303294
// Exclude results in test code:
304295
not isTestFile(sink.getNode())

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

Lines changed: 67 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,78 @@ module InsecureFeatureFlag {
1414
name.regexpMatch("(?i).*(secure|selfCert|selfSign|validat|verif|trust|(en|dis)able).*")
1515
}
1616

17-
/** Gets a global value number representing a (likely) feature flag for certificate checking. */
18-
GVN getAFeatureFlag() {
17+
/**
18+
* Holds if `name` suggests an old or legacy version.
19+
*
20+
* We accept 'intermediate' because it appears to be common for TLS users
21+
* to define three profiles: modern, intermediate, legacy/old, perhaps based
22+
* on https://wiki.mozilla.org/Security/Server_Side_TLS (though note the
23+
* 'intermediate' used there would now pass muster according to this query)
24+
*/
25+
bindingset[name]
26+
predicate isLegacyFlagName(string name) { name.regexpMatch("(?i).*(old|intermediate|legacy).*") }
27+
28+
/**
29+
* A kind of flag that may indicate security expectations regarding the code it guards.
30+
*/
31+
abstract class FlagKind extends string {
32+
FlagKind() { this = "feature" or this = "legacy" }
33+
34+
/**
35+
* Returns a flag name of this type.
36+
*/
37+
abstract string getAFlagName();
38+
}
39+
40+
/**
41+
* Flags suggesting an optional feature, perhaps deliberately insecure.
42+
*/
43+
class FeatureFlag extends FlagKind {
44+
FeatureFlag() { this = "feature" }
45+
46+
bindingset[result]
47+
override string getAFlagName() { isFeatureFlagName(result) }
48+
}
49+
50+
/**
51+
* Flags suggesting an optional feature, perhaps deliberately insecure.
52+
*/
53+
string featureFlag() { result = "feature" }
54+
55+
/**
56+
* Flags suggesting support for an old or legacy feature.
57+
*/
58+
class LegacyFlag extends FlagKind {
59+
LegacyFlag() { this = "legacy" }
60+
61+
bindingset[result]
62+
override string getAFlagName() { isLegacyFlagName(result) }
63+
}
64+
65+
/**
66+
* Flags suggesting support for an old or legacy feature.
67+
*/
68+
string legacyFlag() { result = "legacy" }
69+
70+
/** Gets a global value number representing a (likely) security flag. */
71+
GVN getAFlag(FlagKind flagKind) {
1972
// a call like `cfg.disableVerification()`
20-
exists(DataFlow::CallNode c | isFeatureFlagName(c.getTarget().getName()) |
73+
exists(DataFlow::CallNode c | c.getTarget().getName() = flagKind.getAFlagName() |
2174
result = globalValueNumber(c)
2275
)
2376
or
2477
// a variable or field like `insecure`
25-
exists(ValueEntity flag | isFeatureFlagName(flag.getName()) |
78+
exists(ValueEntity flag | flag.getName() = flagKind.getAFlagName() |
2679
result = globalValueNumber(flag.getARead())
2780
)
2881
or
2982
// a string constant such as `"insecure"` or `"skipVerification"`
30-
exists(DataFlow::Node const | isFeatureFlagName(const.getStringValue()) |
83+
exists(DataFlow::Node const | const.getStringValue() = flagKind.getAFlagName() |
3184
result = globalValueNumber(const)
3285
)
3386
or
3487
// track feature flags through various operations
35-
exists(DataFlow::Node flag | flag = getAFeatureFlag().getANode() |
88+
exists(DataFlow::Node flag | flag = getAFlag(flagKind).getANode() |
3689
// tuple destructurings
3790
result = globalValueNumber(DataFlow::extractTupleElement(flag, _))
3891
or
@@ -66,6 +119,13 @@ module InsecureFeatureFlag {
66119
* Gets a control-flow node that represents a (likely) feature-flag check for certificate checking.
67120
*/
68121
ControlFlow::ConditionGuardNode getAFeatureFlagCheck() {
69-
result.ensures(getAFeatureFlag().getANode(), _)
122+
result.ensures(getAFlag(featureFlag()).getANode(), _)
123+
}
124+
125+
/**
126+
* Gets a control-flow node that represents a (likely) feature-flag check for certificate checking.
127+
*/
128+
ControlFlow::ConditionGuardNode getALegacyVersionCheck() {
129+
result.ensures(getAFlag(legacyFlag()).getANode(), _)
70130
}
71131
}

0 commit comments

Comments
 (0)