diff --git a/Cargo.lock b/Cargo.lock index cf16499..56ebea6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1840,6 +1840,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7f05c1d5476066defcdfacce1f52fc3cae3af1d3089727100c02ae92e5abbe0" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -2089,6 +2098,7 @@ dependencies = [ "secrecy", "serde", "serde_json", + "serde_path_to_error", "standback", "stderrlog", "steamguard", diff --git a/Cargo.toml b/Cargo.toml index 249da71..0d5a65f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,6 +60,7 @@ qrcode = { version = "0.12.0", optional = true } gethostname = "0.4.3" secrecy = { version = "0.8", features = ["serde"] } zeroize = "^1.4.3" +serde_path_to_error = "0.1.11" [dev-dependencies] tempdir = "0.3" diff --git a/src/accountmanager.rs b/src/accountmanager.rs index fc6891d..608f2b6 100644 --- a/src/accountmanager.rs +++ b/src/accountmanager.rs @@ -53,7 +53,8 @@ impl AccountManager { let mut reader = BufReader::new(file); let mut buffer = String::new(); reader.read_to_string(&mut buffer)?; - let manifest: Manifest = match serde_json::from_str(&buffer) { + let mut deser = serde_json::Deserializer::from_str(&buffer); + let manifest: Manifest = match serde_path_to_error::deserialize(&mut deser) { Ok(m) => m, Err(orig_err) => match serde_json::from_str::(&buffer) { Ok(_) => return Err(ManifestLoadError::MigrationNeeded)?, @@ -159,7 +160,8 @@ impl AccountManager { let file = File::open(path)?; let reader = BufReader::new(file); - let account: SteamGuardAccount = serde_json::from_reader(reader)?; + let mut deser = serde_json::Deserializer::from_reader(reader); + let account: SteamGuardAccount = serde_path_to_error::deserialize(&mut deser)?; ensure!( !self.account_exists(&account.account_name), "Account already exists in manifest, please remove it first." @@ -361,12 +363,16 @@ impl EntryLoader for ManifestEntry { return Err(ManifestAccountLoadError::IncorrectPasskey); } let s = std::str::from_utf8(&plaintext).unwrap(); - serde_json::from_str(s)? + let mut deser = serde_json::Deserializer::from_str(s); + serde_path_to_error::deserialize(&mut deser)? } (None, Some(_)) => { return Err(ManifestAccountLoadError::MissingPasskey); } - (_, None) => serde_json::from_reader(reader)?, + (_, None) => { + let mut deser = serde_json::Deserializer::from_reader(reader); + serde_path_to_error::deserialize(&mut deser)? + } }; Ok(account) } @@ -378,8 +384,8 @@ pub enum ManifestLoadError { Missing(#[from] std::io::Error), #[error("Manifest needs to be migrated to the latest format.")] MigrationNeeded, - #[error("Failed to deserialize the manifest.")] - DeserializationFailed(#[from] serde_json::Error), + #[error("Failed to deserialize the manifest. {self:?}")] + DeserializationFailed(#[from] serde_path_to_error::Error), #[error(transparent)] Unknown(#[from] anyhow::Error), } @@ -394,8 +400,8 @@ pub enum ManifestAccountLoadError { IncorrectPasskey, #[error("Failed to decrypt account. {self:?}")] DecryptionFailed(#[from] crate::encryption::EntryEncryptionError), - #[error("Failed to deserialize the account.")] - DeserializationFailed(#[from] serde_json::Error), + #[error("Failed to deserialize the account. {self:?}")] + DeserializationFailed(#[from] serde_path_to_error::Error), #[error(transparent)] Unknown(#[from] anyhow::Error), } diff --git a/src/accountmanager/legacy.rs b/src/accountmanager/legacy.rs index 63202a9..aa5c2c5 100644 --- a/src/accountmanager/legacy.rs +++ b/src/accountmanager/legacy.rs @@ -88,12 +88,16 @@ impl EntryLoader for SdaManifestEntry { return Err(ManifestAccountLoadError::IncorrectPasskey); } let s = std::str::from_utf8(&plaintext).unwrap(); - serde_json::from_str(s)? + let mut deser = serde_json::Deserializer::from_str(s); + serde_path_to_error::deserialize(&mut deser)? } (None, Some(_)) => { return Err(ManifestAccountLoadError::MissingPasskey); } - (_, None) => serde_json::from_reader(reader)?, + (_, None) => { + let mut deser = serde_json::Deserializer::from_reader(reader); + serde_path_to_error::deserialize(&mut deser)? + } }; Ok(account) } diff --git a/src/accountmanager/migrate.rs b/src/accountmanager/migrate.rs index 55b6a7d..388698a 100644 --- a/src/accountmanager/migrate.rs +++ b/src/accountmanager/migrate.rs @@ -1,7 +1,7 @@ use std::{fs::File, io::Read, path::Path}; use log::debug; -use serde::de::Error; +use serde::{de::Error, Deserialize}; use steamguard::SteamGuardAccount; use thiserror::Error; @@ -82,7 +82,7 @@ pub(crate) enum MigrationError { #[error("Passkey is required to decrypt manifest")] MissingPasskey, #[error("Failed to deserialize manifest: {0}")] - ManifestDeserializeFailed(serde_json::Error), + ManifestDeserializeFailed(serde_path_to_error::Error), #[error("IO error when upgrading manifest: {0}")] IoError(#[from] std::io::Error), #[error("An unexpected error occurred during manifest migration: {0}")] @@ -180,20 +180,44 @@ impl From for Manifest { } } -fn deserialize_manifest(text: String) -> Result { - let json: serde_json::Value = serde_json::from_str(&text)?; - debug!("deserializing manifest: version {}", json["version"]); - if json["version"] == 1 { - let manifest: ManifestV1 = serde_json::from_str(&text)?; - Ok(MigratingManifest::ManifestV1(manifest)) - } else if json["version"] == serde_json::Value::Null { - let manifest: SdaManifest = serde_json::from_str(&text)?; - Ok(MigratingManifest::Sda(manifest)) - } else { - Err(serde_json::Error::custom(format!( - "Unknown manifest version: {}", - json["version"] - ))) +#[derive(Deserialize)] +struct JustVersion { + version: Option, +} + +fn deserialize_manifest( + text: String, +) -> Result> { + let mut deser = serde_json::Deserializer::from_str(&text); + let version: JustVersion = serde_path_to_error::deserialize(&mut deser)?; + + debug!("deserializing manifest: version {:?}", version.version); + let mut deser = serde_json::Deserializer::from_str(&text); + + match version.version { + Some(1) => { + let manifest: ManifestV1 = serde_path_to_error::deserialize(&mut deser)?; + Ok(MigratingManifest::ManifestV1(manifest)) + } + None => { + let manifest: SdaManifest = serde_path_to_error::deserialize(&mut deser)?; + Ok(MigratingManifest::Sda(manifest)) + } + _ => { + // HACK: there's no way to construct the Path type, so we create it by forcing a deserialize error + + #[derive(Debug)] + struct Dummy; + impl<'de> Deserialize<'de> for Dummy { + fn deserialize>(_: D) -> Result { + Err(D::Error::custom("Unknown manifest version".to_string())) + } + } + + let mut deser = serde_json::Deserializer::from_str(""); + let err = serde_path_to_error::deserialize::<_, Dummy>(&mut deser).unwrap_err(); + Err(err) + } } }