From 1af22a70b765ac2b60e3cc1baebe419c44fadb90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Tue, 16 Dec 2025 13:23:40 +0100 Subject: [PATCH] fix(sdk_common): `TimelineEvent::from_bundled_latest_event` can remove `session_id` What's more, this is saved into the event cache and sometimes it overrides another instance of the same event that actually contains the right info. This results in unresolvables UTDs. This change tries to fetch the session id from the existing event content. It's fixed these kind of UTDs when tested in a real client. --- crates/matrix-sdk-common/CHANGELOG.md | 4 ++ .../src/deserialized_responses.rs | 64 ++++++++++++++++--- 2 files changed, 60 insertions(+), 8 deletions(-) diff --git a/crates/matrix-sdk-common/CHANGELOG.md b/crates/matrix-sdk-common/CHANGELOG.md index 33d598418..1a9cb7d7d 100644 --- a/crates/matrix-sdk-common/CHANGELOG.md +++ b/crates/matrix-sdk-common/CHANGELOG.md @@ -6,6 +6,10 @@ All notable changes to this project will be documented in this file. ## [Unreleased] - ReleaseDate +### Bug Fixes + +- Fix `TimelineEvent::from_bundled_latest_event` sometimes removing the `session_id` of UTDs. This broken event could later be saved to the event cache and become an unresolvable UTD. ([#5970](https://github.com/matrix-org/matrix-rust-sdk/pull/5970)). + ## [0.16.0] - 2025-12-04 ### Features diff --git a/crates/matrix-sdk-common/src/deserialized_responses.rs b/crates/matrix-sdk-common/src/deserialized_responses.rs index a59d31aaa..a0211abc7 100644 --- a/crates/matrix-sdk-common/src/deserialized_responses.rs +++ b/crates/matrix-sdk-common/src/deserialized_responses.rs @@ -18,7 +18,7 @@ use ruma::{ DeviceKeyAlgorithm, MilliSecondsSinceUnixEpoch, OwnedDeviceId, OwnedEventId, OwnedUserId, events::{ AnySyncMessageLikeEvent, AnySyncTimelineEvent, AnyTimelineEvent, AnyToDeviceEvent, - MessageLikeEventType, + MessageLikeEventType, room::encrypted::EncryptedEventScheme, }, push::Action, serde::{ @@ -713,14 +713,21 @@ impl TimelineEvent { Ok(Some(MessageLikeEventType::RoomEncrypted)) => { // The bundled latest thread event is encrypted, but we didn't have any - // information about it in the unsigned map. Provide some dummy - // UTD info, since we can't really do much better. + // information about it in the unsigned map. Try to fetch the information from + // the content instead. + let session_id = if let Some(content) = + latest_event.get_field::("content").ok().flatten() + { + match content { + EncryptedEventScheme::MegolmV1AesSha2(content) => Some(content.session_id), + _ => None, + } + } else { + None + }; Some(Box::new(TimelineEvent::from_utd_with_max_timestamp( latest_event.cast(), - UnableToDecryptInfo { - session_id: None, - reason: UnableToDecryptReason::Unknown, - }, + UnableToDecryptInfo { session_id, reason: UnableToDecryptReason::Unknown }, max_timestamp, ))) } @@ -1457,7 +1464,9 @@ mod tests { use insta::{assert_json_snapshot, with_settings}; use ruma::{ DeviceKeyAlgorithm, MilliSecondsSinceUnixEpoch, UInt, device_id, event_id, - events::room::message::RoomMessageEventContent, serde::Raw, user_id, + events::{AnySyncTimelineEvent, room::message::RoomMessageEventContent}, + serde::Raw, + user_id, }; use serde::Deserialize; use serde_json::json; @@ -2096,4 +2105,43 @@ mod tests { } }); } + + #[test] + fn test_from_bundled_latest_event_keeps_session_id() { + let session_id = "hgLyeSqXfb8vc5AjQLsg6TSHVu0HJ7HZ4B6jgMvxkrs"; + let serialized = json!({ + "content": { + "algorithm": "m.megolm.v1.aes-sha2", + "ciphertext": "AwgAEoABzL1JYhqhjW9jXrlT3M6H8mJ4qffYtOQOnPuAPNxsuG20oiD/Fnpv6jnQGhU6YbV9pNM+1mRnTvxW3CbWOPjLKqCWTJTc7Q0vDEVtYePg38ncXNcwMmfhgnNAoW9S7vNs8C003x3yUl6NeZ8bH+ci870BZL+kWM/lMl10tn6U7snNmSjnE3ckvRdO+11/R4//5VzFQpZdf4j036lNSls/WIiI67Fk9iFpinz9xdRVWJFVdrAiPFwb8L5xRZ8aX+e2JDMlc1eW8gk", + "device_id": "SKCGPNUWAU", + "sender_key": "Gim/c7uQdSXyrrUbmUOrBT6sMC0gO7QSLmOK6B7NOm0", + "session_id": session_id, + }, + "event_id": "$xxxxx:example.org", + "origin_server_ts": 2189, + "room_id": "!someroom:example.com", + "sender": "@carl:example.com", + "type": "m.room.encrypted" + }); + let json = serialized.to_string(); + let value = Raw::::from_json_string(json).unwrap(); + + let kind = TimelineEventKind::UnableToDecrypt { + event: value.clone(), + utd_info: UnableToDecryptInfo { + session_id: None, + reason: UnableToDecryptReason::Unknown, + }, + }; + let result = TimelineEvent::from_bundled_latest_event( + &kind, + Some(value.cast_unchecked()), + MilliSecondsSinceUnixEpoch::now(), + ) + .expect("Could not get bundled latest event"); + + assert_let!(TimelineEventKind::UnableToDecrypt { utd_info, .. } = result.kind); + assert!(utd_info.session_id.is_some()); + assert_eq!(utd_info.session_id.unwrap(), session_id); + } }