cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 1 | use std::time::Duration; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 2 | |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 3 | use futures_util::StreamExt; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 4 | use matrix_sdk::{ |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 5 | deserialized_responses::TimelineEventKind, event_cache::RedecryptorReport, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 6 | test_utils::mocks::MatrixMockServer, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 7 | }; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 8 | use matrix_sdk_test::{JoinedRoomBuilder, event_factory::EventFactory}; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 9 | use ruma::{RoomVersionId, device_id, event_id, room_id, user_id}; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 10 | use serde_json::json; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 11 | |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 12 | #[tokio::test] |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 13 | async fn late_key_exposes_a_mention_without_a_ui_timeline() { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 14 | let server = MatrixMockServer::new().await; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 15 | server.mock_crypto_endpoints_preset().await; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 16 | let alice_id = user_id!("@alice:localhost"); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 17 | let bob_id = user_id!("@bob:localhost"); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 18 | let room_id = room_id!("!mentions-encrypted:localhost"); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 19 | let event_id = event_id!("$late-mention"); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 20 | let alice = server |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 21 | .client_builder_for_crypto_end_to_end(alice_id, device_id!("ALICEDEVICE")) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 22 | .build() |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 23 | .await; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 24 | let bob = server |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 25 | .client_builder_for_crypto_end_to_end(bob_id, device_id!("BOBDEVICE")) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 26 | .build() |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 27 | .await; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 28 | bob.event_cache().subscribe().unwrap(); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 29 | server.exchange_e2ee_identities(&alice, &bob).await; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 30 | |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 31 | let factory = EventFactory::new().room(room_id).sender(alice_id); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 32 | let joined = JoinedRoomBuilder::new(room_id) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 33 | .add_state_event(factory.create(alice_id, RoomVersionId::V1)) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 34 | .add_state_event(factory.room_encryption()) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 35 | .add_state_event(factory.member(alice_id)) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 36 | .add_state_event(factory.member(bob_id)); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 37 | server.sync_room(&alice, joined.clone()).await; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 38 | server.sync_room(&bob, joined).await; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 39 | |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 40 | let room = alice.get_room(room_id).unwrap(); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 41 | let (encrypted_event, send) = server.mock_room_send().ok_with_capture(event_id, alice_id); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 42 | let _send = send.mock_once().mount_as_scoped().await; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 43 | let (_keys, room_key) = server.mock_capture_put_to_device(alice_id).await; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 44 | server |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 45 | .mock_get_members() |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 46 | .ok(vec![ |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 47 | factory.member(alice_id).into_raw(), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 48 | factory.member(bob_id).into_raw(), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 49 | ]) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 50 | .mock_once() |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 51 | .mount() |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 52 | .await; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 53 | room.send_raw( |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 54 | "m.room.message", |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 55 | json!({ |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 56 | "msgtype": "m.text", |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 57 | "body": "a private mention", |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 58 | "m.mentions": { "user_ids": [bob_id] } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 59 | }), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 60 | ) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 61 | .await |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 62 | .unwrap(); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 63 | let encrypted_event = encrypted_event.await.unwrap(); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 64 | let room_key = room_key.await; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 65 | |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 66 | let mut sync_updates = bob.subscribe_to_all_room_updates(); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 67 | let mut cache_updates = bob.event_cache().subscribe_to_room_generic_updates(); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 68 | let mut reports = bob.event_cache().subscribe_to_decryption_reports(); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 69 | server |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 70 | .sync_room( |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 71 | &bob, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 72 | JoinedRoomBuilder::new(room_id).add_timeline_event(encrypted_event), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 73 | ) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 74 | .await; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 75 | let update = sync_updates.recv().await.unwrap(); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 76 | let event = &update.joined[room_id].timeline.events[0]; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 77 | assert!(matches!( |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 78 | event.kind, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 79 | TimelineEventKind::UnableToDecrypt { .. } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 80 | )); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 81 | tokio::time::timeout(Duration::from_secs(5), async { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 82 | while cache_updates.recv().await.unwrap().room_id != room_id {} |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 83 | }) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 84 | .await |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 85 | .unwrap(); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 86 | |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 87 | server |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 88 | .mock_sync() |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 89 | .ok_and_run(&bob, |sync| { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 90 | sync.add_to_device_event(room_key.deserialize_as().unwrap()); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 91 | }) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 92 | .await; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 93 | tokio::time::timeout(Duration::from_secs(5), async { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 94 | while let Some(report) = reports.next().await { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 95 | if let Ok(RedecryptorReport::ResolvedUtds { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 96 | room_id: resolved, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 97 | events, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 98 | }) = report |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 99 | { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 100 | if resolved == room_id && events.contains(event_id) { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 101 | return; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 102 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 103 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 104 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 105 | panic!("decryption reports ended before the mention was decrypted"); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 106 | }) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 107 | .await |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 108 | .unwrap(); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 109 | |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 110 | let room = bob.get_room(room_id).unwrap(); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 111 | let (cache, _cache_handles) = room.event_cache().await.unwrap(); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 112 | let decrypted = cache.find_event(event_id).await.unwrap().unwrap(); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 113 | assert!(matches!(decrypted.kind, TimelineEventKind::Decrypted(_))); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 114 | let content = decrypted |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 115 | .raw() |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 116 | .get_field::<serde_json::Value>("content") |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 117 | .unwrap() |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 118 | .unwrap(); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 119 | assert_eq!(content["body"], "a private mention"); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 120 | assert_eq!(content["m.mentions"]["user_ids"], json!([bob_id])); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 121 | let push_actions = room |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 122 | .event_push_actions(decrypted.raw()) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 123 | .await |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 124 | .unwrap() |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 125 | .unwrap(); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 126 | assert!(push_actions.iter().any(|action| action.should_notify())); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 127 | assert_eq!(cache.events().await.unwrap().len(), 1); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 128 | } |