From 57ea58fc3cd7f131e8eca216f2fc7b3763be011a Mon Sep 17 00:00:00 2001 From: David Perez Date: Thu, 21 Nov 2024 13:35:57 -0600 Subject: [PATCH] Log vault deserialization errors (#4353) --- .../data/platform/util/JsonExtensions.kt | 23 +++++++++++++++++-- .../datasource/disk/VaultDiskSourceImpl.kt | 16 +++++++++---- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/x8bit/bitwarden/data/platform/util/JsonExtensions.kt b/app/src/main/java/com/x8bit/bitwarden/data/platform/util/JsonExtensions.kt index b481db793f..2a63f27dc3 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/platform/util/JsonExtensions.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/platform/util/JsonExtensions.kt @@ -12,8 +12,27 @@ inline fun Json.decodeFromStringOrNull( ): T? = try { decodeFromString(string = string) - } catch (e: SerializationException) { + } catch (_: SerializationException) { null - } catch (e: IllegalArgumentException) { + } catch (_: IllegalArgumentException) { null } + +/** + * Attempts to decode the given JSON [string] into the given type [T]. If there is an error in + * processing the JSON or deserializing, the exception is still throw after [onFailure] lambda is + * invoked. + */ +inline fun Json.decodeFromStringWithErrorCallback( + string: String, + onFailure: (throwable: Throwable) -> Unit, +): T = + try { + decodeFromString(string = string) + } catch (se: SerializationException) { + onFailure(se) + throw se + } catch (iae: IllegalArgumentException) { + onFailure(iae) + throw iae + } diff --git a/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/disk/VaultDiskSourceImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/disk/VaultDiskSourceImpl.kt index 9631f1441e..ce659e852d 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/disk/VaultDiskSourceImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/disk/VaultDiskSourceImpl.kt @@ -2,6 +2,7 @@ package com.x8bit.bitwarden.data.vault.datasource.disk import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow +import com.x8bit.bitwarden.data.platform.util.decodeFromStringWithErrorCallback import com.x8bit.bitwarden.data.vault.datasource.disk.dao.CiphersDao import com.x8bit.bitwarden.data.vault.datasource.disk.dao.CollectionsDao import com.x8bit.bitwarden.data.vault.datasource.disk.dao.DomainsDao @@ -24,6 +25,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json +import timber.log.Timber /** * Default implementation of [VaultDiskSource]. @@ -70,9 +72,9 @@ class VaultDiskSourceImpl( entities .map { entity -> async { - json.decodeFromString( + json.decodeFromStringWithErrorCallback( string = entity.cipherJson, - ) + ) { Timber.e(it, "Failed to deserialize Cipher in Vault") } } } .awaitAll() @@ -126,7 +128,11 @@ class VaultDiskSourceImpl( .getDomains(userId) .map { entity -> withContext(dispatcherManager.default) { - entity?.domainsJson?.let { json.decodeFromString(it) } + entity?.domainsJson?.let { domains -> + json.decodeFromStringWithErrorCallback( + string = domains, + ) { Timber.e(it, "Failed to deserialize Domains in Vault") } + } } } @@ -192,7 +198,9 @@ class VaultDiskSourceImpl( entities .map { entity -> async { - json.decodeFromString(entity.sendJson) + json.decodeFromStringWithErrorCallback( + string = entity.sendJson, + ) { Timber.e(it, "Failed to deserialize Send in Vault") } } } .awaitAll()