From 2d2e1e937c4c30f1cfdf9fc785139401eec6fd6c Mon Sep 17 00:00:00 2001 From: Bilal Elmoussaoui Date: Tue, 31 Mar 2026 22:55:52 +0100 Subject: [PATCH 1/4] client: Test migration helper As we have all the infrastructure to do so. --- client/src/migration.rs | 153 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 141 insertions(+), 12 deletions(-) diff --git a/client/src/migration.rs b/client/src/migration.rs index 753f52a95..30e2add50 100644 --- a/client/src/migration.rs +++ b/client/src/migration.rs @@ -1,4 +1,6 @@ -use crate::{AsAttributes, Result, dbus::Service, file::UnlockedKeyring}; +use std::path::Path; + +use crate::{AsAttributes, Result, Secret, dbus::Service, file::UnlockedKeyring}; /// Helper to migrate your secrets from the host Secret Service /// to the sandboxed file backend. @@ -7,17 +9,29 @@ use crate::{AsAttributes, Result, dbus::Service, file::UnlockedKeyring}; /// Secret Service. pub async fn migrate(attributes: Vec, replace: bool) -> Result<()> { let service = Service::new().await?; - let secret = crate::Secret::sandboxed().await?; - let file_backend = - match UnlockedKeyring::load(crate::file::api::Keyring::default_path()?, secret).await { - Ok(file) => Ok(file), - Err(super::file::Error::Portal(ashpd::Error::PortalNotFound(_))) => { - #[cfg(feature = "tracing")] - tracing::debug!("Portal not available, no migration to do"); - return Ok(()); - } - Err(err) => Err(err), - }?; + let secret = match Secret::sandboxed().await { + Ok(secret) => Ok(secret), + Err(super::file::Error::Portal(ashpd::Error::PortalNotFound(_))) => { + #[cfg(feature = "tracing")] + tracing::debug!("Portal not available, no migration to do"); + return Ok(()); + } + Err(err) => Err(err), + }?; + let keyring_path = crate::file::api::Keyring::default_path()?; + + migrate_inner(&service, secret, &keyring_path, attributes, replace).await +} + +/// Inner migration function for testing. +async fn migrate_inner( + service: &Service, + secret: Secret, + keyring_path: &Path, + attributes: Vec, + replace: bool, +) -> Result<()> { + let file_backend = UnlockedKeyring::load(keyring_path, secret).await?; let collection = service.default_collection().await?; let mut all_items = Vec::default(); @@ -58,3 +72,118 @@ pub async fn migrate(attributes: Vec, replace: bool) -> Resul Ok(()) } + +#[cfg(test)] +mod tests { + use super::*; + use crate::{Secret, dbus::Service, file::UnlockedKeyring}; + + #[tokio::test] + #[cfg(feature = "tokio")] + async fn test_migrate_from_dbus_to_file() { + let temp_dir = tempfile::tempdir().unwrap(); + let setup = oo7_server::tests::TestServiceSetup::plain_session(true) + .await + .unwrap(); + + // Create a DBus service with test connection + let service = Service::new_with_connection(&setup.client_conn) + .await + .unwrap(); + + // Create some items on the DBus backend + let collection = service.default_collection().await.unwrap(); + + collection + .create_item( + "Migration Test 1", + &[("app", "test-migration"), ("user", "alice")], + "secret1", + false, + None, + ) + .await + .unwrap(); + + collection + .create_item( + "Migration Test 2", + &[("app", "test-migration"), ("user", "bob")], + "secret2", + false, + None, + ) + .await + .unwrap(); + + // Verify items exist in DBus backend + let items_before = collection + .search_items(&[("app", "test-migration")]) + .await + .unwrap(); + assert_eq!(items_before.len(), 2); + + // Create file backend keyring + let keyring_path = temp_dir.path().join("migrated.keyring"); + let secret = Secret::from([1, 2].into_iter().cycle().take(64).collect::>()); + + // Perform migration using internal function + migrate_inner( + &service, + secret.clone(), + &keyring_path, + vec![&[("app", "test-migration")]], + false, + ) + .await + .unwrap(); + + // Verify items are deleted from DBus backend + let items_after = collection + .search_items(&[("app", "test-migration")]) + .await + .unwrap(); + assert_eq!(items_after.len(), 0); + + // Verify items exist in file backend + let file_backend = UnlockedKeyring::load(&keyring_path, secret).await.unwrap(); + let migrated_items = file_backend + .search_items(&[("app", "test-migration")]) + .await + .unwrap(); + + assert_eq!(migrated_items.len(), 2); + + // Verify item details + let alice_item = migrated_items + .iter() + .find(|item| { + item.attributes() + .get("user") + .map(|u| u == "alice") + .unwrap_or(false) + }) + .expect("Alice's item should exist"); + + assert_eq!(alice_item.label(), "Migration Test 1"); + assert_eq!(alice_item.secret(), Secret::text("secret1")); + assert_eq!( + alice_item.attributes().get("app").unwrap(), + "test-migration" + ); + + let bob_item = migrated_items + .iter() + .find(|item| { + item.attributes() + .get("user") + .map(|u| u == "bob") + .unwrap_or(false) + }) + .expect("Bob's item should exist"); + + assert_eq!(bob_item.label(), "Migration Test 2"); + assert_eq!(bob_item.secret(), Secret::text("secret2")); + assert_eq!(bob_item.attributes().get("app").unwrap(), "test-migration"); + } +} From 2296845abedd481f27b7b2fd1591e72d5ac37e4d Mon Sep 17 00:00:00 2001 From: Bilal Elmoussaoui Date: Tue, 31 Mar 2026 22:56:44 +0100 Subject: [PATCH 2/4] coverage: Ignore binary files As main.rs could use some integration tests but the API surface for now is too small. For capability, we can't do much to test it without contaminating the user's workstation. So skip those for now. --- coverage.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/coverage.sh b/coverage.sh index 50e5a000a..1940fa7c9 100755 --- a/coverage.sh +++ b/coverage.sh @@ -60,7 +60,9 @@ grcov coverage-raw/combined.info \ --ignore "**/examples/*" \ --ignore "**/kwallet/*" \ --ignore "**/target/*" \ - --ignore "**/error.rs" + --ignore "**/error.rs" \ + --ignore "**/main.rs" \ + --ignore "**/capability.rs" # Generate HTML report with grcov grcov coverage-raw/combined.info \ @@ -78,7 +80,9 @@ grcov coverage-raw/combined.info \ --ignore "**/examples/*" \ --ignore "**/kwallet/*" \ --ignore "**/target/*" \ - --ignore "**/error.rs" + --ignore "**/error.rs" \ + --ignore "**/main.rs" \ + --ignore "**/capability.rs" # Extract and display coverage percentage if [ -f coverage/html/coverage.json ]; then From f3ecc7cfb94ea10daaad69e3c4fa9cceb697fcbd Mon Sep 17 00:00:00 2001 From: Bilal Elmoussaoui Date: Tue, 31 Mar 2026 22:58:55 +0100 Subject: [PATCH 3/4] kwallet: Replace md5 crate with the other one used in the workspace --- Cargo.lock | 8 +------- kwallet/parser/Cargo.toml | 2 +- kwallet/parser/src/crypto.rs | 3 ++- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 13d1cf685..fe7da9989 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -850,7 +850,7 @@ dependencies = [ "cipher", "ecb", "hex", - "md5", + "md-5", "pbkdf2", "serde", "serde_json", @@ -945,12 +945,6 @@ dependencies = [ "digest", ] -[[package]] -name = "md5" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" - [[package]] name = "memchr" version = "2.8.0" diff --git a/kwallet/parser/Cargo.toml b/kwallet/parser/Cargo.toml index 5f06c1a65..38b9d5a56 100644 --- a/kwallet/parser/Cargo.toml +++ b/kwallet/parser/Cargo.toml @@ -16,7 +16,7 @@ exclude.workspace = true blowfish = "0.9" cbc = "0.1" ecb = "0.1" -md5 = "0.7" +md-5 = "0.10" sha1 = "0.10" sha2 = "0.10" pbkdf2 = { version = "0.12", default-features = false, features = ["simple"] } diff --git a/kwallet/parser/src/crypto.rs b/kwallet/parser/src/crypto.rs index 2d9d34c78..582ec6c0d 100644 --- a/kwallet/parser/src/crypto.rs +++ b/kwallet/parser/src/crypto.rs @@ -222,7 +222,8 @@ fn validate_sha1(data: &[u8], expected_hash: &[u8]) -> Result<()> { /// Compute MD5 hash pub fn compute_md5(data: &[u8]) -> [u8; 16] { - md5::compute(data).into() + use md5::Digest; + md5::Md5::digest(data).into() } /// Extract wallet data from decrypted payload and validate SHA-1 hash From 2042b22a4d238a6fed7feea0f4b90c10a5ae6929 Mon Sep 17 00:00:00 2001 From: Bilal Elmoussaoui Date: Tue, 31 Mar 2026 23:10:00 +0100 Subject: [PATCH 4/4] Update deps --- Cargo.lock | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fe7da9989..c947c990f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -819,9 +819,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "js-sys" -version = "0.3.92" +version = "0.3.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc4c90f45aa2e6eacbe8645f77fdea542ac97a494bcd117a67df9ff4d611f995" +checksum = "797146bb2677299a1eb6b7b50a890f4c361b29ef967addf5b2fa45dae1bb6d7d" dependencies = [ "once_cell", "wasm-bindgen", @@ -1946,32 +1946,32 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "1.1.0+spec-1.1.0" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97251a7c317e03ad83774a8752a7e81fb6067740609f75ea2b585b569a59198f" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" dependencies = [ "serde_core", ] [[package]] name = "toml_edit" -version = "0.25.8+spec-1.1.0" +version = "0.25.9+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16bff38f1d86c47f9ff0647e6838d7bb362522bdf44006c7068c2b1e606f1f3c" +checksum = "da053d28fe57e2c9d21b48261e14e7b4c8b670b54d2c684847b91feaf4c7dac5" dependencies = [ "indexmap", "toml_datetime", "toml_parser", - "winnow 1.0.0", + "winnow 1.0.1", ] [[package]] name = "toml_parser" -version = "1.1.0+spec-1.1.0" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2334f11ee363607eb04df9b8fc8a13ca1715a72ba8662a26ac285c98aabb4011" +checksum = "39ca317ebc49f06bd748bfba29533eac9485569dc9bf80b849024b025e814fb9" dependencies = [ - "winnow 1.0.0", + "winnow 1.0.1", ] [[package]] @@ -2137,9 +2137,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.115" +version = "0.2.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6523d69017b7633e396a89c5efab138161ed5aafcbc8d3e5c5a42ae38f50495a" +checksum = "7dc0882f7b5bb01ae8c5215a1230832694481c1a4be062fd410e12ea3da5b631" dependencies = [ "cfg-if", "once_cell", @@ -2150,9 +2150,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.115" +version = "0.2.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e3a6c758eb2f701ed3d052ff5737f5bfe6614326ea7f3bbac7156192dc32e67" +checksum = "75973d3066e01d035dbedaad2864c398df42f8dd7b1ea057c35b8407c015b537" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2160,9 +2160,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.115" +version = "0.2.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "921de2737904886b52bcbb237301552d05969a6f9c40d261eb0533c8b055fedf" +checksum = "91af5e4be765819e0bcfee7322c14374dc821e35e72fa663a830bbc7dc199eac" dependencies = [ "bumpalo", "proc-macro2", @@ -2173,9 +2173,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.115" +version = "0.2.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a93e946af942b58934c604527337bad9ae33ba1d5c6900bbb41c2c07c2364a93" +checksum = "c9bf0406a78f02f336bf1e451799cca198e8acde4ffa278f0fb20487b150a633" dependencies = [ "unicode-ident", ] @@ -2418,9 +2418,9 @@ dependencies = [ [[package]] name = "winnow" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a90e88e4667264a994d34e6d1ab2d26d398dcdca8b7f52bec8668957517fc7d8" +checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5" dependencies = [ "memchr", ]