diff --git a/app/common/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java b/app/common/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java index c752485f0..bce6e1272 100644 --- a/app/common/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java +++ b/app/common/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java @@ -516,6 +516,8 @@ public class EndpointConfiguration { addEndpointAlternative("compress-pdf", "qpdf"); addEndpointAlternative("compress-pdf", "Ghostscript"); addEndpointAlternative("compress-pdf", "Java"); + addEndpointAlternative("crop", "Ghostscript"); + addEndpointAlternative("crop", "Java"); addEndpointAlternative("ocr-pdf", "tesseract"); addEndpointAlternative("ocr-pdf", "OCRmyPDF"); diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/SecurityConfiguration.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/SecurityConfiguration.java index 8b61d9c3e..3f03dcbaf 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/SecurityConfiguration.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/SecurityConfiguration.java @@ -208,21 +208,20 @@ public class SecurityConfiguration { } else { String xFrameOption = securityProperties.getXFrameOptions(); if (xFrameOption != null) { - http.headers(headers -> { - if ("DISABLED".equalsIgnoreCase(xFrameOption)) { - headers.frameOptions(frameOptions -> frameOptions.disable()); - } else if ("SAMEORIGIN".equalsIgnoreCase(xFrameOption)) { - headers.frameOptions(frameOptions -> frameOptions.sameOrigin()); - } else { - // Default to DENY - headers.frameOptions(frameOptions -> frameOptions.deny()); - } - }); + http.headers( + headers -> { + if ("DISABLED".equalsIgnoreCase(xFrameOption)) { + headers.frameOptions(frameOptions -> frameOptions.disable()); + } else if ("SAMEORIGIN".equalsIgnoreCase(xFrameOption)) { + headers.frameOptions(frameOptions -> frameOptions.sameOrigin()); + } else { + // Default to DENY + headers.frameOptions(frameOptions -> frameOptions.deny()); + } + }); } else { // If not configured, use default DENY - http.headers(headers -> - headers.frameOptions(frameOptions -> frameOptions.deny()) - ); + http.headers(headers -> headers.frameOptions(frameOptions -> frameOptions.deny())); } } diff --git a/build.gradle b/build.gradle index 2366c6a92..950705d4f 100644 --- a/build.gradle +++ b/build.gradle @@ -67,7 +67,7 @@ springBoot { allprojects { group = 'stirling.software' - version = '2.4.0' + version = '2.4.1' configurations.configureEach { exclude group: 'commons-logging', module: 'commons-logging' diff --git a/frontend/src-tauri/Cargo.lock b/frontend/src-tauri/Cargo.lock index d60b8f0b7..bda073963 100644 --- a/frontend/src-tauri/Cargo.lock +++ b/frontend/src-tauri/Cargo.lock @@ -689,7 +689,7 @@ dependencies = [ "bitflags 2.10.0", "core-foundation 0.10.1", "core-graphics-types", - "foreign-types 0.5.0", + "foreign-types", "libc", ] @@ -1168,15 +1168,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared 0.1.1", -] - [[package]] name = "foreign-types" version = "0.5.0" @@ -1184,7 +1175,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" dependencies = [ "foreign-types-macros", - "foreign-types-shared 0.3.1", + "foreign-types-shared", ] [[package]] @@ -1198,12 +1189,6 @@ dependencies = [ "syn 2.0.114", ] -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "foreign-types-shared" version = "0.3.1" @@ -1832,6 +1817,20 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http 0.2.12", + "hyper 0.14.32", + "rustls 0.21.12", + "tokio", + "tokio-rustls 0.24.1", +] + [[package]] name = "hyper-rustls" version = "0.27.7" @@ -1841,25 +1840,12 @@ dependencies = [ "http 1.4.0", "hyper 1.8.1", "hyper-util", - "rustls", + "rustls 0.23.36", "rustls-pki-types", "tokio", - "tokio-rustls", + "tokio-rustls 0.26.4", "tower-service", - "webpki-roots", -] - -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper 0.14.32", - "native-tls", - "tokio", - "tokio-native-tls", + "webpki-roots 1.0.5", ] [[package]] @@ -1890,9 +1876,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.64" +version = "0.1.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -2415,23 +2401,6 @@ dependencies = [ "windows-sys 0.60.2", ] -[[package]] -name = "native-tls" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" -dependencies = [ - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework 2.11.1", - "security-framework-sys", - "tempfile", -] - [[package]] name = "ndk" version = "0.9.0" @@ -2750,50 +2719,12 @@ dependencies = [ "pathdiff", ] -[[package]] -name = "openssl" -version = "0.10.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" -dependencies = [ - "bitflags 2.10.0", - "cfg-if", - "foreign-types 0.3.2", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.114", -] - [[package]] name = "openssl-probe" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" -[[package]] -name = "openssl-sys" -version = "0.9.111" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - [[package]] name = "option-ext" version = "0.2.0" @@ -3027,7 +2958,7 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" dependencies = [ - "siphasher 1.0.1", + "siphasher 1.0.2", ] [[package]] @@ -3254,7 +3185,7 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash", - "rustls", + "rustls 0.23.36", "socket2 0.6.2", "thiserror 2.0.18", "tokio", @@ -3274,7 +3205,7 @@ dependencies = [ "rand 0.9.2", "ring", "rustc-hash", - "rustls", + "rustls 0.23.36", "rustls-pki-types", "slab", "thiserror 2.0.18", @@ -3527,15 +3458,16 @@ dependencies = [ "http 0.2.12", "http-body 0.4.6", "hyper 0.14.32", - "hyper-tls", + "hyper-rustls 0.24.2", "ipnet", "js-sys", "log", "mime", - "native-tls", "once_cell", "percent-encoding", "pin-project-lite", + "rustls 0.21.12", + "rustls-native-certs", "rustls-pemfile", "serde", "serde_json", @@ -3543,12 +3475,13 @@ dependencies = [ "sync_wrapper 0.1.2", "system-configuration 0.5.1", "tokio", - "tokio-native-tls", + "tokio-rustls 0.24.1", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", + "webpki-roots 0.25.4", "winreg 0.50.0", ] @@ -3570,7 +3503,7 @@ dependencies = [ "http-body 1.0.1", "http-body-util", "hyper 1.8.1", - "hyper-rustls", + "hyper-rustls 0.27.7", "hyper-util", "js-sys", "log", @@ -3578,14 +3511,14 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls", + "rustls 0.23.36", "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", "sync_wrapper 1.0.2", "tokio", - "tokio-rustls", + "tokio-rustls 0.26.4", "tokio-util", "tower", "tower-http", @@ -3595,7 +3528,7 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots", + "webpki-roots 1.0.5", ] [[package]] @@ -3695,6 +3628,18 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "log", + "ring", + "rustls-webpki 0.101.7", + "sct", +] + [[package]] name = "rustls" version = "0.23.36" @@ -3704,11 +3649,23 @@ dependencies = [ "once_cell", "ring", "rustls-pki-types", - "rustls-webpki", + "rustls-webpki 0.103.9", "subtle", "zeroize", ] +[[package]] +name = "rustls-native-certs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework 2.11.1", +] + [[package]] name = "rustls-pemfile" version = "1.0.4" @@ -3728,6 +3685,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "rustls-webpki" version = "0.103.9" @@ -3826,6 +3793,16 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "seahash" version = "4.1.0" @@ -4145,9 +4122,9 @@ checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" [[package]] name = "siphasher" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" +checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" [[package]] name = "slab" @@ -5046,12 +5023,12 @@ dependencies = [ ] [[package]] -name = "tokio-native-tls" -version = "0.3.1" +name = "tokio-rustls" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "native-tls", + "rustls 0.21.12", "tokio", ] @@ -5061,7 +5038,7 @@ version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ - "rustls", + "rustls 0.23.36", "tokio", ] @@ -5427,12 +5404,6 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ba6f5989077681266825251a52748b8c1d8a4ad098cc37e440103d0ea717fc0" -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - [[package]] name = "version-compare" version = "0.2.1" @@ -5641,6 +5612,12 @@ dependencies = [ "system-deps", ] +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + [[package]] name = "webpki-roots" version = "1.0.5" @@ -6483,18 +6460,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.33" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" +checksum = "fdea86ddd5568519879b8187e1cf04e24fce28f7fe046ceecbce472ff19a2572" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.33" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" +checksum = "0c15e1b46eff7c6c91195752e0eeed8ef040e391cdece7c25376957d5f15df22" dependencies = [ "proc-macro2", "quote", @@ -6563,9 +6540,9 @@ dependencies = [ [[package]] name = "zmij" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfcd145825aace48cff44a8844de64bf75feec3080e0aa5cdbde72961ae51a65" +checksum = "02aae0f83f69aafc94776e879363e9771d7ecbffe2c7fbb6c14c5e00dfe88439" [[package]] name = "zvariant" diff --git a/frontend/src-tauri/Cargo.toml b/frontend/src-tauri/Cargo.toml index 87c00bc8d..a415d879f 100644 --- a/frontend/src-tauri/Cargo.toml +++ b/frontend/src-tauri/Cargo.toml @@ -35,7 +35,7 @@ tauri-plugin-opener = "2.5.3" tauri-plugin-deep-link = "2.4.6" keyring = { version = "3.6.1", features = ["apple-native", "windows-native"] } tokio = { version = "1.0", features = ["time", "sync"] } -reqwest = { version = "0.11", features = ["json"] } +reqwest = { version = "0.11", default-features = false, features = ["json", "rustls-tls", "rustls-tls-native-roots"] } tiny_http = "0.12" url = "2.5" urlencoding = "2.1" diff --git a/frontend/src-tauri/src/commands/auth.rs b/frontend/src-tauri/src/commands/auth.rs index 64be3ab17..43fa9128c 100644 --- a/frontend/src-tauri/src/commands/auth.rs +++ b/frontend/src-tauri/src/commands/auth.rs @@ -213,8 +213,20 @@ pub async fn login( // Detect if this is Supabase (SaaS) or Spring Boot (self-hosted) let is_supabase = server_url.trim_end_matches('/') == saas_server_url.trim_end_matches('/'); - // Create HTTP client - let client = reqwest::Client::new(); + // Create HTTP client with certificate bypass + // This handles: + // - Self-signed certificates + // - Missing intermediate certificates + // - Certificate hostname mismatches + // Note: Rustls only supports TLS 1.2 and TLS 1.3 + let client = reqwest::Client::builder() + .danger_accept_invalid_certs(true) + .timeout(std::time::Duration::from_secs(30)) + .build() + .map_err(|e| { + log::error!("Failed to create HTTP client: {}", e); + format!("Failed to create HTTP client: {}", e) + })?; if is_supabase { // Supabase authentication flow @@ -235,7 +247,24 @@ pub async fn login( .json(&request_body) .send() .await - .map_err(|e| format!("Network error: {}", e))?; + .map_err(|e| { + let error_msg = e.to_string(); + let error_lower = error_msg.to_lowercase(); + log::error!("Supabase login network error: {}", e); + + // Detect TLS version mismatch + if error_lower.contains("peer is incompatible") || + error_lower.contains("protocol version") || + error_lower.contains("peerincompatible") || + (error_lower.contains("handshake") && (error_lower.contains("tls") || error_lower.contains("ssl"))) { + format!( + "TLS version not supported: The Supabase server appears to require an unsupported TLS version. \ + Please contact support. Technical details: {}", e + ) + } else { + format!("Network error connecting to Supabase: {}", e) + } + })?; let status = response.status(); @@ -296,7 +325,39 @@ pub async fn login( .json(&payload) .send() .await - .map_err(|e| format!("Network error: {}", e))?; + .map_err(|e| { + let error_msg = e.to_string(); + let error_lower = error_msg.to_lowercase(); + log::error!("Spring Boot login network error: {}", e); + + // Detect TLS version mismatch (server using TLS 1.0/1.1) + if error_lower.contains("peer is incompatible") || + error_lower.contains("protocol version") || + error_lower.contains("peerincompatible") || + (error_lower.contains("handshake") && (error_lower.contains("tls") || error_lower.contains("ssl"))) { + format!( + "TLS version not supported: The server appears to be using TLS 1.0 or TLS 1.1, which are not supported by this desktop app. \ + Please upgrade your server to use TLS 1.2 or higher, or use the web version of Stirling-PDF instead. \ + Technical details: {}", e + ) + // Other TLS/SSL errors (certificate issues) + } else if error_lower.contains("tls") || error_lower.contains("ssl") || + error_lower.contains("certificate") || error_lower.contains("decrypt") { + format!( + "TLS/SSL connection error: This usually means the server has certificate issues. \ + The desktop app accepts self-signed certificates, so this might be a TLS version issue. \ + Technical details: {}", e + ) + } else if error_lower.contains("connection refused") { + format!("Connection refused: Server is not reachable at {}. Check if the server is running and the URL is correct.", login_url) + } else if error_lower.contains("timeout") { + format!("Connection timeout: Server at {} is not responding. Check your network connection.", login_url) + } else if error_lower.contains("dns") || error_lower.contains("resolve") { + format!("DNS resolution failed: Cannot resolve hostname. Check if the server URL is correct.") + } else { + format!("Network error: {}", e) + } + })?; let status = response.status(); log::debug!("Spring Boot login response status: {}", status); @@ -505,7 +566,20 @@ async fn exchange_code_for_token( ) -> Result { log::info!("Exchanging authorization code for access token with PKCE"); - let client = reqwest::Client::new(); + // Create HTTP client with certificate bypass + // This handles: + // - Self-signed certificates + // - Missing intermediate certificates + // - Certificate hostname mismatches + // Note: Rustls only supports TLS 1.2 and TLS 1.3 + let client = reqwest::Client::builder() + .danger_accept_invalid_certs(true) + .timeout(std::time::Duration::from_secs(30)) + .build() + .map_err(|e| { + log::error!("Failed to create HTTP client: {}", e); + format!("Failed to create HTTP client: {}", e) + })?; // grant_type goes in query string, not body! let token_url = format!("{}/auth/v1/token?grant_type=pkce", auth_server_url.trim_end_matches('/')); @@ -526,7 +600,24 @@ async fn exchange_code_for_token( .json(&body) .send() .await - .map_err(|e| format!("Failed to exchange code for token: {}", e))?; + .map_err(|e| { + let error_msg = e.to_string(); + let error_lower = error_msg.to_lowercase(); + log::error!("OAuth token exchange network error: {}", e); + + // Detect TLS version mismatch + if error_lower.contains("peer is incompatible") || + error_lower.contains("protocol version") || + error_lower.contains("peerincompatible") || + (error_lower.contains("handshake") && (error_lower.contains("tls") || error_lower.contains("ssl"))) { + format!( + "TLS version not supported: The authentication server appears to require an unsupported TLS version. \ + Please contact support. Technical details: {}", e + ) + } else { + format!("Failed to exchange code for token: {}", e) + } + })?; let status = response.status(); if !status.is_success() { diff --git a/frontend/src-tauri/tauri.conf.json b/frontend/src-tauri/tauri.conf.json index f22d2d44f..41c446709 100644 --- a/frontend/src-tauri/tauri.conf.json +++ b/frontend/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "../node_modules/@tauri-apps/cli/config.schema.json", "productName": "Stirling-PDF", - "version": "2.4.0", + "version": "2.4.1", "identifier": "stirling.pdf.dev", "build": { "frontendDist": "../dist", @@ -16,7 +16,8 @@ "width": 1280, "height": 800, "resizable": true, - "fullscreen": false + "fullscreen": false, + "additionalBrowserArgs": "--enable-features=CertVerifierBuiltinFeature" } ] }, diff --git a/frontend/src/core/testing/serverExperienceSimulations.ts b/frontend/src/core/testing/serverExperienceSimulations.ts index 121c14fd1..a0948ba28 100644 --- a/frontend/src/core/testing/serverExperienceSimulations.ts +++ b/frontend/src/core/testing/serverExperienceSimulations.ts @@ -38,7 +38,7 @@ const FREE_LICENSE_INFO: LicenseInfo = { const BASE_NO_LOGIN_CONFIG: AppConfig = { enableAnalytics: true, - appVersion: '2.4.0', + appVersion: '2.4.1', serverCertificateEnabled: false, enableAlphaFunctionality: false, serverPort: 8080, diff --git a/frontend/src/desktop/services/connectionModeService.ts b/frontend/src/desktop/services/connectionModeService.ts index c11b27c2a..5f5101bd4 100644 --- a/frontend/src/desktop/services/connectionModeService.ts +++ b/frontend/src/desktop/services/connectionModeService.ts @@ -251,11 +251,15 @@ export class ConnectionModeService { diagnostics.push(stage2Result); if (stage2Result.success) { - console.log(`[ConnectionModeService] ⚠️ Certificate issue detected - works without validation`); + console.log(`[ConnectionModeService] ⚠️ Certificate issue detected - but connection works with bypass enabled`); + console.log(`[ConnectionModeService] ==================== DIAGNOSTIC SUMMARY ====================`); + console.log(`[ConnectionModeService] ✅ CONNECTION SUCCESSFUL (with certificate bypass)`); + console.log(`[ConnectionModeService] Protocol: HTTPS with certificate validation disabled`); + console.log(`[ConnectionModeService] Duration: ${stage2Result.duration}ms`); + console.log(`[ConnectionModeService] Note: Server has missing intermediate certificate or invalid cert`); + console.log(`[ConnectionModeService] ==================== DIAGNOSTIC SESSION END ====================`); return { - success: false, - error: 'SSL certificate validation failed. The server has an invalid, self-signed, or untrusted certificate.', - errorCode: 'SSL_CERTIFICATE_INVALID', + success: true, diagnostics, }; } @@ -487,7 +491,13 @@ export class ConnectionModeService { let detailedMessage = `Failed: ${errorMsg}`; - if (errorLower.includes('timeout') || errorLower.includes('timed out')) { + // Check for TLS version mismatch (TLS 1.0/1.1 not supported) + if (errorLower.includes('peer is incompatible') || + errorLower.includes('protocol version') || + errorLower.includes('peerincompatible') || + (errorLower.includes('handshake') && (errorLower.includes('tls') || errorLower.includes('ssl')))) { + detailedMessage = `TLS version not supported - Server appears to use TLS 1.0 or 1.1 (desktop app requires TLS 1.2+). Please upgrade your server's TLS configuration or use the web version.`; + } else if (errorLower.includes('timeout') || errorLower.includes('timed out')) { detailedMessage = `Timeout after ${duration}ms - server not responding`; } else if (errorLower.includes('certificate') || errorLower.includes('cert') || errorLower.includes('ssl') || errorLower.includes('tls')) { detailedMessage = `SSL/TLS error - ${errorMsg}`; diff --git a/frontend/src/desktop/services/tauriHttpClient.ts b/frontend/src/desktop/services/tauriHttpClient.ts index f83d46d1f..6fcec36bb 100644 --- a/frontend/src/desktop/services/tauriHttpClient.ts +++ b/frontend/src/desktop/services/tauriHttpClient.ts @@ -193,12 +193,26 @@ class TauriHttpClient { const credentials: RequestCredentials = finalConfig.withCredentials ? 'include' : 'omit'; // Make the request using Tauri's native HTTP client (standard Fetch API) - const response = await fetch(url, { + // Enable certificate bypass for HTTPS to handle missing intermediate certs and self-signed certs + const fetchOptions: any = { method, headers, body, credentials, - }); + }; + + // Always enable dangerous settings for HTTPS to allow connections to servers with: + // - Missing intermediate certificates + // - Self-signed certificates + // - Certificate hostname mismatches + if (url.startsWith('https://')) { + fetchOptions.danger = { + acceptInvalidCerts: true, + acceptInvalidHostnames: true, + }; + } + + const response = await fetch(url, fetchOptions); // Parse response based on responseType let data: T; diff --git a/frontend/src/proprietary/testing/serverExperienceSimulations.ts b/frontend/src/proprietary/testing/serverExperienceSimulations.ts index 1c3b5a6ad..056a4aa09 100644 --- a/frontend/src/proprietary/testing/serverExperienceSimulations.ts +++ b/frontend/src/proprietary/testing/serverExperienceSimulations.ts @@ -48,7 +48,7 @@ const FREE_LICENSE_INFO: LicenseInfo = { const BASE_NO_LOGIN_CONFIG: AppConfig = { enableAnalytics: true, - appVersion: '2.4.0', + appVersion: '2.4.1', serverCertificateEnabled: false, enableAlphaFunctionality: false, enableDesktopInstallSlide: true,