fix(mysql): repair preferred TLS fallback and clarify SSL failures

This commit is contained in:
ccq authored and GitHub committed 2026-10-24 16:29:24 +08:00
1 parent 1af2b62f94
commit a401aeb828
23 files changed
+469 -3

No files matched your search

Generated
+1
View File
@@ -2395,6 +2395,7 @@ dependencies = [
"mysql_common",
"percent-encoding",
"rust_decimal",
"rustls 0.23.43",
"serde_json",
"sqlparser",
"tokio",
+3
View File
@@ -812,6 +812,9 @@ export default withEnglishFallback({
mysqlClientKeyPlaceholder: "/path/to/client.key",
mysqlClientCertHint: "MySQL mTLS tələb etdikdə müştəri sertifikatı və məxfi açar birlikdə təqdim edilməlidir.",
mysqlTlsConnectionFailureHint: "MySQL serveri TLS tələb etmirsə, əlaqəni redaktə edin, TLS rejimini deaktiv edin və yenidən cəhd edin.",
mysqlUnsupportedTlsVersionHint:
"DBX-in daxili MySQL drayveri TLS 1.2/1.3 dəstəkləyir; bu server yalnız köhnə versiyaları dəstəkləyə bilər. Server TLS-ni yeniləyin və ya yenidən qurun, yaxud uyğun müştəri və ya TLS çevirici proksi istifadə edin. TLS yalnız hesabda REQUIRE SSL olmadıqda söndürülə bilər; əks halda MySQL 1045 qaytara bilər. Təkcə SSH, REQUIRE SSL tələbini ödəmir.",
mysqlDisabledTlsAccessDeniedHint: "TLS söndürüldükdə 1045 yalnız yanlış parol və ya icazələr deyil, hesabın SSL tələb etdiyini də göstərə bilər. İşləyən administrator sessiyasından hesabın SHOW GRANTS nəticəsini yoxlayın. Təkcə SSH, REQUIRE SSL tələbini ödəmir.",
mysqlUnsupportedCertVersionHint: "Ciddi yoxlama bu server sertifikatı versiyasını dəstəkləmir. Onu X.509 v3 sertifikatı ilə əvəz edin və ya yalnız etibarlı şəbəkədə məcburi rejimdən istifadə edin.",
mysqlClientCertBrowse: "Müştəri sertifikatını seçin",
mysqlClientKeyBrowse: "Müştərinin məxfi açarını seçin",
+3
View File
@@ -1513,6 +1513,9 @@ export default {
mysqlClientKeyPlaceholder: "/path/to/client.key",
mysqlClientCertHint: "Client certificate and private key must be provided together when MySQL requires mTLS.",
mysqlTlsConnectionFailureHint: "If the MySQL server does not require TLS, edit the connection, set TLS Mode to Disabled, and try again.",
mysqlUnsupportedTlsVersionHint:
"DBX's built-in MySQL driver supports TLS 1.2/1.3; this server may only support older TLS versions. Upgrade or reconfigure server TLS, or use a compatible client or TLS-converting proxy. Disabling TLS only works if the account does not REQUIRE SSL; otherwise MySQL may return 1045. SSH alone does not satisfy REQUIRE SSL.",
mysqlDisabledTlsAccessDeniedHint: "With TLS disabled, 1045 may mean the account requires SSL, not only an incorrect password or grants. From a working administrator session, check the account's SHOW GRANTS. SSH alone does not satisfy REQUIRE SSL.",
mysqlUnsupportedCertVersionHint: "Strict verification does not support this server certificate version. Replace it with an X.509 v3 certificate, or use Required mode only on a trusted network.",
mysqlClientCertBrowse: "Choose client certificate",
mysqlClientKeyBrowse: "Choose client private key",
+3
View File
@@ -1329,6 +1329,9 @@ export default withEnglishFallback({
mysqlClientKeyPlaceholder: "/ruta/a/client.key",
mysqlClientCertHint: "El certificado y la clave privada deben indicarse juntos si MySQL requiere mTLS.",
mysqlTlsConnectionFailureHint: "Si el servidor MySQL no requiere TLS, edita la conexión, establece el Modo TLS en Deshabilitado e inténtalo de nuevo.",
mysqlUnsupportedTlsVersionHint:
"El controlador MySQL integrado de DBX admite TLS 1.2/1.3; este servidor quizá solo admita versiones antiguas. Actualiza o reconfigura TLS en el servidor, o usa un cliente compatible o un proxy de conversión TLS. Solo se puede deshabilitar TLS si la cuenta no tiene REQUIRE SSL; de lo contrario, MySQL puede devolver 1045. SSH por sí solo no cumple REQUIRE SSL.",
mysqlDisabledTlsAccessDeniedHint: "Con TLS deshabilitado, 1045 puede indicar que la cuenta requiere SSL, además de una contraseña o permisos incorrectos. Comprueba SHOW GRANTS de la cuenta desde una sesión de administrador que funcione. SSH por sí solo no cumple REQUIRE SSL.",
mysqlUnsupportedCertVersionHint: "La verificación estricta no admite esta versión del certificado del servidor. Sustitúyelo por un certificado X.509 v3 o usa el modo Requerido solo en una red de confianza.",
mysqlClientCertBrowse: "Elegir certificado cliente",
mysqlClientKeyBrowse: "Elegir clave privada cliente",
+3
View File
@@ -1070,6 +1070,9 @@ export default withEnglishFallback({
mysqlClientKeyPlaceholder: "/path/to/client.key",
mysqlClientCertHint: "Sertifikat klien dan private key harus diberikan bersamaan saat MySQL memerlukan mTLS.",
mysqlTlsConnectionFailureHint: "Jika server MySQL tidak memerlukan TLS, edit koneksi, tetapkan Mode TLS ke Dinonaktifkan, lalu coba lagi.",
mysqlUnsupportedTlsVersionHint:
"Driver MySQL bawaan DBX mendukung TLS 1.2/1.3; server ini mungkin hanya mendukung versi lama. Tingkatkan atau konfigurasi ulang TLS server, atau gunakan klien kompatibel atau proksi konversi TLS. TLS hanya dapat dinonaktifkan jika akun tidak memiliki REQUIRE SSL; jika tidak, MySQL dapat mengembalikan 1045. SSH saja tidak memenuhi REQUIRE SSL.",
mysqlDisabledTlsAccessDeniedHint: "Saat TLS dinonaktifkan, 1045 dapat berarti akun memerlukan SSL, bukan hanya kata sandi atau izin yang salah. Periksa SHOW GRANTS akun dari sesi administrator yang berfungsi. SSH saja tidak memenuhi REQUIRE SSL.",
mysqlUnsupportedCertVersionHint: "Verifikasi ketat tidak mendukung versi sertifikat server ini. Gantilah dengan sertifikat X.509 v3, atau gunakan mode Wajib hanya pada jaringan tepercaya.",
mysqlClientCertBrowse: "Pilih sertifikat klien",
mysqlClientKeyBrowse: "Pilih private key klien",
+3
View File
@@ -1252,6 +1252,9 @@ export default withEnglishFallback({
mysqlClientKeyPlaceholder: "/percorso/per/client.key",
mysqlClientCertHint: "Il certificato client e la chiave privata devono essere forniti insieme quando MySQL richiede mTLS.",
mysqlTlsConnectionFailureHint: "Se il server MySQL non richiede TLS, modifica la connessione, imposta la modalità TLS su Disabilitata e riprova.",
mysqlUnsupportedTlsVersionHint:
"Il driver MySQL integrato di DBX supporta TLS 1.2/1.3; questo server potrebbe supportare solo versioni precedenti. Aggiorna o riconfigura TLS sul server, oppure usa un client compatibile o un proxy di conversione TLS. TLS può essere disabilitato solo se l'account non ha REQUIRE SSL; altrimenti MySQL può restituire 1045. SSH da solo non soddisfa REQUIRE SSL.",
mysqlDisabledTlsAccessDeniedHint: "Con TLS disabilitato, 1045 può indicare che l'account richiede SSL, oltre a una password o autorizzazioni errate. Controlla SHOW GRANTS dell'account da una sessione amministrativa funzionante. SSH da solo non soddisfa REQUIRE SSL.",
mysqlUnsupportedCertVersionHint: "La verifica rigorosa non supporta questa versione del certificato del server. Sostituiscilo con un certificato X.509 v3 oppure usa la modalità Obbligatoria solo su una rete attendibile.",
mysqlClientCertBrowse: "Scegli certificato client",
mysqlClientKeyBrowse: "Scegli chiave privata client",
+3
View File
@@ -1252,6 +1252,9 @@ export default withEnglishFallback({
mysqlClientKeyPlaceholder: "/path/to/client.key",
mysqlClientCertHint: "MySQLがmTLSを要求する場合、クライアント証明書と秘密鍵の両方を指定する必要があります。",
mysqlTlsConnectionFailureHint: "MySQLサーバーがTLSを要求しない場合は、接続を編集し、TLSモードを無効にしてから再試行してください。",
mysqlUnsupportedTlsVersionHint:
"DBX組み込みのMySQLドライバーはTLS 1.2/1.3をサポートします。このサーバーは古いTLSのみをサポートしている可能性があります。サーバーTLSを更新・再設定するか、互換クライアントまたはTLS変換プロキシを使用してください。TLSを無効にできるのはREQUIRE SSLが設定されていないアカウントのみです。それ以外は1045が返る場合があります。SSHだけではREQUIRE SSLを満たせません。",
mysqlDisabledTlsAccessDeniedHint: "TLSが無効の場合、1045はパスワードや権限の誤りだけでなく、アカウントのSSL要件を示す場合があります。接続可能な管理者セッションからアカウントのSHOW GRANTSを確認してください。SSHだけではREQUIRE SSLを満たせません。",
mysqlUnsupportedCertVersionHint: "厳密な検証では、このサーバー証明書のバージョンはサポートされていません。X.509 v3証明書に置き換えるか、信頼できるネットワークでのみ「必須」モードを使用してください。",
mysqlClientCertBrowse: "クライアント証明書を選択",
mysqlClientKeyBrowse: "クライアント秘密鍵を選択",
+3
View File
@@ -1292,6 +1292,9 @@ export default withEnglishFallback({
mysqlClientKeyPlaceholder: "/path/to/client.key",
mysqlClientCertHint: "MySQL이 mTLS를 요구할 때 클라이언트 인증서와 개인 키를 함께 제공해야 합니다.",
mysqlTlsConnectionFailureHint: "MySQL 서버가 TLS를 요구하지 않으면 연결을 편집하여 TLS 모드를 비활성화로 설정하고 다시 시도하세요.",
mysqlUnsupportedTlsVersionHint:
"DBX 내장 MySQL 드라이버는 TLS 1.2/1.3을 지원합니다. 이 서버는 이전 TLS 버전만 지원할 수 있습니다. 서버 TLS를 업그레이드하거나 재설정하고, 또는 호환 클라이언트나 TLS 변환 프록시를 사용하세요. REQUIRE SSL이 없는 계정만 TLS를 비활성화할 수 있으며, 그 외에는 1045가 반환될 수 있습니다. SSH만으로는 REQUIRE SSL을 충족할 수 없습니다.",
mysqlDisabledTlsAccessDeniedHint: "TLS가 비활성화되면 1045는 잘못된 비밀번호나 권한뿐 아니라 계정의 SSL 요구 사항을 의미할 수도 있습니다. 연결 가능한 관리자 세션에서 계정의 SHOW GRANTS를 확인하세요. SSH만으로는 REQUIRE SSL을 충족할 수 없습니다.",
mysqlUnsupportedCertVersionHint: "엄격한 검증은 이 서버 인증서 버전을 지원하지 않습니다. X.509 v3 인증서로 교체하거나 신뢰할 수 있는 네트워크에서만 필수 모드를 사용하세요.",
mysqlClientCertBrowse: "클라이언트 인증서 선택",
mysqlClientKeyBrowse: "클라이언트 개인 키 선택",
+3
View File
@@ -1134,6 +1134,9 @@ export default withEnglishFallback({
mysqlClientKeyPlaceholder: "/path/to/client.key",
mysqlClientCertHint: "Jeśli MySQL wymaga mTLS, certyfikat klienta i klucz prywatny trzeba podać razem.",
mysqlTlsConnectionFailureHint: "Jeżeli serwer MySQL nie wymaga TLS, edytuj połączenie, ustaw Tryb TLS na Wyłączony i spróbuj ponownie.",
mysqlUnsupportedTlsVersionHint:
"Wbudowany sterownik MySQL w DBX obsługuje TLS 1.2/1.3; ten serwer może obsługiwać tylko starsze wersje. Zaktualizuj lub skonfiguruj TLS serwera albo użyj zgodnego klienta lub proxy konwertującego TLS. TLS można wyłączyć tylko dla kont bez REQUIRE SSL; w przeciwnym razie MySQL może zwrócić 1045. Sam SSH nie spełnia REQUIRE SSL.",
mysqlDisabledTlsAccessDeniedHint: "Przy wyłączonym TLS błąd 1045 może oznaczać, że konto wymaga SSL, a nie tylko błędne hasło lub uprawnienia. Sprawdź SHOW GRANTS konta w działającej sesji administratora. Sam SSH nie spełnia REQUIRE SSL.",
mysqlUnsupportedCertVersionHint: "Weryfikacja ścisła nie obsługuje tej wersji certyfikatu serwera. Zastąp go certyfikatem X.509 v3 albo użyj trybu Wymagany tylko w zaufanej sieci.",
mysqlClientCertBrowse: "Wybierz certyfikat klienta",
mysqlClientKeyBrowse: "Wybierz klucz prywatny klienta",
+3
View File
@@ -1253,6 +1253,9 @@ export default withEnglishFallback({
mysqlClientKeyPlaceholder: "/caminho/para/client.key",
mysqlClientCertHint: "O certificado e a chave privada do cliente devem ser fornecidos juntos quando o MySQL exige mTLS.",
mysqlTlsConnectionFailureHint: "Se o servidor MySQL não exigir TLS, edite a conexão, defina o Modo TLS como Desabilitado e tente novamente.",
mysqlUnsupportedTlsVersionHint:
"O driver MySQL integrado do DBX suporta TLS 1.2/1.3; este servidor pode suportar apenas versões antigas. Atualize ou reconfigure o TLS do servidor, ou use um cliente compatível ou um proxy de conversão TLS. Só desabilite o TLS se a conta não tiver REQUIRE SSL; caso contrário, o MySQL pode retornar 1045. SSH sozinho não satisfaz REQUIRE SSL.",
mysqlDisabledTlsAccessDeniedHint: "Com TLS desabilitado, 1045 pode indicar que a conta exige SSL, além de senha ou permissões incorretas. Verifique SHOW GRANTS da conta em uma sessão administrativa que funcione. SSH sozinho não satisfaz REQUIRE SSL.",
mysqlUnsupportedCertVersionHint: "A verificação estrita não oferece suporte a esta versão do certificado do servidor. Substitua-o por um certificado X.509 v3 ou use o modo Obrigatório apenas em uma rede confiável.",
mysqlClientCertBrowse: "Escolher certificado do cliente",
mysqlClientKeyBrowse: "Escolher chave privada do cliente",
+3
View File
@@ -1268,6 +1268,9 @@ export default withEnglishFallback({
mysqlClientKeyPlaceholder: "/path/to/client.key",
mysqlClientCertHint: "Клиентский сертификат и закрытый ключ должны предоставляться вместе, когда MySQL требует mTLS.",
mysqlTlsConnectionFailureHint: "Если сервер MySQL не требует TLS, измените подключение, установите для режима TLS значение «Отключено» и повторите попытку.",
mysqlUnsupportedTlsVersionHint:
"Встроенный драйвер MySQL в DBX поддерживает TLS 1.2/1.3; сервер может поддерживать только старые версии. Обновите или перенастройте TLS сервера либо используйте совместимый клиент или прокси преобразования TLS. Отключать TLS можно только для учётных записей без REQUIRE SSL; иначе MySQL может вернуть 1045. Сам по себе SSH не выполняет REQUIRE SSL.",
mysqlDisabledTlsAccessDeniedHint: "При отключённом TLS ошибка 1045 может означать требование SSL для учётной записи, а не только неверный пароль или права. Проверьте SHOW GRANTS этой учётной записи из работающего сеанса администратора. Сам по себе SSH не выполняет REQUIRE SSL.",
mysqlUnsupportedCertVersionHint: "Строгая проверка не поддерживает эту версию сертификата сервера. Замените его на сертификат X.509 v3 или используйте режим «Обязательный» только в доверенной сети.",
mysqlClientCertBrowse: "Выбрать клиентский сертификат",
mysqlClientKeyBrowse: "Выбрать закрытый ключ клиента",
+3
View File
@@ -1430,6 +1430,9 @@ export default withEnglishFallback({
mysqlClientKeyPlaceholder: "/path/to/client.key",
mysqlClientCertHint: "ต้องระบุใบรับรอง Client และ Private Key พร้อมกันเมื่อ MySQL ต้องใช้ mTLS",
mysqlTlsConnectionFailureHint: "หาก Server MySQL ไม่ต้องใช้ TLS ให้แก้ไขการเชื่อมต่อ ตั้งโหมด TLS เป็น Disabled แล้วลองอีกครั้ง",
mysqlUnsupportedTlsVersionHint:
"ไดรเวอร์ MySQL ในตัว DBX รองรับ TLS 1.2/1.3 แต่เซิร์ฟเวอร์นี้อาจรองรับเฉพาะเวอร์ชันเก่า โปรดอัปเกรดหรือปรับการตั้งค่า TLS ของเซิร์ฟเวอร์ หรือใช้ไคลเอนต์ที่เข้ากันได้หรือพร็อกซีแปลง TLS ปิด TLS ได้เฉพาะเมื่อบัญชีไม่มี REQUIRE SSL มิฉะนั้น MySQL อาจส่งข้อผิดพลาด 1045 การใช้ SSH เพียงอย่างเดียวไม่เป็นไปตามข้อกำหนด REQUIRE SSL",
mysqlDisabledTlsAccessDeniedHint: "เมื่อปิด TLS ข้อผิดพลาด 1045 อาจหมายถึงบัญชีต้องใช้ SSL ไม่ใช่เพียงรหัสผ่านหรือสิทธิ์ไม่ถูกต้อง โปรดตรวจสอบ SHOW GRANTS ของบัญชีจากเซสชันผู้ดูแลระบบที่เชื่อมต่อได้ การใช้ SSH เพียงอย่างเดียวไม่เป็นไปตามข้อกำหนด REQUIRE SSL",
mysqlUnsupportedCertVersionHint: "การตรวจสอบแบบ Strict ไม่รองรับใบรับรอง Server เวอร์ชันนี้ แทนที่ด้วยใบรับรอง X.509 v3 หรือใช้โหมด Required เฉพาะบนเครือข่ายที่เชื่อถือได้",
mysqlClientCertBrowse: "เลือกใบรับรอง Client",
mysqlClientKeyBrowse: "เลือก Private Key ของ Client",
+3
View File
@@ -812,6 +812,9 @@ export default withEnglishFallback({
mysqlClientKeyPlaceholder: "/path/to/client.key",
mysqlClientCertHint: "MySQL mTLS gerektirdiğinde istemci sertifikası ve özel anahtarı birlikte verilmelidir.",
mysqlTlsConnectionFailureHint: "MySQL sunucusu TLS gerektirmiyorsa bağlantıyı düzenleyin, TLS Modunu Devre dışı yapın ve tekrar deneyin.",
mysqlUnsupportedTlsVersionHint:
"DBX'in yerleşik MySQL sürücüsü TLS 1.2/1.3'ü destekler; bu sunucu yalnızca eski sürümleri destekliyor olabilir. Sunucu TLS ayarlarını yükseltin veya yeniden yapılandırın ya da uyumlu bir istemci veya TLS dönüştürme vekili kullanın. TLS yalnızca hesapta REQUIRE SSL yoksa devre dışı bırakılabilir; aksi halde MySQL 1045 döndürebilir. Tek başına SSH, REQUIRE SSL koşulunu karşılamaz.",
mysqlDisabledTlsAccessDeniedHint: "TLS devre dışıyken 1045, yanlış parola veya izinlerin yanı sıra hesabın SSL gerektirdiğini de gösterebilir. Çalışan bir yönetici oturumundan hesabın SHOW GRANTS çıktısını kontrol edin. Tek başına SSH, REQUIRE SSL koşulunu karşılamaz.",
mysqlUnsupportedCertVersionHint: "Katı doğrulama bu sunucu sertifikası sürümünü desteklemiyor. Sertifikayı X.509 v3 ile değiştirin veya Zorunlu modu yalnızca güvenilir bir ağda kullanın.",
mysqlClientCertBrowse: "İstemci sertifikası seçin",
mysqlClientKeyBrowse: "İstemci özel anahtarı seçin",
+2
View File
@@ -1448,6 +1448,8 @@ export default withEnglishFallback({
mysqlClientKeyPlaceholder: "/path/to/client.key",
mysqlClientCertHint: "MySQL 要求 mTLS 时,客户端证书和私钥必须一起填写。",
mysqlTlsConnectionFailureHint: "如果 MySQL 服务端不要求 TLS,请编辑连接,在 TLS 模式中选择“禁用”后重试。",
mysqlUnsupportedTlsVersionHint: "DBX 内置 MySQL 驱动支持 TLS 1.2/1.3,此服务端可能只支持更旧的 TLS 版本。请升级或调整服务端 TLS 配置,或使用兼容客户端、TLS 协议转换代理。只有账号未配置 REQUIRE SSL 时才能禁用 TLS,否则 MySQL 可能返回 1045。仅使用 SSH 隧道不能满足 REQUIRE SSL。",
mysqlDisabledTlsAccessDeniedHint: "禁用 TLS 时,1045 除了可能表示密码或授权有误,也可能表示账号要求 SSL。请通过其他可连接的管理员会话检查该账号的 SHOW GRANTS。仅使用 SSH 隧道不能满足 REQUIRE SSL。",
mysqlUnsupportedCertVersionHint: "服务端证书版本不受严格校验支持。请更换为 X.509 v3 证书,或仅在可信网络中改用“必需”模式。",
mysqlClientCertBrowse: "选择客户端证书",
mysqlClientKeyBrowse: "选择客户端私钥",
+2
View File
@@ -1256,6 +1256,8 @@ export default withEnglishFallback({
mysqlClientKeyPlaceholder: "/path/to/client.key",
mysqlClientCertHint: "MySQL 要求 mTLS 時,用戶端憑證和私鑰必須一起填寫。",
mysqlTlsConnectionFailureHint: "如果 MySQL 伺服器不要求 TLS,請編輯連線,在 TLS 模式中選擇「停用」後重試。",
mysqlUnsupportedTlsVersionHint: "DBX 內建 MySQL 驅動程式支援 TLS 1.2/1.3,此伺服器可能只支援較舊的 TLS 版本。請升級或調整伺服器 TLS 設定,或使用相容用戶端、TLS 協定轉換代理。只有帳號未設定 REQUIRE SSL 時才能停用 TLS,否則 MySQL 可能回傳 1045。僅使用 SSH 通道無法滿足 REQUIRE SSL。",
mysqlDisabledTlsAccessDeniedHint: "停用 TLS 時,1045 除了可能表示密碼或授權有誤,也可能表示帳號要求 SSL。請透過其他可連線的管理員工作階段檢查該帳號的 SHOW GRANTS。僅使用 SSH 通道無法滿足 REQUIRE SSL。",
mysqlUnsupportedCertVersionHint: "伺服器憑證版本不受嚴格驗證支援。請更換為 X.509 v3 憑證,或僅在可信任網路中改用「必要」模式。",
mysqlClientCertBrowse: "選擇用戶端憑證",
mysqlClientKeyBrowse: "選擇用戶端私鑰",
@@ -33,6 +33,8 @@ function jdbcConfig(): ConnectionConfig {
const t = (key: string) => {
if (key === "connection.mysqlTlsConnectionFailureHint") return "Set TLS Mode to Disabled.";
if (key === "connection.mysqlUnsupportedTlsVersionHint") return "Use a TLS 1.2/1.3 server or compatible client. REQUIRE SSL rejects plaintext; SSH alone does not satisfy it.";
if (key === "connection.mysqlDisabledTlsAccessDeniedHint") return "1045 may indicate REQUIRE SSL, invalid credentials, or grants. Check SHOW GRANTS from a working admin session.";
if (key === "connection.mysqlUnsupportedCertVersionHint") return "Replace the certificate or use Required mode.";
if (key === "connection.mysqlMissingPasswordHint") return "No database password was sent.";
if (key === "connection.jdbcMissingRuntimeDependencyHint") return "Install from Maven or import every dependency JAR.";
@@ -40,6 +42,58 @@ const t = (key: string) => {
};
describe("appendConnectionErrorHints", () => {
const legacyTlsError = "MySQL connection failed: Input/output error: Input/output error: peer is incompatible: ServerDoesNotSupportTls12Or13";
const accessDenied = "MySQL connection failed: Server error: `ERROR 1045 (28000): Access denied for user 'icc'@'127.0.0.1' (using password: YES)'";
it.each(["ssl-mode=preferred", "sslMode=REQUIRED", "ssl-mode=verify_ca", "ssl-mode=verify_identity"])("explains unsupported TLS versions without recommending disabling TLS (%s)", (params) => {
const message = appendConnectionErrorHints(mysqlConfig(params), legacyTlsError, t);
expect(message).toContain(legacyTlsError);
expect(message).toContain(t("connection.mysqlUnsupportedTlsVersionHint"));
expect(message).not.toContain("Set TLS Mode to Disabled.");
expect(appendConnectionErrorHints(mysqlConfig(params), message, t)).toBe(message);
});
it("retains both attempts while explaining a failed Preferred fallback", () => {
const error = `MySQL TLS attempt failed: ${legacyTlsError}\n\nMySQL plaintext fallback failed: ${accessDenied}`;
const message = appendConnectionErrorHints({ ...mysqlConfig("ssl-mode=preferred"), password: "secret" }, error, t);
expect(message).toContain(error);
expect(message).toContain(t("connection.mysqlUnsupportedTlsVersionHint"));
expect(message).not.toContain("Set TLS Mode to Disabled.");
});
it.each(["ssl-mode=disabled", "sslMode=DISABLE", "require_ssl=false", "useSSL=false", undefined])("explains Disabled access denial conditionally without replacing it (%s)", (params) => {
const config = { ...mysqlConfig(params), password: "secret" };
const message = appendConnectionErrorHints(config, accessDenied, t);
expect(message).toContain(accessDenied);
expect(message).toContain(t("connection.mysqlDisabledTlsAccessDeniedHint"));
expect(appendConnectionErrorHints(config, message, t)).toBe(message);
});
it.each(["ssl-mode=preferred", "ssl-mode=required", "ssl-mode=verify_ca"])("does not claim plaintext was used for an ordinary TLS-mode access denial (%s)", (params) => {
expect(appendConnectionErrorHints({ ...mysqlConfig(params), password: "secret" }, accessDenied, t)).toBe(accessDenied);
});
it("keeps missing-password priority and ignores unrelated errors", () => {
const noPassword = accessDenied.replace("password: YES", "password: NO");
expect(appendConnectionErrorHints(mysqlConfig("ssl-mode=disabled"), noPassword, t)).toBe("No database password was sent.");
const unknownPassword = accessDenied.replace(" (using password: YES)", "");
expect(appendConnectionErrorHints(mysqlConfig("ssl-mode=disabled"), unknownPassword, t)).toBe(unknownPassword);
expect(appendConnectionErrorHints({ ...mysqlConfig("ssl-mode=disabled"), password: "secret" }, "ERROR 1044 (42000): Access denied for user root", t)).toBe("ERROR 1044 (42000): Access denied for user root");
expect(appendConnectionErrorHints(jdbcConfig(), legacyTlsError, t)).toBe(legacyTlsError);
expect(appendConnectionErrorHints(undefined, legacyTlsError, t)).toBe(legacyTlsError);
});
it("uses server-reported password use when the saved config omits a transient password", () => {
const config = mysqlConfig("ssl-mode=disabled");
const message = appendConnectionErrorHints(config, accessDenied, t);
expect(message).toContain(accessDenied);
expect(message).toContain(t("connection.mysqlDisabledTlsAccessDeniedHint"));
});
it("recognizes an encrypted SQLite file that was first opened without a password", () => {
const config = { ...mysqlConfig(undefined), db_type: "sqlite" as const, host: "/tmp/encrypted.db", port: 0 };
@@ -99,8 +99,18 @@ export function appendConnectionErrorHints(config: ConnectionConfig | undefined,
if (isMysqlMissingPasswordFailure(config, message)) {
return t("connection.mysqlMissingPasswordHint");
}
if (mysqlTlsMode(config) === "disabled") return message;
if (mysqlTlsMode(config) === "disabled") {
// 1045 does not distinguish bad credentials from an account requiring SSL.
const usedPassword = Boolean(config.password) || /using password:\s*yes\b/i.test(message);
if (usedPassword && /\b1045\b/.test(message) && /\b28000\b/.test(message) && /access denied for user/i.test(message)) {
return appendHint(message, t("connection.mysqlDisabledTlsAccessDeniedHint"));
}
return message;
}
if (!isMysqlTlsLikeFailure(message)) return message;
if (/ServerDoesNotSupportTls12Or13/i.test(message)) {
return appendHint(message, t("connection.mysqlUnsupportedTlsVersionHint"));
}
if (/UnsupportedCertVersion/i.test(message)) {
return appendHint(message, t("connection.mysqlUnsupportedCertVersionHint"));
}
+1
View File
@@ -27,3 +27,4 @@ tokio-util = { version = "0.7", features = ["compat"] }
[dev-dependencies]
mysql_common = { git = "https://github.com/t8y2/rust_mysql_common.git", rev = "47740d85a8277167a5717afac785d37b46a36296", default-features = false }
rustls = { version = "0.23", default-features = false, features = ["aws-lc-rs"] }
+183 -2
View File
@@ -1512,6 +1512,7 @@ async fn connect_with_ca_cert_pool_limit_idle_setup_database_with_mode(
let timeout = super::parse_connect_timeout_with_fallback(url, fallback_timeout);
let mut retry_url = url.to_string();
let mut retry_ca_cert_path = ca_cert_path;
let mut tls_error = None;
let mut result = connect_pool_attempt(
url,
ca_cert_path,
@@ -1528,6 +1529,7 @@ async fn connect_with_ca_cert_pool_limit_idle_setup_database_with_mode(
if result.as_ref().err().is_some_and(|error| mysql_error_should_retry_without_ssl(error)) {
if let Some(fallback_url) = ssl_fallback_url(url) {
log::info!("SSL handshake failed, retrying with ssl-mode=disabled");
tls_error = result.as_ref().err().cloned();
retry_url = fallback_url;
retry_ca_cert_path = None;
result = connect_pool_attempt(
@@ -1547,7 +1549,7 @@ async fn connect_with_ca_cert_pool_limit_idle_setup_database_with_mode(
if result.as_ref().err().is_some_and(|error| mysql_error_should_retry_with_legacy_eof(error)) {
log::info!("MySQL proxy returned legacy EOF packets; retrying with CLIENT_DEPRECATE_EOF disabled");
return connect_pool_attempt(
result = connect_pool_attempt(
&retry_url,
retry_ca_cert_path,
timeout,
@@ -1561,7 +1563,12 @@ async fn connect_with_ca_cert_pool_limit_idle_setup_database_with_mode(
.await;
}
result
result.map_err(|error| match tls_error {
// MySQL also uses 1045 for REQUIRE SSL accounts, so the final error
// alone can conceal why a Preferred connection could not use TLS.
Some(tls_error) => format!("MySQL TLS attempt failed: {tls_error}\n\nMySQL plaintext fallback failed: {error}"),
None => error,
})
}
#[allow(clippy::too_many_arguments)]
@@ -2731,6 +2738,7 @@ fn mysql_error_should_retry_without_ssl(error: &str) -> bool {
error.contains("handshakefailure")
|| error.contains("handshake")
|| error.contains("tls connection")
|| error.contains("serverdoesnotsupporttls12or13")
|| error.contains("server closed session")
// Some older MySQL proxies report a failed preferred-TLS probe as a
// protocol packet error instead of a TLS handshake error.
@@ -9474,6 +9482,179 @@ mod tests {
assert!(mysql_error_should_retry_without_ssl(error));
}
#[test]
fn mysql_tls_legacy_version_error_retries_only_when_tls_is_optional() {
let error = "MySQL connection failed: Input/output error: Input/output error: \
peer is incompatible: ServerDoesNotSupportTls12Or13";
assert!(mysql_error_should_retry_without_ssl(error));
for mode in ["required", "verify_ca", "verify_identity"] {
assert_eq!(ssl_fallback_url(&format!("mysql://host/db?ssl-mode={mode}")), None);
}
assert_eq!(ssl_fallback_url("mysql://host/db?ssl-mode=disabled"), None);
assert_eq!(ssl_fallback_url("mysql://host/db?ssl-cert=client.pem&ssl-key=client.key"), None);
}
#[derive(Clone, Copy)]
enum MysqlTlsTestReply {
LegacyTls,
AccessDenied,
LegacyEof,
Accept,
}
async fn mysql_tls_test_packet(socket: &mut tokio::net::TcpStream) -> Vec<u8> {
use tokio::io::AsyncReadExt;
let mut header = [0; 4];
socket.read_exact(&mut header).await.unwrap();
let length = u32::from_le_bytes([header[0], header[1], header[2], 0]) as usize;
assert!(length < 65536);
let mut payload = vec![0; length];
socket.read_exact(&mut payload).await.unwrap();
payload
}
async fn mysql_tls_test_write(socket: &mut tokio::net::TcpStream, seq: u8, payload: &[u8]) {
use tokio::io::AsyncWriteExt;
let length = (payload.len() as u32).to_le_bytes();
socket.write_all(&[length[0], length[1], length[2], seq]).await.unwrap();
socket.write_all(payload).await.unwrap();
}
async fn mysql_tls_test_server(
listener: tokio::net::TcpListener,
replies: Vec<MysqlTlsTestReply>,
) -> Vec<mysql_async::consts::CapabilityFlags> {
use mysql_async::consts::CapabilityFlags as Caps;
use mysql_common::proto::MySerialize;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
let mut attempts = Vec::new();
for reply in replies {
let (mut socket, _) = listener.accept().await.unwrap();
let greeting = mysql_common::packets::HandshakePacket::new(
10,
&b"5.6.24-test"[..],
1,
*b"12345678",
Some(&b"abcdefghijkl\0"[..]),
Caps::CLIENT_PROTOCOL_41
| Caps::CLIENT_SECURE_CONNECTION
| Caps::CLIENT_PLUGIN_AUTH
| Caps::CLIENT_SSL
| Caps::CLIENT_DEPRECATE_EOF,
33,
StatusFlags::SERVER_STATUS_AUTOCOMMIT,
Some(&b"mysql_native_password"[..]),
);
let mut payload = Vec::new();
greeting.serialize(&mut payload);
mysql_tls_test_write(&mut socket, 0, &payload).await;
let request = mysql_tls_test_packet(&mut socket).await;
let capabilities = Caps::from_bits_truncate(u32::from_le_bytes(request[..4].try_into().unwrap()));
attempts.push(capabilities);
match reply {
MysqlTlsTestReply::LegacyTls => {
assert!(capabilities.contains(Caps::CLIENT_SSL));
assert_eq!(request.len(), 32);
let mut header = [0; 5];
socket.read_exact(&mut header).await.unwrap();
let mut client_hello = vec![0; u16::from_be_bytes([header[3], header[4]]) as usize];
socket.read_exact(&mut client_hello).await.unwrap();
// A complete TLS 1.0 ServerHello makes the real rustls connector
// report ServerDoesNotSupportTls12Or13 before authentication.
let mut hello = vec![0x16, 3, 1, 0, 44, 2, 0, 0, 40, 3, 1];
hello.extend_from_slice(&[0; 32]);
hello.extend_from_slice(&[0, 0xc0, 0x2f, 0, 0, 0]);
socket.write_all(&hello).await.unwrap();
}
MysqlTlsTestReply::AccessDenied => {
assert!(!capabilities.contains(Caps::CLIENT_SSL));
let mut error = vec![0xff, 0x15, 0x04];
error.extend_from_slice(b"#28000Access denied for user 'test'@'localhost' (using password: YES)");
mysql_tls_test_write(&mut socket, 2, &error).await;
}
MysqlTlsTestReply::LegacyEof => {
assert!(!capabilities.contains(Caps::CLIENT_SSL));
assert!(capabilities.contains(Caps::CLIENT_DEPRECATE_EOF));
mysql_tls_test_write(&mut socket, 3, &[0, 0, 0, 2, 0, 0, 0]).await;
}
MysqlTlsTestReply::Accept => {
assert!(!capabilities.contains(Caps::CLIENT_SSL));
mysql_tls_test_write(&mut socket, 2, &[0, 0, 0, 2, 0, 0, 0]).await;
loop {
let command = mysql_tls_test_packet(&mut socket).await;
if command[0] == 1 {
break;
}
mysql_tls_test_write(&mut socket, 1, &[0, 0, 0, 2, 0, 0, 0]).await;
}
}
}
}
attempts
}
async fn mysql_tls_test_connect(
replies: Vec<MysqlTlsTestReply>,
mode: &str,
) -> (Result<MySqlPool, String>, tokio::task::JoinSet<Vec<mysql_async::consts::CapabilityFlags>>) {
let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
let listener = tokio::net::TcpListener::bind(("127.0.0.1", 0)).await.unwrap();
let port = listener.local_addr().unwrap().port();
let mut servers = tokio::task::JoinSet::new();
servers.spawn(mysql_tls_test_server(listener, replies));
let url = format!("mysql://test:secret@127.0.0.1:{port}/?ssl-mode={mode}&max_allowed_packet=65536&wait_timeout=60&sessionVariables=group_concat_max_len%3D1024");
let result = connect_with_ca_cert_and_pool_limit(&url, None, Duration::from_secs(2), 1).await;
(result, servers)
}
#[tokio::test]
async fn mysql_tls_preferred_retains_legacy_tls_and_plaintext_errors() {
let (result, mut servers) =
mysql_tls_test_connect(vec![MysqlTlsTestReply::LegacyTls, MysqlTlsTestReply::AccessDenied], "preferred")
.await;
let error = result.unwrap_err();
assert!(error.contains("ServerDoesNotSupportTls12Or13"), "{error}");
assert!(error.contains("1045 (28000)"), "{error}");
assert_eq!(servers.join_next().await.unwrap().unwrap().len(), 2);
}
#[tokio::test]
async fn mysql_tls_preferred_returns_a_working_plaintext_pool() {
let (result, mut servers) =
mysql_tls_test_connect(vec![MysqlTlsTestReply::LegacyTls, MysqlTlsTestReply::Accept], "preferred").await;
let pool = result.unwrap();
verify_pool_connection(&pool, Duration::from_secs(2)).await.unwrap();
pool.disconnect().await.unwrap();
assert_eq!(servers.join_next().await.unwrap().unwrap().len(), 2);
}
#[tokio::test]
async fn mysql_tls_preferred_retains_original_error_after_legacy_eof_retry() {
let (result, mut servers) = mysql_tls_test_connect(
vec![MysqlTlsTestReply::LegacyTls, MysqlTlsTestReply::LegacyEof, MysqlTlsTestReply::AccessDenied],
"preferred",
)
.await;
let error = result.unwrap_err();
assert!(error.contains("ServerDoesNotSupportTls12Or13"), "{error}");
assert!(error.contains("1045 (28000)"), "{error}");
let attempts = servers.join_next().await.unwrap().unwrap();
assert_eq!(attempts.len(), 3);
assert!(!attempts[2].contains(mysql_async::consts::CapabilityFlags::CLIENT_DEPRECATE_EOF));
}
#[tokio::test]
async fn mysql_tls_strict_modes_never_retry_in_plaintext() {
for mode in ["required", "verify_ca", "verify_identity"] {
let (result, mut servers) = mysql_tls_test_connect(vec![MysqlTlsTestReply::LegacyTls], mode).await;
let error = result.unwrap_err();
assert!(error.contains("ServerDoesNotSupportTls12Or13"), "{mode}: {error}");
assert_eq!(servers.join_next().await.unwrap().unwrap().len(), 1);
}
}
#[test]
fn mysql_server_without_ssl_capability_retries_without_ssl() {
let error =
@@ -0,0 +1,69 @@
# MySQL TLS Diagnostics Implementation Plan
> **For agentic workers:** Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox syntax for tracking.
**Goal:** Fix Preferred TLS fallback and explain legacy TLS / REQUIRE SSL failures without weakening strict modes.
**Architecture:** Extend the existing error classifier and preserve the first TLS failure until all plaintext retries finish. Add specialized frontend hints before the generic TLS path, using existing i18n resources.
**Tech Stack:** Rust, mysql_async 0.37 with rustls, Tokio, TypeScript, Vitest, vue-i18n.
**Spec:** `docs/specs/970-mysql-tls-diagnostics/spec.md`
## Global Constraints
- Preserve Required, Verify CA, Verify Identity, client-certificate, Disabled, and URL alias behavior.
- Keep the legacy-EOF retry and preserve both errors only when plaintext fallback actually occurred and failed.
- Do not diagnose REQUIRE SSL from 1045 alone or recommend SSH as satisfying REQUIRE SSL.
- No TLS backend/runtime dependency/account changes. Do not touch the user's original checkout.
- Follow CONTRIBUTING.md: fork branch, PR to t8y2/dbx main, tests and explicit limitations.
## Review Focus
- Exact reported rustls error triggers fallback only in permitted modes.
- Plaintext failure after TLS and legacy-EOF retry retains the first error.
- Successful fallback yields a working pool without diagnostic errors.
- Disabled aliases, ordinary 1045, missing-password, and non-MySQL failures retain accurate guidance.
- Translation rendering remains valid and specific hints do not append misleading generic advice.
### Task 1: MySQL retry diagnostics
**Files:** `crates/dbx-driver-mysql/src/mysql.rs` (production and existing tests), `crates/dbx-driver-mysql/Cargo.toml` and `Cargo.lock` (test-only direct reference to existing rustls).
**Interfaces:** Retain public `connect` and `connect_with_ca_cert` signatures, `mysql_error_should_retry_without_ssl(&str) -> bool`, and `ssl_fallback_url(&str) -> Option<String>`.
- [x] Add failing tests for the exact reported classifier input and wire-protocol tests through `connect` for Preferred fallback failure/success and strict modes; test the final legacy-EOF retry result.
- [x] Run `cargo test -p dbx-driver-mysql --lib --locked mysql_tls_`; expect regressions to fail for missing fallback/error preservation.
- [x] Recognize only the exact rustls incompatibility variant; store the initial error when fallback starts, assign the legacy-EOF result rather than returning early, and combine errors at the end.
- [x] Run `cargo test -p dbx-driver-mysql --lib --locked`; expect all non-ignored driver tests to pass.
- [x] Commit the driver change and regression tests.
### Task 2: Actionable localized hints
**Files:** `apps/desktop/src/lib/connection/connectionErrorHints.ts`, its existing `__tests__/connectionErrorHints.spec.ts`, and `apps/desktop/src/i18n/locales/*.ts`.
**Interfaces:** Keep `appendConnectionErrorHints(config, message, t): string`. Add `connection.mysqlUnsupportedTlsVersionHint` and `connection.mysqlDisabledTlsAccessDeniedHint`.
- [x] Add failing table-driven tests for exact legacy-version errors, combined fallback errors, Disabled aliases with 1045/28000, ordinary authentication errors, empty passwords, non-MySQL errors, and duplicate hint prevention.
- [x] Run `pnpm exec vitest run apps/desktop/src/lib/connection/__tests__/connectionErrorHints.spec.ts`; expect the new hint cases to fail.
- [x] Add specific hint routing and translations; preserve existing missing-password/certificate priority and native error details.
- [x] Run the focused tests, the i18n suite, and `pnpm test`; expect green, or document unrelated failures individually.
- [x] Commit localized hints and tests.
### Task 3: Verification and submission
**Files:** This plan and Spec; no new production interfaces.
- [x] Run formatting checks, TypeScript checking, complete MySQL crate tests, and frontend suite; record exact outcomes and any environment limitations.
- [x] Request a fresh whole-branch code review; address important findings with RED/GREEN tests.
- [x] Verify issue ownership, fork destination, upstream main base, diff scope, and clean status. Push a codex/ branch to caichangqing1120/dbx and create/attach a PR against t8y2/dbx main. Do not merge, deploy, or close the issue.
Submitted: https://github.com/t8y2/dbx/pull/13294 (head `caichangqing1120:codex/fix-13265-mysql-tls-diagnostics`, base `t8y2:main`). Remote CI is separate from the local checks in `verification.md`.
## Self-review
Verification evidence: `verification.md`. One independent-review P2 was fixed with RED/GREEN coverage.
All acceptance requirements map to Tasks 1/2; Task 3 covers delivery. Existing
mode guards remain authoritative. Tests must include the production connection
flow rather than only testing string helpers. Approved scope is unchanged.
Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

@@ -0,0 +1,35 @@
# MySQL TLS fallback diagnostics
Issue: https://github.com/t8y2/dbx/issues/13265
Approval: the user approved the P0 proposal in this conversation on 2026-10-23
and authorized claim, implementation, verification, and a PR to upstream main.
## Scope and acceptance
- Recognize rustls `ServerDoesNotSupportTls12Or13` in the existing Preferred
plaintext fallback classifier. Preserve existing aliases and fallback guards.
- Required, Verify CA, Verify Identity, and client-certificate connections must
never downgrade. Disabled must not attempt TLS.
- If fallback fails, preserve both the original TLS error and the final plaintext
error, including the result of the existing legacy-EOF retry. Successful
fallback must still return a usable pool.
- Give TLS-version-specific guidance instead of the generic disable-TLS hint:
the built-in MySQL driver supports TLS 1.2/1.3, legacy-only servers require
server TLS upgrades or a compatible client/proxy, REQUIRE SSL can reject
plaintext with 1045, and SSH alone does not satisfy REQUIRE SSL.
- Disabled-mode 1045/28000 with a supplied password gets a conditional REQUIRE
SSL explanation, never a definitive diagnosis. Keep the original error and
suggest checking SHOW GRANTS from a working administrator session.
- Preserve missing-password priority, certificate-specific hints, non-MySQL
behavior, generic TLS hints, and existing URL/queue/authentication contracts.
- Add tests against production functions and the real connection entry point;
mock only the remote wire-protocol boundary. No external database is required.
## Non-goals
No TLS 1.0/1.1 support, TLS backend switch, diagnostic UI, runtime dependency changes, server
account changes, deployment, auto-merge, or automatic issue closure.
Test setup may directly reference the already locked rustls dependency to
initialize the same crypto provider used by the application entry points.
@@ -0,0 +1,75 @@
# Verification: MySQL TLS diagnostics
Issue: https://github.com/t8y2/dbx/issues/13265
Pull request: https://github.com/t8y2/dbx/pull/13294 (submitted, not merged).
Base: `0297c5ecf9d727b18e918175d1bcc2f8aa08f08f` (upstream main).
## Results
| Check | Result |
| --- | --- |
| `cargo test -p dbx-driver-mysql --locked` | 299 passed, 4 pre-existing live-environment tests ignored; doctests passed (0 cases) |
| `pnpm test --maxWorkers=4` | 1,963 files, 26,313 tests passed |
| `NODE_OPTIONS=--max-old-space-size=8192 pnpm typecheck` | Passed |
| `cargo clippy -p dbx-driver-mysql --all-targets --no-deps --locked -- -D warnings` | Passed |
| `rustfmt --edition 2021 --check crates/dbx-driver-mysql/src/mysql.rs` | Passed |
| `pnpm exec oxfmt --check` on changed TypeScript files | Passed |
| `pnpm exec oxlint --vue-plugin` on changed connection hint/test files | Passed |
| `git diff --check` | Passed |
## Regression evidence
- Before the driver fix, the exact reported error failed the classifier test;
real rustls wire tests also failed Preferred fallback/failure-preservation cases.
A synthetic TLS 1.0 ServerHello reproduces `ServerDoesNotSupportTls12Or13`
through the production mysql_async connector. The server is not a mocked
retry function. Strict-mode tests already passed and remain passing.
- Successful Preferred fallback returns a pool that can be checked out and
pinged again. A fallback that then requires the legacy-EOF retry retains both
the initial TLS error and the final 1045, with the expected capability flags.
- Frontend regression additions initially produced 10 failures in the existing
hint suite. The final hint suite has 30 passing cases and preserves missing
password, certificate, non-MySQL, alias, native error, and idempotency behavior.
- Independent review found one P2: persisted configs can omit a transient
password. Its new regression failed before accepting native `(using password:
YES)` evidence and passed afterward. Native NO retains missing-password
priority; absent password-use evidence is not guessed.
- The wire tests use a single-connection pool to keep the fixture focused on
retry behavior. A test-only direct reference to the already locked rustls
initializes the same AWS-LC provider as the application entry points; no
dependency version or runtime TLS backend changed.
## Initial-run limitations and reruns
- The initial 8-worker frontend run had two failures: the unchanged
`legacyWebviewFallback.spec.ts` scrollbar case exceeded 10 seconds, and the
unchanged `sqlSyntaxTreeWindow.spec.ts` multi-statement case returned null.
Both passed in an independent one-worker run and the complete 4-worker rerun.
No changes were made to those tests or their production code.
- Default-heap type checking exhausted the Node heap. The complete check passed
with the explicit 8 GB heap limit above.
- Dependency-inclusive Clippy with Rust 1.95 stopped on three unchanged
`dbx-sql-core` warnings: `nonminimal_bool` in
`object_source_sql/oracle_routines/analysis.rs:442` and `collapsible_match` in
`sql_analysis.rs:1486` and `sql_analysis.rs:1517`. The strict MySQL-only check
passed. These unrelated files were not modified.
## Screenshots and scope
The screenshot uses the real Vue application and connection error dialog with
Playwright-intercepted local HTTP errors and synthetic credentials. They verify
UI rendering, not a live MySQL server or production deployment. The focused
browser session also encountered unrelated startup warnings/errors because it
used a minimal mock backend, not a complete DBX Web server.
![Preferred TLS failure plus plaintext failure](screenshots/preferred-legacy-tls.png)
The Disabled-mode browser snapshot also confirmed the conditional 1045 hint,
SHOW GRANTS guidance, and SSH/REQUIRE SSL distinction in the actual error dialog.
No TLS 1.0/1.1 support is introduced. No live legacy MySQL, REQUIRE SSL account,
cross-platform runtime, multi-connection pool lifecycle, or deployment acceptance
is claimed. Translation technical content/token safety was reviewed across all
14 locales, not independently certified by native speakers.