Add SSL certificate check functionality and UI integration

This commit is contained in:
Ross
2025-11-24 15:59:01 +00:00
parent 796027a971
commit 9c7777323d
+70 -3
View File
@@ -39,6 +39,9 @@ from starlette.middleware.base import BaseHTTPMiddleware
import typer
import logging
import subprocess
import shutil
from urllib.parse import urlparse
import socket
import platform
import importlib.util
@@ -393,6 +396,46 @@ def clear_anonymized_files():
return
def ssl_check_cmd(url: str) -> str:
"""Run openssl s_client -showcerts against the given URL host and return output.
Falls back to a minimal Python-based peer certificate fetch if openssl is not available.
"""
try:
parsed = urlparse(url)
host = parsed.hostname or url
port = parsed.port or (443 if parsed.scheme in ("https", "") else 80)
except Exception:
host = url
port = 443
openssl_path = shutil.which("openssl")
if openssl_path:
cmd = [openssl_path, "s_client", "-showcerts", "-connect", f"{host}:{port}", "-servername", host]
try:
proc = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
out = proc.stdout + "\n" + proc.stderr
return out
except Exception as e:
return f"OpenSSL check failed: {e}\n"
else:
# Fallback: use Python ssl to fetch the peer certificate (leaf only)
import ssl
try:
ctx = ssl.create_default_context()
with ctx.wrap_socket(
socket.socket(socket.AF_INET), server_hostname=host
) as s:
s.settimeout(10)
s.connect((host, port))
der = s.getpeercert(True)
pem = ssl.DER_cert_to_PEM_cert(der)
return pem
except Exception as e:
return f"Python SSL fallback failed: {e}\n"
def upload_files(q, rqst):
# global rqst#, uploaded_files
@@ -681,6 +724,16 @@ def main_page():
ui.button("Clear anon path", on_click=clear_anonymized_files).tooltip(
"Delete all files in ANON_PATH"
)
async def ssl_check_ui() -> None:
# Run the openssl/Python SSL check in a thread and show output in a dialog
output = await run.cpu_bound(ssl_check_cmd, BASE_SITE_URL)
with ui.dialog() as d:
with ui.card().classes("p-4"):
ui.label("SSL Check output").classes("text-h6")
ui.code(output).props("white-space: pre-wrap")
ui.button("Close", on_click=d.close)
ui.button("SSL check", on_click=ssl_check_ui).tooltip("Run openssl s_client -showcerts against the configured site and show output")
def shutdown_app():
app.shutdown()
@@ -790,13 +843,26 @@ def launch_app(
if files:
# Create a temporary combined CA bundle file (persisted)
tmp = tempfile.NamedTemporaryFile(delete=False, prefix="nice_uploader_ca_", suffix=".pem")
included = []
try:
import base64
for f in files:
try:
with open(f, "rb") as fh:
tmp.write(fh.read())
# Ensure certs are separated
tmp.write(b"\n")
data = fh.read()
# If the file already contains a PEM block, write as-is
if b"-----BEGIN CERTIFICATE-----" in data:
tmp.write(data)
tmp.write(b"\n")
included.append(str(f))
else:
# Assume DER/binary and convert to PEM by base64-encoding
b64 = base64.encodebytes(data)
tmp.write(b"-----BEGIN CERTIFICATE-----\n")
tmp.write(b64)
tmp.write(b"-----END CERTIFICATE-----\n")
included.append(str(f) + " (converted from DER)")
except Exception:
# skip unreadable files
logger.debug(f"Skipping unreadable cert file: {f}")
@@ -804,6 +870,7 @@ def launch_app(
tmp.close()
VERIFY_SSL = str(Path(tmp.name))
logger.info(f"Using bundled CA bundle dir combined to {VERIFY_SSL}")
logger.debug(f"Bundled cert files: {included}")
except Exception as e:
try:
tmp.close()