From 27231c8eecf92ae212558479ffba2a8e9fea0724 Mon Sep 17 00:00:00 2001 From: Davide Grilli Date: Thu, 2 Jul 2026 23:19:43 +0200 Subject: [PATCH] fix(core): stop corrupted state from permanently breaking SSL and wallet loading CertificatePinStore.Load threw on a corrupted pin file, blocking every SSL connection until the user manually deleted it; EncryptedFile.IsEncrypted threw on valid JSON with a non-object root or invalid UTF-16 instead of returning false. Both now degrade gracefully. Found by property-based tests while extending the test suite. --- src/Core/Net/CertificatePinStore.cs | 19 +++++++++++++++---- src/Core/Storage/EncryptedFile.cs | 10 +++++++++- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/Core/Net/CertificatePinStore.cs b/src/Core/Net/CertificatePinStore.cs index 6fc2f1c..a61baca 100644 --- a/src/Core/Net/CertificatePinStore.cs +++ b/src/Core/Net/CertificatePinStore.cs @@ -58,10 +58,21 @@ public sealed class CertificatePinStore(string filePath) } } - private Dictionary Load() => - File.Exists(filePath) - ? JsonSerializer.Deserialize>(File.ReadAllText(filePath)) ?? [] - : []; + private Dictionary Load() + { + if (!File.Exists(filePath)) + return []; + try + { + return JsonSerializer.Deserialize>(File.ReadAllText(filePath)) ?? []; + } + catch (JsonException) + { + // A corrupt pin file must not make every SSL connection fail forever: + // fall back to first-contact TOFU (same trust level as the bootstrap). + return []; + } + } private void Save(Dictionary pins) { diff --git a/src/Core/Storage/EncryptedFile.cs b/src/Core/Storage/EncryptedFile.cs index f560637..5736b0e 100644 --- a/src/Core/Storage/EncryptedFile.cs +++ b/src/Core/Storage/EncryptedFile.cs @@ -26,13 +26,21 @@ public static class EncryptedFile try { using var doc = JsonDocument.Parse(fileContent); - return doc.RootElement.TryGetProperty("Format", out var f) + return doc.RootElement.ValueKind == JsonValueKind.Object + && doc.RootElement.TryGetProperty("Format", out var f) + && f.ValueKind == JsonValueKind.String && f.GetString() == Format; } catch (JsonException) { return false; } + catch (ArgumentException) + { + // Invalid UTF-16 (lone surrogates) cannot be transcoded for parsing: + // certainly not an encrypted container. + return false; + } } public static string Encrypt(string plaintext, string password)