Always use our custom ssl.match_hostname() implementation
Python's implementation still accepts commonName and we don't want that behavior.
MODIFY
src/urllib3/packages/ssl_match_hostname/__init__.py
src/urllib3/packages/ssl_match_hostname/__init__.py
-import sys
+from ._implementation import CertificateError, match_hostname
-try:
- # Our match_hostname function is the same as 3.5's, so we only want to
- # import the match_hostname function if it's at least that good.
- from ssl import CertificateError, match_hostname
-except ImportError:
- try:
- # Backport of the function from a pypi module
- from backports.ssl_match_hostname import ( # type: ignore
- CertificateError,
- match_hostname,
- )
- except ImportError:
- # Our vendored copy
- from ._implementation import CertificateError, match_hostname # type: ignore
-
-# Not needed, but documenting what we provide.
__all__ = ("CertificateError", "match_hostname")

DELETE
src/urllib3/packages/ssl_match_hostname/__init__.pyi
-import ssl
-
-CertificateError = ssl.CertificateError
-match_hostname = ssl.match_hostname

MODIFY
test/with_dummyserver/test_https.py
test/with_dummyserver/test_https.py
@@ -779,6 +779,18 @@ class TestHTTPS_TLSv1_3(TestHTTPS):
certs = TLSv1_3_CERTS
+class TestHTTPS_NoSAN:
+ def test_common_name_without_san_fails(self, no_san_server):
+ with HTTPSConnectionPool(
+ no_san_server.host,
+ no_san_server.port,
+ cert_reqs="CERT_REQUIRED",
+ ca_certs=no_san_server.ca_certs,
+ ) as https_pool:
+ with pytest.raises(MaxRetryError, match="no appropriate subjectAltName"):
+ https_pool.request("GET", "/")
+
+
class TestHTTPS_IPSAN:
def test_can_validate_ip_san(self, ip_san_server):
"""Ensure that urllib3 can validate SANs with IP addresses in them."""

Always use our custom ssl.match_hostname() implementation

Python's implementation still accepts commonName and we don't want that behavior.