"""Both update requests must reject an untrusted TLS peer before trusting its metadata or bytes.""" import http.server import json import os from pathlib import Path import ssl import subprocess import sys import tempfile import threading binary = Path(sys.argv[1]).resolve() work = Path(tempfile.mkdtemp(prefix='koh-update-tls-')) subprocess.run(['openssl', 'req', '-x509', '-newkey', 'rsa:2048', '-nodes', '-days', '1', '-subj', '/CN=localhost', '-addext', 'subjectAltName=IP:127.0.0.1', '-keyout', str(work / 'key.pem'), '-out', str(work / 'cert.pem')], check=True, capture_output=True) platforms = ['macos-aarch64', 'macos-x86_64', 'linux-aarch64', 'linux-x86_64'] class Handler(http.server.BaseHTTPRequestHandler): def do_GET(self): body = json.dumps({'version': '999.0.0', 'platforms': {p: {'url': tls_url, 'blake3': '0' * 64} for p in platforms}}).encode() self.send_response(200) self.send_header('Content-Length', str(len(body))) self.end_headers() self.wfile.write(body) def log_message(self, *_): pass secure = http.server.ThreadingHTTPServer(('127.0.0.1', 0), Handler) context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) context.load_cert_chain(work / 'cert.pem', work / 'key.pem') secure.socket = context.wrap_socket(secure.socket, server_side=True) tls_url = f'https://127.0.0.1:{secure.server_port}/release' plain = http.server.ThreadingHTTPServer(('127.0.0.1', 0), Handler) for server in (secure, plain): threading.Thread(target=server.serve_forever, daemon=True).start() try: trusted = dict(os.environ, HOME=str(work), KOH_UPDATE_SOURCE=tls_url, SSL_CERT_FILE=str(work / 'cert.pem')) good = subprocess.run([str(binary), 'update', '--check', '--json'], env=trusted, capture_output=True, text=True, timeout=30) assert good.returncode == 1 and json.loads(good.stdout)['latest'] == '999.0.0', (good.stdout, good.stderr) for url, args, message in [(tls_url, ['--check', '--json'], 'could not reach'), (f'http://127.0.0.1:{plain.server_port}/release', ['--yes', '--json'], 'could not download')]: env = dict(os.environ, HOME=str(work), KOH_UPDATE_SOURCE=url) result = subprocess.run([str(binary), 'update', *args], env=env, capture_output=True, text=True, timeout=30) assert result.returncode == 1 and message in result.stderr, (result.stdout, result.stderr) for args in [['remote', f'https://127.0.0.1:{secure.server_port}', '--code', 'x', '--json'], ['steal', f'https://127.0.0.1:{secure.server_port}/nina', '--json'], ['remote', 's3', tls_url, 'test', 'test', '--json']]: isolated = dict(os.environ, HOME=str(work), KOH_ACCESS_KEY='test', KOH_SECRET_KEY='test') isolated.pop('SSL_CERT_FILE', None) result = subprocess.run([str(binary), *args], cwd=work, env=isolated, capture_output=True, text=True, timeout=45) assert result.returncode == 1 and 'handshake' in result.stderr, (args, result.stdout, result.stderr) print('PASS: trusted TLS accepted; metadata, binaries, connect, steal and S3 reject self-signed TLS') finally: secure.shutdown() plain.shutdown()