From 2cb219d30faceaa0999b20d98153abfdf61a0ee8 Mon Sep 17 00:00:00 2001 From: Glenn Gore Date: Sun, 24 May 2026 17:46:56 +0800 Subject: [PATCH 1/7] fix(build): import arboard::Clipboard in did_keys_export_show The clipboard-clear logic added in 56b3642 references `Clipboard::new()` and `clipboard.get_text()` without importing `arboard::Clipboard`. The adjacent [C] copy handler goes through `crate::clipboard::copy_to_clipboard` (an OSC 52 / arboard abstraction) which is why the file had no direct arboard import, and why the regression slipped past local `cargo check --all-features` but failed default-features CI on PR #61 with E0433. Signed-off-by: Glenn Gore --- openvtc/src/ui/pages/setup_flow/did_keys_export_show.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/openvtc/src/ui/pages/setup_flow/did_keys_export_show.rs b/openvtc/src/ui/pages/setup_flow/did_keys_export_show.rs index 3bb5a01..5b67ddc 100644 --- a/openvtc/src/ui/pages/setup_flow/did_keys_export_show.rs +++ b/openvtc/src/ui/pages/setup_flow/did_keys_export_show.rs @@ -1,6 +1,7 @@ use crate::colors::{ COLOR_BORDER, COLOR_ORANGE, COLOR_SOFT_PURPLE, COLOR_SUCCESS, COLOR_TEXT_DEFAULT, }; +use arboard::Clipboard; use crossterm::event::{KeyCode, KeyEvent}; use ratatui::{ Frame, From 34f605fea7cddaf36086516f4927565d6a24d609 Mon Sep 17 00:00:00 2001 From: Glenn Gore Date: Sun, 24 May 2026 17:47:01 +0800 Subject: [PATCH 2/7] chore(deps): bump vta-sdk 0.6 -> 0.7 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Picks up the 0.7.0 release of the Verifiable Trust Infrastructure SDK (github.com/OpenVTC/verifiable-trust-infrastructure). No API changes required at the openvtc call sites — workspace builds clean under default and --no-default-features, tests and clippy pass. Signed-off-by: Glenn Gore --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 6a53ef6..0e54d97 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,7 +77,7 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] } tui-input = "0.15" url = "2.5" uuid = { version = "1.23", features = ["v4", "fast-rng"] } -vta-sdk = { version = "0.6", features = [ +vta-sdk = { version = "0.7", features = [ "session", "client", "didcomm", From 5234f8a3bf9d1abf505a86de6b4afc67b583266d Mon Sep 17 00:00:00 2001 From: Glenn Gore Date: Sun, 24 May 2026 17:47:08 +0800 Subject: [PATCH 3/7] chore(deps): cargo update Pulls the latest compatible patch versions of the Affinidi / VTA / WASM dependency stacks: affinidi-crypto 0.1.5 -> 0.1.6 affinidi-messaging-mediator 0.15.3 -> 0.15.4 affinidi-messaging-test-mediator 0.2.2 -> 0.2.3 didwebvh-rs 0.5.2 -> 0.5.3 vta-sdk 0.5.0 -> 0.7.0 (lockfile resync) serde_json 1.0.149 -> 1.0.150 autocfg, bumpalo, js-sys, wasm-bindgen*, web-sys No Cargo.toml ranges changed beyond the explicit vta-sdk minor bump in the previous commit. Signed-off-by: Glenn Gore --- Cargo.lock | 86 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 44 insertions(+), 42 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8516561..3c2c167 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -85,9 +85,9 @@ dependencies = [ [[package]] name = "affinidi-crypto" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47c0fb004d079a20c37bd74bfb1d5be7122aaec25dcc6265c684e46b8b7b26c1" +checksum = "63cac399b6d7547a9af38375e596dc6df60d206d02eec037e69f30600a37dac6" dependencies = [ "affinidi-encoding", "base58", @@ -313,9 +313,9 @@ dependencies = [ [[package]] name = "affinidi-messaging-mediator" -version = "0.15.3" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf259e6125089547a106034ee0cb47af7cbe37a3dbe74f42442391d976c6b46" +checksum = "51867d375bcce7c5e97bceffd14a50a72907ca0577be7acb95568f787f2ea29e" dependencies = [ "affinidi-did-common", "affinidi-did-resolver-cache-sdk", @@ -366,7 +366,7 @@ dependencies = [ "tracing-subscriber", "url", "uuid", - "vta-sdk 0.5.0", + "vta-sdk 0.6.0", ] [[package]] @@ -465,9 +465,9 @@ dependencies = [ [[package]] name = "affinidi-messaging-test-mediator" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a234146cb485879b403b3f3a5fb3832e897fe94a2caadb96dfa9fd39deb1081" +checksum = "a527868dba7d46940ea26b2daec2a41d97ee6f4e86d818209314f0b0ba7473b7" dependencies = [ "affinidi-did-resolver-cache-sdk", "affinidi-messaging-mediator", @@ -831,9 +831,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "aws-lc-rs" @@ -1226,9 +1226,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.20.2" +version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "bytemuck" @@ -2262,7 +2262,7 @@ dependencies = [ "tokio", "tracing", "tracing-subscriber", - "vta-sdk 0.6.0", + "vta-sdk 0.7.0", "windows-native-keyring-store", "zeroize", ] @@ -2324,9 +2324,9 @@ dependencies = [ [[package]] name = "didwebvh-rs" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a1ebf5a11b92dfe193a288c2c5173a913b9bbed139d56b5dcef70c9fa7bea2a" +checksum = "854fcce63deb51e3baac8a5fddc5512c9f437e3ff81c78a78bd08f931672f7ce" dependencies = [ "affinidi-data-integrity", "affinidi-did-common", @@ -2336,6 +2336,7 @@ dependencies = [ "base58", "chrono", "getrandom 0.4.2", + "percent-encoding", "reqwest 0.13.3", "serde", "serde_json", @@ -3936,9 +3937,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.98" +version = "0.3.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67df7112613f8bfd9150013a0314e196f4800d3201ae742489d999db2f979f08" +checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11" dependencies = [ "cfg-if", "futures-util", @@ -5134,7 +5135,7 @@ dependencies = [ [[package]] name = "openvtc" -version = "0.2.0" +version = "0.2.1" dependencies = [ "aes-gcm", "affinidi-data-integrity", @@ -5184,7 +5185,7 @@ dependencies = [ "tui-input", "url", "uuid", - "vta-sdk 0.6.0", + "vta-sdk 0.7.0", "windows-native-keyring-store", "x25519-dalek", "zeroize", @@ -5192,7 +5193,7 @@ dependencies = [ [[package]] name = "openvtc-core" -version = "0.2.0" +version = "0.2.1" dependencies = [ "aes-gcm", "affinidi-data-integrity", @@ -5232,14 +5233,14 @@ dependencies = [ "tracing-subscriber", "url", "uuid", - "vta-sdk 0.6.0", + "vta-sdk 0.7.0", "x25519-dalek", "zeroize", ] [[package]] name = "openvtc-service" -version = "0.2.0" +version = "0.2.1" dependencies = [ "affinidi-tdk", "anyhow", @@ -6457,7 +6458,7 @@ dependencies = [ [[package]] name = "robotic-maintainers" -version = "0.2.0" +version = "0.2.1" dependencies = [ "affinidi-tdk", "anyhow", @@ -6845,9 +6846,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.149" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" dependencies = [ "itoa", "memchr", @@ -8521,9 +8522,9 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "vta-sdk" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fba075fc5275473109af54d96e91d3828d1f3c10a77d3089e672efa103f7dbfd" +checksum = "6bff1e0c2c00bc1ccdc85d380f8f3a63b1f1a420ea9e8bda3da871aa5cb108e9" dependencies = [ "aes 0.9.0", "aes-gcm", @@ -8542,21 +8543,21 @@ dependencies = [ "thiserror 2.0.18", "tokio", "tracing", + "url", "uuid", "x25519-dalek", ] [[package]] name = "vta-sdk" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bff1e0c2c00bc1ccdc85d380f8f3a63b1f1a420ea9e8bda3da871aa5cb108e9" +checksum = "bcfddb2f41f35ad49650e63848aa32ddaecb7f24b4e2337561d7a586e2fd7faf" dependencies = [ - "aes 0.9.0", - "aes-gcm", "affinidi-crypto", "affinidi-data-integrity", "affinidi-did-resolver-cache-sdk", + "affinidi-messaging-didcomm", "affinidi-secrets-resolver", "affinidi-tdk", "affinidi-vc", @@ -8564,6 +8565,7 @@ dependencies = [ "chrono", "ciborium", "curve25519-dalek", + "didwebvh-rs", "ed25519-dalek", "getrandom 0.4.2", "hpke", @@ -8635,9 +8637,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.121" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49ace1d07c165b0864824eee619580c4689389afa9dc9ed3a4c75040d82e6790" +checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409" dependencies = [ "cfg-if", "once_cell", @@ -8648,9 +8650,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.71" +version = "0.4.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96492d0d3ffba25305a7dc88720d250b1401d7edca02cc3bcd50633b424673b8" +checksum = "9473dbd2991ae90b6291c3c32c30c6187ac49aa32f9905d1cce280ec1e110b0f" dependencies = [ "js-sys", "wasm-bindgen", @@ -8658,9 +8660,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.121" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e68e6f4afd367a562002c05637acb8578ff2dea1943df76afb9e83d177c8578" +checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -8668,9 +8670,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.121" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d95a9ec35c64b2a7cb35d3fead40c4238d0940c86d107136999567a4703259f2" +checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e" dependencies = [ "bumpalo", "proc-macro2", @@ -8681,9 +8683,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.121" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4e0100b01e9f0d03189a92b96772a1fb998639d981193d7dbab487302513441" +checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437" dependencies = [ "unicode-ident", ] @@ -8804,9 +8806,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.98" +version = "0.3.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b572dff8bcf38bad0fa19729c89bb5748b2b9b1d8be70cf90df697e3a8f32aa" +checksum = "6d621441cfc37b84979402712047321980c178f299193a3589d05b99e8763436" dependencies = [ "js-sys", "wasm-bindgen", From f6e497e5c90f9d12921258e7a8df007e9493fdca Mon Sep 17 00:00:00 2001 From: Glenn Gore Date: Sun, 24 May 2026 20:48:27 +0800 Subject: [PATCH 4/7] fix(test): drop removed token fields from WebvhServerRecord fixture vta-sdk 0.7 moves `access_token`, `access_expires_at` and `refresh_token` off `WebvhServerRecord` and into a separate service-internal `WebvhServerAuthRecord` (keyspace prefix `server-auth:`). Public-surface records no longer carry token material so they don't leak into REST list responses, DIDComm `webvh.servers.list` results, or backup exports. Update the navigation test fixture to construct the new shape. Detected by default-features CI on PR #61 / #62 (`(bin "openvtc" test)` target) with E0560. Masked locally because earlier verification piped `cargo test --workspace` through `tail`, dropping cargo's non-zero exit code. Signed-off-by: Glenn Gore --- openvtc/src/ui/pages/setup_flow/navigation.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/openvtc/src/ui/pages/setup_flow/navigation.rs b/openvtc/src/ui/pages/setup_flow/navigation.rs index df04f52..265a66c 100644 --- a/openvtc/src/ui/pages/setup_flow/navigation.rs +++ b/openvtc/src/ui/pages/setup_flow/navigation.rs @@ -315,9 +315,6 @@ mod tests { id: "test-id".to_string(), did: "did:webvh:test".to_string(), label: Some("test".to_string()), - access_token: None, - access_expires_at: None, - refresh_token: None, created_at: Utc::now(), updated_at: Utc::now(), }]; From 37b5309e537b2f7387de151c63f3978af3fdb3a7 Mon Sep 17 00:00:00 2001 From: Glenn Gore Date: Sun, 24 May 2026 20:48:33 +0800 Subject: [PATCH 5/7] chore(ci): bump MSRV pin 1.94.0 -> 1.95.0 The workspace declares `rust-version = "1.95.0"` and `did-git-sign` depends on language features (e.g. let-chains) that require it, but the dedicated MSRV CI job still pinned `dtolnay/rust-toolchain@1.94.0`. That job has been red since the workspace rust-version was bumped; align the pin with the declared floor so MSRV verifies the version we actually support. Signed-off-by: Glenn Gore --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d819b05..2f3b9d1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -94,7 +94,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@1.94.0 + - uses: dtolnay/rust-toolchain@1.95.0 - uses: Swatinem/rust-cache@v2 - name: Install system dependencies run: sudo apt-get update && sudo apt-get install -y libpcsclite-dev libdbus-1-dev From d9b74e170013d8bacbac2138f598200155276a04 Mon Sep 17 00:00:00 2001 From: Glenn Gore Date: Sun, 24 May 2026 20:48:47 +0800 Subject: [PATCH 6/7] chore(clippy): apply Rust 1.95 stable lint suggestions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CI runs `cargo clippy --workspace --all-targets -- -D warnings`. Several lints that were warning-only in 1.94 became enforced after the workspace rust-version bumped to 1.95, leaving clippy red on main: did-git-sign/src/main.rs collapsible_else_if (uninstall scope) openvtc-core/tests/relationship_e2e.rs explicit_auto_deref on `*type_url` openvtc/src/state_handler/message_dispatch.rs items_after_test_module — move `mod tests` to file end (matches sibling modules' convention) openvtc/src/state_handler/mod.rs collapsible_if on the VTA-context lookup; collapse into a single chained `if matches!(...) && let ...` openvtc/src/state_handler/settings_actions.rs collapsible_match — hoist `field == 0` into the pattern guard openvtc/src/state_handler/setup_wizard.rs collapsible_match — `VtaCreateKeys` hoists cleanly; the two arms that bind owned values (`server_id`, `custom_path`, `webvh_address`) can't move into a match guard (E0507), so keep the inner-if form and `#[allow]` the lint locally openvtc/src/ui/pages/main/mod.rs collapsible_match on Up / Down arms openvtc/src/ui/pages/setup_flow/mod.rs default_constructed_unit_structs (`DidGitSignSetup` is a unit struct) No behaviour changes; pure refactors. Verified with `cargo clippy --workspace --all-targets -- -D warnings` clean and `cargo build --workspace [--no-default-features]` passing. Signed-off-by: Glenn Gore --- did-git-sign/src/main.rs | 6 +- openvtc-core/tests/relationship_e2e.rs | 2 +- openvtc/src/state_handler/message_dispatch.rs | 203 +++++++++--------- openvtc/src/state_handler/mod.rs | 26 ++- openvtc/src/state_handler/settings_actions.rs | 8 +- openvtc/src/state_handler/setup_wizard.rs | 12 +- openvtc/src/ui/pages/main/mod.rs | 20 +- openvtc/src/ui/pages/setup_flow/mod.rs | 2 +- 8 files changed, 137 insertions(+), 142 deletions(-) diff --git a/did-git-sign/src/main.rs b/did-git-sign/src/main.rs index e7f5df0..dcf7e32 100644 --- a/did-git-sign/src/main.rs +++ b/did-git-sign/src/main.rs @@ -742,12 +742,8 @@ fn cmd_uninstall( // install when one is present at the CWD, falling back to global. let global = if global_flag { true - } else if local_flag { - false - } else if SigningConfig::repo_local_path().exists() { - false } else { - true + !(local_flag || SigningConfig::repo_local_path().exists()) }; // Discover did_key_id: explicit override, or read from the diff --git a/openvtc-core/tests/relationship_e2e.rs b/openvtc-core/tests/relationship_e2e.rs index 233f0b0..1baa3ed 100644 --- a/openvtc-core/tests/relationship_e2e.rs +++ b/openvtc-core/tests/relationship_e2e.rs @@ -100,7 +100,7 @@ async fn start_profile_service( handler_fn(ignore_handler), )?; for type_url in routes { - router = router.route(*type_url, make_capture())?; + router = router.route(type_url, make_capture())?; } let shutdown = CancellationToken::new(); diff --git a/openvtc/src/state_handler/message_dispatch.rs b/openvtc/src/state_handler/message_dispatch.rs index c6a44ad..1cc9bd5 100644 --- a/openvtc/src/state_handler/message_dispatch.rs +++ b/openvtc/src/state_handler/message_dispatch.rs @@ -92,107 +92,6 @@ fn unix_now() -> u64 { .unwrap_or(0) } -#[cfg(test)] -mod tests { - use super::*; - - fn msg(id: &str, created: Option, expires: Option) -> Message { - let mut m = - Message::build(id.to_string(), "test".to_string(), serde_json::json!({})).finalize(); - m.created_time = created; - m.expires_time = expires; - m - } - - #[test] - fn seen_messages_marks_first_observation_unseen() { - let mut seen = SeenMessages::with_capacity(4); - assert!(!seen.observe("a")); - } - - #[test] - fn seen_messages_detects_replay() { - let mut seen = SeenMessages::with_capacity(4); - assert!(!seen.observe("a")); - assert!(seen.observe("a")); - } - - #[test] - fn seen_messages_evicts_oldest_at_capacity() { - let mut seen = SeenMessages::with_capacity(2); - assert!(!seen.observe("a")); - assert!(!seen.observe("b")); - // "b" still in cache. - assert!(seen.observe("b")); - // "c" pushes "a" out. - assert!(!seen.observe("c")); - // "a" was evicted — observing again should report unseen. - assert!(!seen.observe("a")); - } - - #[test] - fn check_message_age_accepts_message_with_no_timestamps() { - assert!(check_message_age(&msg("id", None, None)).is_ok()); - } - - #[test] - fn check_message_age_rejects_old_messages() { - let now = unix_now(); - let too_old = now - MAX_MESSAGE_AGE_SECS - 60; - assert!(check_message_age(&msg("id", Some(too_old), None)).is_err()); - } - - #[test] - fn check_message_age_rejects_future_messages() { - let now = unix_now(); - let too_future = now + MAX_FUTURE_SKEW_SECS + 60; - assert!(check_message_age(&msg("id", Some(too_future), None)).is_err()); - } - - #[test] - fn check_message_age_accepts_within_skew() { - let now = unix_now(); - // 1 minute in the future is fine. - assert!(check_message_age(&msg("id", Some(now + 60), None)).is_ok()); - } - - #[test] - fn check_message_age_rejects_expired_messages() { - let now = unix_now(); - // expires_time in the past - assert!(check_message_age(&msg("id", Some(now), Some(now - 60))).is_err()); - } - - #[test] - fn validate_did_accepts_well_formed_dids() { - assert!(validate_did("did:web:example.com").is_ok()); - assert!(validate_did("did:webvh:abcdef0123:example.com").is_ok()); - assert!(validate_did("did:peer:2.Vz6Mk-something").is_ok()); - assert!(validate_did("did:key:z6MkpzExampleKey").is_ok()); - assert!(validate_did("did:web:example.com%3A8080:path").is_ok()); - } - - #[test] - fn validate_did_rejects_old_prefix_loophole() { - // The previous validator accepted these — current one must not. - assert!(validate_did("did:").is_err()); - assert!(validate_did("did:abc").is_err()); // no msi - assert!(validate_did("did::abc").is_err()); // empty method - assert!(validate_did("not-a-did").is_err()); - assert!(validate_did("").is_err()); - } - - #[test] - fn validate_did_rejects_uppercase_method() { - assert!(validate_did("did:WEB:example.com").is_err()); - } - - #[test] - fn validate_did_rejects_msi_with_invalid_chars() { - assert!(validate_did("did:web:exam ple.com").is_err()); // space - assert!(validate_did("did:web:exam\u{200E}ple.com").is_err()); // LRM - } -} /// Validate the message timestamps. Returns `Err(reason)` if the message /// should be dropped as too old, expired, or implausibly future-dated. @@ -772,3 +671,105 @@ fn create_finalize_message( Some(task_id.as_str()), ) } + +#[cfg(test)] +mod tests { + use super::*; + + fn msg(id: &str, created: Option, expires: Option) -> Message { + let mut m = + Message::build(id.to_string(), "test".to_string(), serde_json::json!({})).finalize(); + m.created_time = created; + m.expires_time = expires; + m + } + + #[test] + fn seen_messages_marks_first_observation_unseen() { + let mut seen = SeenMessages::with_capacity(4); + assert!(!seen.observe("a")); + } + + #[test] + fn seen_messages_detects_replay() { + let mut seen = SeenMessages::with_capacity(4); + assert!(!seen.observe("a")); + assert!(seen.observe("a")); + } + + #[test] + fn seen_messages_evicts_oldest_at_capacity() { + let mut seen = SeenMessages::with_capacity(2); + assert!(!seen.observe("a")); + assert!(!seen.observe("b")); + // "b" still in cache. + assert!(seen.observe("b")); + // "c" pushes "a" out. + assert!(!seen.observe("c")); + // "a" was evicted — observing again should report unseen. + assert!(!seen.observe("a")); + } + + #[test] + fn check_message_age_accepts_message_with_no_timestamps() { + assert!(check_message_age(&msg("id", None, None)).is_ok()); + } + + #[test] + fn check_message_age_rejects_old_messages() { + let now = unix_now(); + let too_old = now - MAX_MESSAGE_AGE_SECS - 60; + assert!(check_message_age(&msg("id", Some(too_old), None)).is_err()); + } + + #[test] + fn check_message_age_rejects_future_messages() { + let now = unix_now(); + let too_future = now + MAX_FUTURE_SKEW_SECS + 60; + assert!(check_message_age(&msg("id", Some(too_future), None)).is_err()); + } + + #[test] + fn check_message_age_accepts_within_skew() { + let now = unix_now(); + // 1 minute in the future is fine. + assert!(check_message_age(&msg("id", Some(now + 60), None)).is_ok()); + } + + #[test] + fn check_message_age_rejects_expired_messages() { + let now = unix_now(); + // expires_time in the past + assert!(check_message_age(&msg("id", Some(now), Some(now - 60))).is_err()); + } + + #[test] + fn validate_did_accepts_well_formed_dids() { + assert!(validate_did("did:web:example.com").is_ok()); + assert!(validate_did("did:webvh:abcdef0123:example.com").is_ok()); + assert!(validate_did("did:peer:2.Vz6Mk-something").is_ok()); + assert!(validate_did("did:key:z6MkpzExampleKey").is_ok()); + assert!(validate_did("did:web:example.com%3A8080:path").is_ok()); + } + + #[test] + fn validate_did_rejects_old_prefix_loophole() { + // The previous validator accepted these — current one must not. + assert!(validate_did("did:").is_err()); + assert!(validate_did("did:abc").is_err()); // no msi + assert!(validate_did("did::abc").is_err()); // empty method + assert!(validate_did("not-a-did").is_err()); + assert!(validate_did("").is_err()); + } + + #[test] + fn validate_did_rejects_uppercase_method() { + assert!(validate_did("did:WEB:example.com").is_err()); + } + + #[test] + fn validate_did_rejects_msi_with_invalid_chars() { + assert!(validate_did("did:web:exam ple.com").is_err()); // space + assert!(validate_did("did:web:exam\u{200E}ple.com").is_err()); // LRM + } +} diff --git a/openvtc/src/state_handler/mod.rs b/openvtc/src/state_handler/mod.rs index 1dfea85..9eb23c9 100644 --- a/openvtc/src/state_handler/mod.rs +++ b/openvtc/src/state_handler/mod.rs @@ -359,21 +359,19 @@ impl StateHandler { if matches!( &config.key_backend, openvtc_core::config::KeyBackend::Vta { .. } - ) { - if let Ok(client) = - openvtc_core::config::build_runtime_vta_client(&config.key_backend).await - && let Ok(resp) = client.list_contexts().await + ) && let Ok(client) = + openvtc_core::config::build_runtime_vta_client(&config.key_backend).await + && let Ok(resp) = client.list_contexts().await + { + if let Some(ctx) = resp + .contexts + .iter() + .find(|c| c.did.as_deref() == Some(config.public.persona_did.as_str())) { - if let Some(ctx) = resp - .contexts - .iter() - .find(|c| c.did.as_deref() == Some(config.public.persona_did.as_str())) - { - state.main_page.content_panel.vta.context_name = Some(ctx.name.clone()); - } else if let Some(ctx) = resp.contexts.first() { - // Fallback to first context - state.main_page.content_panel.vta.context_name = Some(ctx.name.clone()); - } + state.main_page.content_panel.vta.context_name = Some(ctx.name.clone()); + } else if let Some(ctx) = resp.contexts.first() { + // Fallback to first context + state.main_page.content_panel.vta.context_name = Some(ctx.name.clone()); } } diff --git a/openvtc/src/state_handler/settings_actions.rs b/openvtc/src/state_handler/settings_actions.rs index 844e2cd..c749d91 100644 --- a/openvtc/src/state_handler/settings_actions.rs +++ b/openvtc/src/state_handler/settings_actions.rs @@ -232,10 +232,10 @@ fn handle_field_update(state: &mut State, value: String) { fn handle_form_field_update(state: &mut State, field: usize, value: String) { match &mut state.main_page.content_panel.settings.mode { SettingsMode::ExportConfig { path_input, .. } - | SettingsMode::ImportConfig { path_input, .. } => { - if field == 0 { - *path_input = value; - } + | SettingsMode::ImportConfig { path_input, .. } + if field == 0 => + { + *path_input = value; } _ => {} } diff --git a/openvtc/src/state_handler/setup_wizard.rs b/openvtc/src/state_handler/setup_wizard.rs index 13533c0..852d03c 100644 --- a/openvtc/src/state_handler/setup_wizard.rs +++ b/openvtc/src/state_handler/setup_wizard.rs @@ -87,10 +87,10 @@ impl StateHandler { Action::VtaStartProvision(context_id) => { setup_vta_actions::handle_vta_start_provision(state, &self.state_tx, context_id).await?; }, - Action::VtaCreateKeys => { - if setup_vta_actions::handle_vta_create_keys(state, &self.state_tx).await? { - continue; - } + Action::VtaCreateKeys + if setup_vta_actions::handle_vta_create_keys(state, &self.state_tx).await? => + { + continue; }, Action::ExportDIDKeys(export_inputs) => { setup_did_actions::handle_export_did_keys(state, &self.state_tx, export_inputs).await; @@ -123,6 +123,9 @@ impl StateHandler { setup_token_actions::handle_set_token_name(state, &self.state_tx, token, &name); }, Action::WebvhServerCreateDid(server_id, custom_path) => { + // Cannot move owned bound vars into a match guard, so the + // collapsible_match form clippy suggests doesn't compile. + #[allow(clippy::collapsible_match)] if setup_did_actions::handle_webvh_server_create_did(state, &self.state_tx, tdk, server_id, custom_path).await? { continue; } @@ -146,6 +149,7 @@ impl StateHandler { } }, Action::CreateWebVHDID(webvh_address) => { + #[allow(clippy::collapsible_match)] if setup_did_actions::handle_create_webvh_did(state, &self.profile, webvh_address).await? { continue; } diff --git a/openvtc/src/ui/pages/main/mod.rs b/openvtc/src/ui/pages/main/mod.rs index bdbdfca..52745e5 100644 --- a/openvtc/src/ui/pages/main/mod.rs +++ b/openvtc/src/ui/pages/main/mod.rs @@ -122,19 +122,15 @@ impl Component for MainPage { KeyCode::F(10) => { let _ = self.action_tx.send(Action::Exit); } - KeyCode::Up => { - if self.props.main_page.menu_panel.selected { - let _ = self.action_tx.send(Action::MainMenuSelected( - self.props.main_page.menu_panel.selected_menu.prev(), - )); - } + KeyCode::Up if self.props.main_page.menu_panel.selected => { + let _ = self.action_tx.send(Action::MainMenuSelected( + self.props.main_page.menu_panel.selected_menu.prev(), + )); } - KeyCode::Down => { - if self.props.main_page.menu_panel.selected { - let _ = self.action_tx.send(Action::MainMenuSelected( - self.props.main_page.menu_panel.selected_menu.next(), - )); - } + KeyCode::Down if self.props.main_page.menu_panel.selected => { + let _ = self.action_tx.send(Action::MainMenuSelected( + self.props.main_page.menu_panel.selected_menu.next(), + )); } KeyCode::Tab | KeyCode::Left | KeyCode::Right => { let next_panel = match self.props.main_page.menu_panel.selected { diff --git a/openvtc/src/ui/pages/setup_flow/mod.rs b/openvtc/src/ui/pages/setup_flow/mod.rs index a3def94..d119bd9 100644 --- a/openvtc/src/ui/pages/setup_flow/mod.rs +++ b/openvtc/src/ui/pages/setup_flow/mod.rs @@ -153,7 +153,7 @@ impl Component for SetupFlow { did_keys_export_inputs: DIDKeysExportInputs::default(), did_keys_export_show: DIDKeysExportShow::default(), did_git_sign_ask: DidGitSignAsk::default(), - did_git_sign_setup: DidGitSignSetup::default(), + did_git_sign_setup: DidGitSignSetup, #[cfg(feature = "openpgp-card")] token_start: TokenStart::default(), From 54c9756bd9fa05d7ff1b91f3f80181ddbbc53de9 Mon Sep 17 00:00:00 2001 From: Glenn Gore Date: Sun, 24 May 2026 20:49:28 +0800 Subject: [PATCH 7/7] style: cargo fmt Signed-off-by: Glenn Gore --- openvtc/src/state_handler/message_dispatch.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/openvtc/src/state_handler/message_dispatch.rs b/openvtc/src/state_handler/message_dispatch.rs index 1cc9bd5..9f82cf3 100644 --- a/openvtc/src/state_handler/message_dispatch.rs +++ b/openvtc/src/state_handler/message_dispatch.rs @@ -92,7 +92,6 @@ fn unix_now() -> u64 { .unwrap_or(0) } - /// Validate the message timestamps. Returns `Err(reason)` if the message /// should be dropped as too old, expired, or implausibly future-dated. fn check_message_age(message: &Message) -> Result<(), &'static str> {