From 005787920d855cb071bc6c72949b9e14aba4b9f1 Mon Sep 17 00:00:00 2001 From: Erni Durdevic Date: Thu, 18 Jun 2026 15:46:20 +0200 Subject: [PATCH] Fix SSL cert verification failures behind enterprise inspection proxies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Python's urllib does not load the OS trust store on all platforms, so workspaces behind a corporate TLS inspection proxy (which injects a self-signed CA) fail with CERTIFICATE_VERIFY_FAILED even though curl succeeds. Honor REQUESTS_CA_BUNDLE, CURL_CA_BUNDLE, and SSL_CERT_FILE — the same env vars respected by curl and the requests library — so customers can point ucode at their enterprise CA bundle. Co-authored-by: Isaac --- src/ucode/databricks.py | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/ucode/databricks.py b/src/ucode/databricks.py index 574d906..c2cc15f 100644 --- a/src/ucode/databricks.py +++ b/src/ucode/databricks.py @@ -13,6 +13,7 @@ import re import shlex import shutil +import ssl import subprocess from pathlib import Path from typing import Literal, cast, overload @@ -194,6 +195,27 @@ def _log_auth_diagnostics() -> None: _debug(f"databrickscfg ({cfg_path})", f"read error: {exc}") +def _make_ssl_context() -> ssl.SSLContext: + """Return an SSL context that trusts the system CA bundle plus any custom CA + pointed to by REQUESTS_CA_BUNDLE or CURL_CA_BUNDLE. + + Enterprise environments often inject a self-signed certificate via an SSL + inspection proxy. curl picks it up from the system store automatically; + Python's default ssl context doesn't on all platforms. Honoring the same + env vars that curl and the `requests` library use lets customers point ucode + at their enterprise CA bundle without patching the system Python install.""" + ctx = ssl.create_default_context() + for env_var in ("REQUESTS_CA_BUNDLE", "CURL_CA_BUNDLE", "SSL_CERT_FILE"): + ca_bundle = os.environ.get(env_var, "").strip() + if ca_bundle and Path(ca_bundle).is_file(): + try: + ctx.load_verify_locations(cafile=ca_bundle) + except ssl.SSLError: + pass + break + return ctx + + def _http_get_json( url: str, token: str, *, timeout: int = 10 ) -> tuple[dict | list | None, str | None]: @@ -206,7 +228,9 @@ def _http_get_json( headers={"Authorization": f"Bearer {token}", "Accept": "application/json"}, ) try: - with urllib_request.urlopen(request, timeout=timeout) as response: + with urllib_request.urlopen( + request, timeout=timeout, context=_make_ssl_context() + ) as response: body = response.read().decode("utf-8") _debug(f"GET {url}", f"HTTP 200, {len(body)} bytes") if _debug_enabled(): @@ -253,7 +277,9 @@ def _http_post_json( }, ) try: - with urllib_request.urlopen(request, timeout=timeout) as response: + with urllib_request.urlopen( + request, timeout=timeout, context=_make_ssl_context() + ) as response: body = response.read().decode("utf-8") _debug(f"POST {url}", f"HTTP {response.status}, {len(body)} bytes") if _debug_enabled(): @@ -1466,7 +1492,7 @@ def discover_sql_warehouse_http_path( ) try: - with urllib_request.urlopen(request, timeout=20) as response: + with urllib_request.urlopen(request, timeout=20, context=_make_ssl_context()) as response: payload = json.loads(response.read().decode("utf-8")) except urllib_error.HTTPError as exc: body = exc.read().decode("utf-8", errors="replace") if exc.fp else ""