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

Commit b057cbe

Browse files
authored
Merge pull request #256 from smowton/smowton/admin/cwe-327-cleanup
Polish CWE-327 (weak TLS config) query
2 parents 5de55d0 + 2a7754a commit b057cbe

File tree

16 files changed

+1099
-568
lines changed

16 files changed

+1099
-568
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
lgtm,codescanning
2+
* Query "Insecure TLS configuration" (`go/insecure-tls`) is promoted from experimental status. This checks for use of insecure SSL/TLS versions and cipher suites.

ql/src/InconsistentCode/MissingErrorCheck.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ predicate returnUncheckedAtNode(
9090
err.getAPredecessor() = call.getResult(1) and
9191
call.asInstruction() = node and
9292
isDereferenceableType(ptr.getType()) and
93-
err.getType().implements(Builtin::error().getType().getUnderlyingType()) and
93+
err.getType() instanceof ErrorType and
9494
calleeMayReturnNilWithError(call)
9595
or
9696
// Recursive case: check that some predecessor is missing a check, and `node` does not itself

ql/src/Security/CWE-295/DisabledCertificateCheck.ql

Lines changed: 39 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -22,81 +22,55 @@
2222
*/
2323

2424
import go
25+
import semmle.go.security.InsecureFeatureFlag::InsecureFeatureFlag
2526

2627
/**
27-
* Holds if `name` may be the name of a feature flag that controls whether certificate checking is
28-
* enabled.
28+
* Holds if `part` becomes a part of `whole`, either by (local) data flow or by being incorporated
29+
* into `whole` through having its address taken or being written to a field of `whole`.
2930
*/
30-
bindingset[name]
31-
predicate isFeatureFlagName(string name) {
32-
name.regexpMatch("(?i).*(secure|selfCert|selfSign|validat|verif|trust|(en|dis)able).*")
33-
}
34-
35-
/** Gets a global value number representing a (likely) feature flag for certificate checking. */
36-
GVN getAFeatureFlag() {
37-
// a call like `cfg.disableVerification()`
38-
exists(DataFlow::CallNode c | isFeatureFlagName(c.getTarget().getName()) |
39-
result = globalValueNumber(c)
40-
)
41-
or
42-
// a variable or field like `insecure`
43-
exists(ValueEntity flag | isFeatureFlagName(flag.getName()) |
44-
result = globalValueNumber(flag.getARead())
45-
)
31+
predicate becomesPartOf(DataFlow::Node part, DataFlow::Node whole) {
32+
DataFlow::localFlow(part, whole)
4633
or
47-
// a string constant such as `"insecure"` or `"skipVerification"`
48-
exists(DataFlow::Node const | isFeatureFlagName(const.getStringValue()) |
49-
result = globalValueNumber(const)
50-
)
34+
whole.(DataFlow::AddressOperationNode).getOperand() = part
5135
or
52-
// track feature flags through various operations
53-
exists(DataFlow::Node flag | flag = getAFeatureFlag().getANode() |
54-
// tuple destructurings
55-
result = globalValueNumber(DataFlow::extractTupleElement(flag, _))
56-
or
57-
// type casts
58-
exists(DataFlow::TypeCastNode tc |
59-
tc.getOperand() = flag and
60-
result = globalValueNumber(tc)
61-
)
62-
or
63-
// pointer dereferences
64-
exists(DataFlow::PointerDereferenceNode deref |
65-
deref.getOperand() = flag and
66-
result = globalValueNumber(deref)
67-
)
68-
or
69-
// calls like `os.Getenv("DISABLE_TLS_VERIFICATION")`
70-
exists(DataFlow::CallNode call |
71-
call.getAnArgument() = flag and
72-
result = globalValueNumber(call)
73-
)
74-
or
75-
// comparisons like `insecure == true`
76-
exists(DataFlow::EqualityTestNode eq |
77-
eq.getAnOperand() = flag and
78-
result = globalValueNumber(eq)
79-
)
80-
)
36+
exists(Write w | w.writesField(whole.(DataFlow::PostUpdateNode).getPreUpdateNode(), _, part))
8137
}
8238

8339
/**
84-
* Gets a control-flow node that represents a (likely) feature-flag check for certificate checking.
40+
* Flags suggesting a deliberately insecure certificate setup.
8541
*/
86-
ControlFlow::ConditionGuardNode getAFeatureFlagCheck() {
87-
result.ensures(getAFeatureFlag().getANode(), _)
42+
class InsecureCertificateFlag extends FlagKind {
43+
InsecureCertificateFlag() { this = "insecureCertificate" }
44+
45+
bindingset[result]
46+
override string getAFlagName() {
47+
result.regexpMatch("(?i).*(selfCert|selfSign|validat|verif|trust).*")
48+
}
8849
}
8950

9051
/**
91-
* Holds if `part` becomes a part of `whole`, either by (local) data flow or by being incorporated
92-
* into `whole` through having its address taken or being written to a field of `whole`.
52+
* Gets a control-flow node that represents a (likely) flag controlling an insecure certificate setup.
9353
*/
94-
predicate becomesPartOf(DataFlow::Node part, DataFlow::Node whole) {
95-
DataFlow::localFlow(part, whole)
96-
or
97-
whole.(DataFlow::AddressOperationNode).getOperand() = part
98-
or
99-
exists(Write w | w.writesField(whole.(DataFlow::PostUpdateNode).getPreUpdateNode(), _, part))
54+
ControlFlow::ConditionGuardNode getAnInsecureCertificateCheck() {
55+
result.ensures(any(InsecureCertificateFlag f).getAFlag().getANode(), _)
56+
}
57+
58+
/**
59+
* Returns flag kinds relevant to this query: a generic security feature flag, or one
60+
* specifically controlling insecure certificate configuration.
61+
*/
62+
FlagKind securityOrTlsVersionFlag() {
63+
result = any(SecurityFeatureFlag f) or
64+
result = any(InsecureCertificateFlag f)
65+
}
66+
67+
/**
68+
* Holds if `name` is (likely to be) a general security flag or one specifically controlling
69+
* an insecure certificate setup.
70+
*/
71+
bindingset[name]
72+
predicate isSecurityOrCertificateConfigFlag(string name) {
73+
name = securityOrTlsVersionFlag().getAFlagName()
10074
}
10175

10276
from Write w, DataFlow::Node base, Field f, DataFlow::Node rhs
@@ -105,14 +79,14 @@ where
10579
f.hasQualifiedName("crypto/tls", "Config", "InsecureSkipVerify") and
10680
rhs.getBoolValue() = true and
10781
// exclude writes guarded by a feature flag
108-
not getAFeatureFlagCheck().dominatesNode(w) and
82+
not [getASecurityFeatureFlagCheck(), getAnInsecureCertificateCheck()].dominatesNode(w) and
10983
// exclude results in functions whose name documents the insecurity
11084
not exists(FuncDef fn | fn = w.getRoot() |
111-
isFeatureFlagName(fn.getEnclosingFunction*().getName())
85+
isSecurityOrCertificateConfigFlag(fn.getEnclosingFunction*().getName())
11286
) and
11387
// exclude results that flow into a field/variable whose name documents the insecurity
11488
not exists(ValueEntity e, DataFlow::Node init |
115-
isFeatureFlagName(e.getName()) and
89+
isSecurityOrCertificateConfigFlag(e.getName()) and
11690
any(Write w2).writes(e, init) and
11791
becomesPartOf*(base, init)
11892
) and

ql/src/experimental/CWE-327/InsecureTLS.qhelp renamed to ql/src/Security/CWE-327/InsecureTLS.qhelp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@
3939
Wikipedia:
4040
<a href="https://en.wikipedia.org/wiki/Transport_Layer_Security">Transport Layer Security</a>
4141
</li>
42+
<li>
43+
Mozilla:
44+
<a href="https://wiki.mozilla.org/Security/Server_Side_TLS">Security/Server Side TLS</a>
45+
</li>
4246
<li>
4347
OWASP:
4448
<a href="https://cheatsheetseries.owasp.org/cheatsheets/Transport_Layer_Protection_Cheat_Sheet.html">Transport Layer Protection Cheat Sheet</a>

0 commit comments

Comments
 (0)