summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEli Schwartz <eschwartz93@gmail.com>2025-10-16 02:35:40 -0400
committerEli Schwartz <eschwartz93@gmail.com>2025-10-31 11:31:41 -0400
commit85a5abacfc4de902638d9e3f2ddc0d26ce9cc433 (patch)
tree85d96f61e04f42795ac8605924c00ec314cdbca2
parent8b74f795e89a9ac6454428745a75a56e7361f91c (diff)
downloadmeson-85a5abacfc4de902638d9e3f2ddc0d26ce9cc433.tar.gz
wrap: automatically utilize truststore, if it is installed
https://truststore.readthedocs.io/ This library allows patching the stdlib ssl module to respect system certificates, which is a common source of "broken internet" for Windows users especially. Fixes: https://github.com/mesonbuild/meson/issues/15121
-rw-r--r--.github/workflows/lint.yml2
-rw-r--r--mesonbuild/wrap/wrap.py28
2 files changed, 26 insertions, 4 deletions
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index ef5889579..d9a7836c5 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -53,7 +53,7 @@ jobs:
with:
python-version: '3.x'
# Pin mypy to version 1.8, so we retain the ability to lint for Python 3.7
- - run: python -m pip install "mypy==1.8" strictyaml types-PyYAML types-tqdm types-chevron
+ - run: python -m pip install "mypy==1.8" strictyaml truststore types-PyYAML types-tqdm types-chevron
- run: python run_mypy.py --allver
env:
PYTHONUNBUFFERED: 1
diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py
index e6f390a5d..c675389aa 100644
--- a/mesonbuild/wrap/wrap.py
+++ b/mesonbuild/wrap/wrap.py
@@ -77,6 +77,23 @@ else:
PATCH = shutil.which('patch')
+truststore_message = '''
+
+ If you believe the connection should be secure, but python cannot see the
+ correct SSL certificates, install https://truststore.readthedocs.io/ and
+ try again.'''
+
+@lru_cache(maxsize=None)
+def ssl_truststore() -> T.Optional[ssl.SSLContext]:
+ """ Provide a default context=None for urlopen, but use truststore if installed. """
+ try:
+ import truststore
+ except ImportError:
+ # use default
+ return None
+ else:
+ return truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
+
def whitelist_wrapdb(urlstr: str) -> urllib.parse.ParseResult:
""" raises WrapException if not whitelisted subdomain """
url = urllib.parse.urlparse(urlstr)
@@ -99,7 +116,7 @@ def open_wrapdburl(urlstring: str, allow_insecure: bool = False, have_opt: bool
if allow_compression:
headers['Accept-Encoding'] = 'gzip'
req = urllib.request.Request(urllib.parse.urlunparse(url), headers=headers)
- return T.cast('http.client.HTTPResponse', urllib.request.urlopen(req, timeout=REQ_TIMEOUT))
+ return T.cast('http.client.HTTPResponse', urllib.request.urlopen(req, timeout=REQ_TIMEOUT, context=ssl_truststore()))
url = whitelist_wrapdb(urlstring)
if has_ssl:
@@ -110,6 +127,8 @@ def open_wrapdburl(urlstring: str, allow_insecure: bool = False, have_opt: bool
if isinstance(excp, urllib.error.URLError) and isinstance(excp.reason, ssl.SSLCertVerificationError):
if allow_insecure:
mlog.warning(f'{msg}\n\n Proceeding without authentication.')
+ elif ssl_truststore() is None:
+ raise WrapException(f'{msg}{insecure_msg}{truststore_message}')
else:
raise WrapException(f'{msg}{insecure_msg}')
else:
@@ -794,10 +813,13 @@ class Resolver:
try:
req = urllib.request.Request(urlstring, headers=headers)
- resp = urllib.request.urlopen(req, timeout=REQ_TIMEOUT)
+ resp = urllib.request.urlopen(req, timeout=REQ_TIMEOUT, context=ssl_truststore())
except OSError as e:
mlog.log(str(e))
- raise WrapException(f'could not get {urlstring}; is the internet available?')
+ if isinstance(e, urllib.error.URLError) and isinstance(e.reason, ssl.SSLCertVerificationError) and ssl_truststore() is None:
+ raise WrapException(f'could not get {urlstring}; is the internet available?{truststore_message}')
+ else:
+ raise WrapException(f'could not get {urlstring}; is the internet available?')
with contextlib.closing(resp) as resp, tmpfile as tmpfile:
try:
dlsize = int(resp.info()['Content-Length'])