Skip to content

Commit 5b2d799

Browse files
committed
Python: Model certificate disabling in urllib3
1 parent 0d02ca0 commit 5b2d799

File tree

2 files changed

+49
-7
lines changed
  • python/ql

2 files changed

+49
-7
lines changed

python/ql/lib/semmle/python/frameworks/Urllib3.qll

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,20 +42,18 @@ private module Urllib3 {
4242
.getASubclass+()
4343
}
4444

45-
/** Gets a reference to an instance of a `urllib3.request.RequestMethods` subclass. */
46-
private API::Node instance() { result = classRef().getReturn() }
47-
4845
/**
4946
* A call to a method making an outgoing request.
5047
*
5148
* See
5249
* - https://urllib3.readthedocs.io/en/stable/reference/urllib3.request.html#urllib3.request.RequestMethods
5350
* - https://urllib3.readthedocs.io/en/stable/reference/urllib3.connectionpool.html#urllib3.HTTPConnectionPool.urlopen
5451
*/
55-
private class RequestCall extends HTTP::Client::Request::Range, DataFlow::CallCfgNode {
52+
private class RequestCall extends HTTP::Client::Request::Range, API::CallNode {
5653
RequestCall() {
5754
this =
58-
instance()
55+
classRef()
56+
.getReturn()
5957
.getMember(["request", "request_encode_url", "request_encode_body", "urlopen"])
6058
.getACall()
6159
}
@@ -67,8 +65,22 @@ private module Urllib3 {
6765
override predicate disablesCertificateValidation(
6866
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
6967
) {
70-
// TODO: Look into disabling certificate validation
71-
none()
68+
exists(API::CallNode constructor |
69+
constructor = classRef().getACall() and
70+
this = constructor.getReturn().getAMember().getACall()
71+
|
72+
// cert_reqs
73+
// see https://urllib3.readthedocs.io/en/stable/user-guide.html?highlight=cert_reqs#certificate-verification
74+
disablingNode = constructor.getKeywordParameter("cert_reqs").getARhs() and
75+
argumentOrigin = constructor.getKeywordParameter("cert_reqs").getAValueReachingRhs() and
76+
argumentOrigin.asExpr().(StrConst).getText() = "CERT_NONE"
77+
or
78+
// assert_hostname
79+
// see https://urllib3.readthedocs.io/en/stable/reference/urllib3.connectionpool.html?highlight=assert_hostname#urllib3.HTTPSConnectionPool
80+
disablingNode = constructor.getKeywordParameter("assert_hostname").getARhs() and
81+
argumentOrigin = constructor.getKeywordParameter("assert_hostname").getAValueReachingRhs() and
82+
argumentOrigin.asExpr().(BooleanLiteral).booleanValue() = false
83+
)
7284
}
7385
}
7486
}

python/ql/test/library-tests/frameworks/urllib3/test.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,33 @@
2727
resp = pool.request("method", url="url") # $ clientRequestUrlPart="url"
2828
resp = pool.urlopen("method", "url") # $ clientRequestUrlPart="url"
2929
resp = pool.urlopen("method", url="url") # $ clientRequestUrlPart="url"
30+
31+
# ==============================================================================
32+
# Certificate validation disabled
33+
# ==============================================================================
34+
35+
# see https://docs.python.org/3.10/library/ssl.html#ssl.CERT_NONE
36+
pool = urllib3.HTTPSConnectionPool("host", cert_reqs='CERT_NONE')
37+
resp = pool.request("method", "url") # $ clientRequestUrlPart="url" clientRequestCertValidationDisabled
38+
39+
pool = urllib3.HTTPSConnectionPool("host", assert_hostname=False)
40+
resp = pool.request("method", "url") # $ clientRequestUrlPart="url" clientRequestCertValidationDisabled
41+
42+
pool = urllib3.HTTPSConnectionPool("host", cert_reqs='CERT_NONE', assert_hostname=False)
43+
resp = pool.request("method", "url") # $ clientRequestUrlPart="url" clientRequestCertValidationDisabled
44+
45+
46+
# same with PoolManager
47+
pool = urllib3.PoolManager(cert_reqs='CERT_NONE')
48+
resp = pool.request("method", "url") # $ clientRequestUrlPart="url" clientRequestCertValidationDisabled
49+
50+
pool = urllib3.PoolManager(assert_hostname=False)
51+
resp = pool.request("method", "url") # $ clientRequestUrlPart="url" clientRequestCertValidationDisabled
52+
53+
54+
# and same with ProxyManager
55+
pool = urllib3.ProxyManager("https://proxy", cert_reqs='CERT_NONE')
56+
resp = pool.request("method", "url") # $ clientRequestUrlPart="url" clientRequestCertValidationDisabled
57+
58+
pool = urllib3.ProxyManager("https://proxy", assert_hostname=False)
59+
resp = pool.request("method", "url") # $ clientRequestUrlPart="url" clientRequestCertValidationDisabled

0 commit comments

Comments
 (0)