mirror of
https://github.com/matrix-org/matrix-rust-sdk.git
synced 2026-07-30 17:36:27 -04:00
fix(sdk): Use TimelineEvent::timestamp when building an IndexableEvent.
This patch prevents a bug when a malformed `origin_server_ts`
injected in an event can crash Tantivy if the value is too large, [it
overflows][1]:
```
tantivy-common-0.11.0/src/datetime.rs:65:30:
attempt to multiply with overflow
```
The fix consists of using the result of `TimelineEvent::timestamp` which
already deals with malformed `origin_server_ts`.
[1]: 31ca1a8ba2/common/src/datetime.rs (L62-L67)
This commit is contained in:
@@ -49,7 +49,11 @@ pub struct IndexableEvent {
|
||||
/// The sender of the event.
|
||||
pub(crate) sender: OwnedUserId,
|
||||
/// The origin server timestamp of the event.
|
||||
pub(crate) timestamp: MilliSecondsSinceUnixEpoch,
|
||||
///
|
||||
/// Please use the `matrix_sdk_common::TimelineEvent::timestamp` as much as
|
||||
/// possible as it protects against malformed `origin_server_ts`. At worst,
|
||||
/// use the `matrix_sdk_common::serde_helpers::extract_timestamp` function.
|
||||
pub(crate) timestamp: Option<MilliSecondsSinceUnixEpoch>,
|
||||
/// The text to index for this event.
|
||||
pub(crate) body: String,
|
||||
}
|
||||
@@ -60,7 +64,7 @@ impl IndexableEvent {
|
||||
event_id: OwnedEventId,
|
||||
original_event_id: OwnedEventId,
|
||||
sender: OwnedUserId,
|
||||
timestamp: MilliSecondsSinceUnixEpoch,
|
||||
timestamp: Option<MilliSecondsSinceUnixEpoch>,
|
||||
body: String,
|
||||
) -> Self {
|
||||
Self { event_id, original_event_id, sender, timestamp, body }
|
||||
@@ -427,7 +431,7 @@ mod tests {
|
||||
event.event_id.clone(),
|
||||
original_event_id,
|
||||
event.sender.clone(),
|
||||
event.origin_server_ts,
|
||||
Some(event.origin_server_ts),
|
||||
content.body.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -96,11 +96,18 @@ impl MatrixSearchIndexSchema for RoomMessageSchema {
|
||||
|
||||
/// Given an [`IndexableEvent`] return a [`TantivyDocument`].
|
||||
fn make_doc(&self, event: IndexableEvent) -> Result<TantivyDocument, IndexError> {
|
||||
let timestamp = DateTime::from_timestamp_millis(
|
||||
event
|
||||
.timestamp
|
||||
.map(|timestamp| timestamp.get().into())
|
||||
// If the timestamp is missing, use 0 as the “no value”.
|
||||
.unwrap_or(0),
|
||||
);
|
||||
|
||||
let document = doc!(
|
||||
self.event_id_field => event.event_id.to_string(),
|
||||
self.body_field => event.body,
|
||||
self.date_field =>
|
||||
DateTime::from_timestamp_millis(event.timestamp.get().into()),
|
||||
self.date_field => timestamp,
|
||||
self.sender_field => event.sender.to_string(),
|
||||
self.original_event_id_field => event.original_event_id.to_string(),
|
||||
);
|
||||
|
||||
@@ -27,7 +27,7 @@ use matrix_sdk_search::{
|
||||
index::{IndexableEvent, RoomIndex, RoomIndexOperation, builder::RoomIndexBuilder},
|
||||
};
|
||||
use ruma::{
|
||||
EventId, OwnedEventId, OwnedRoomId, RoomId,
|
||||
EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedRoomId, RoomId,
|
||||
events::{
|
||||
AnySyncMessageLikeEvent, AnySyncTimelineEvent,
|
||||
poll::{
|
||||
@@ -284,7 +284,10 @@ fn room_message_body(msgtype: &MessageType) -> Option<String> {
|
||||
|
||||
/// Build an [`IndexableEvent`] from a room message, or `None` if its type
|
||||
/// carries no searchable text.
|
||||
fn indexable_from_room_message(event: &OriginalSyncRoomMessageEvent) -> Option<IndexableEvent> {
|
||||
fn indexable_from_room_message(
|
||||
event: &OriginalSyncRoomMessageEvent,
|
||||
timestamp: Option<MilliSecondsSinceUnixEpoch>,
|
||||
) -> Option<IndexableEvent> {
|
||||
let body = room_message_body(&event.content.msgtype)?;
|
||||
let original_event_id = match &event.content.relates_to {
|
||||
Some(Relation::Replacement(replacement)) => replacement.event_id.clone(),
|
||||
@@ -295,7 +298,7 @@ fn indexable_from_room_message(event: &OriginalSyncRoomMessageEvent) -> Option<I
|
||||
event.event_id.clone(),
|
||||
original_event_id,
|
||||
event.sender.clone(),
|
||||
event.origin_server_ts,
|
||||
timestamp,
|
||||
body,
|
||||
))
|
||||
}
|
||||
@@ -305,12 +308,13 @@ fn indexable_from_room_message(event: &OriginalSyncRoomMessageEvent) -> Option<I
|
||||
/// original.
|
||||
async fn handle_possible_edit(
|
||||
event: &OriginalSyncRoomMessageEvent,
|
||||
timestamp: Option<MilliSecondsSinceUnixEpoch>,
|
||||
cache: &RoomEventCache,
|
||||
) -> Option<RoomIndexOperation> {
|
||||
if let Some(Relation::Replacement(replacement_data)) = &event.content.relates_to {
|
||||
if let Some(recent) = get_most_recent_edit(cache, &replacement_data.event_id).await {
|
||||
return Some(
|
||||
indexable_from_room_message(&recent).map_or(
|
||||
indexable_from_room_message(&recent, timestamp).map_or(
|
||||
RoomIndexOperation::Noop,
|
||||
|indexable| {
|
||||
RoomIndexOperation::Edit(replacement_data.event_id.clone(), indexable)
|
||||
@@ -328,15 +332,18 @@ async fn handle_possible_edit(
|
||||
/// depending on the message.
|
||||
async fn handle_room_message(
|
||||
event: SyncRoomMessageEvent,
|
||||
timestamp: Option<MilliSecondsSinceUnixEpoch>,
|
||||
cache: &RoomEventCache,
|
||||
) -> Option<RoomIndexOperation> {
|
||||
if let Some(event) = event.as_original() {
|
||||
return handle_possible_edit(event, cache).await.or(get_most_recent_edit(
|
||||
return handle_possible_edit(event, timestamp, cache).await.or(get_most_recent_edit(
|
||||
cache,
|
||||
&event.event_id,
|
||||
)
|
||||
.await
|
||||
.and_then(|recent| indexable_from_room_message(&recent).map(RoomIndexOperation::Add)));
|
||||
.and_then(|recent| {
|
||||
indexable_from_room_message(&recent, timestamp).map(RoomIndexOperation::Add)
|
||||
}));
|
||||
}
|
||||
None
|
||||
}
|
||||
@@ -345,6 +352,7 @@ async fn handle_room_message(
|
||||
/// re-adding the most recent remaining version if an edit was redacted.
|
||||
async fn handle_room_redaction(
|
||||
event: SyncRoomRedactionEvent,
|
||||
timestamp: Option<MilliSecondsSinceUnixEpoch>,
|
||||
cache: &RoomEventCache,
|
||||
rules: &RedactionRules,
|
||||
) -> Option<RoomIndexOperation> {
|
||||
@@ -357,7 +365,7 @@ async fn handle_room_redaction(
|
||||
redacted_event,
|
||||
))) = redacted_event.raw().deserialize()
|
||||
&& let Some(redacted_event) = redacted_event.as_original()
|
||||
&& let Some(operation) = handle_possible_edit(redacted_event, cache).await
|
||||
&& let Some(operation) = handle_possible_edit(redacted_event, timestamp, cache).await
|
||||
{
|
||||
return Some(operation);
|
||||
}
|
||||
@@ -368,14 +376,17 @@ async fn handle_room_redaction(
|
||||
}
|
||||
|
||||
/// Return a [`RoomIndexOperation::Add`] indexing a sticker's descriptive text.
|
||||
fn handle_sticker(event: SyncStickerEvent) -> Option<RoomIndexOperation> {
|
||||
fn handle_sticker(
|
||||
event: SyncStickerEvent,
|
||||
timestamp: Option<MilliSecondsSinceUnixEpoch>,
|
||||
) -> Option<RoomIndexOperation> {
|
||||
let event = event.as_original()?;
|
||||
|
||||
Some(RoomIndexOperation::Add(IndexableEvent::new(
|
||||
event.event_id.clone(),
|
||||
event.event_id.clone(),
|
||||
event.sender.clone(),
|
||||
event.origin_server_ts,
|
||||
timestamp,
|
||||
event.content.body.clone(),
|
||||
)))
|
||||
}
|
||||
@@ -386,7 +397,10 @@ fn handle_sticker(event: SyncStickerEvent) -> Option<RoomIndexOperation> {
|
||||
/// ponytail: only indexes the initial `New` poll — edits (`Replacement`) and
|
||||
/// poll ends are ignored. Add edit handling if editing a poll needs to update
|
||||
/// search results.
|
||||
fn handle_unstable_poll_start(event: SyncUnstablePollStartEvent) -> Option<RoomIndexOperation> {
|
||||
fn handle_unstable_poll_start(
|
||||
event: SyncUnstablePollStartEvent,
|
||||
timestamp: Option<MilliSecondsSinceUnixEpoch>,
|
||||
) -> Option<RoomIndexOperation> {
|
||||
let event = event.as_original()?;
|
||||
|
||||
let UnstablePollStartEventContent::New(content) = &event.content else {
|
||||
@@ -404,7 +418,7 @@ fn handle_unstable_poll_start(event: SyncUnstablePollStartEvent) -> Option<RoomI
|
||||
event.event_id.clone(),
|
||||
event.event_id.clone(),
|
||||
event.sender.clone(),
|
||||
event.origin_server_ts,
|
||||
timestamp,
|
||||
body,
|
||||
)))
|
||||
}
|
||||
@@ -414,7 +428,10 @@ fn handle_unstable_poll_start(event: SyncUnstablePollStartEvent) -> Option<RoomI
|
||||
///
|
||||
/// ponytail: like [`handle_unstable_poll_start`], edits and poll ends are
|
||||
/// ignored — only the initial poll is indexed.
|
||||
fn handle_poll_start(event: SyncPollStartEvent) -> Option<RoomIndexOperation> {
|
||||
fn handle_poll_start(
|
||||
event: SyncPollStartEvent,
|
||||
timestamp: Option<MilliSecondsSinceUnixEpoch>,
|
||||
) -> Option<RoomIndexOperation> {
|
||||
let event = event.as_original()?;
|
||||
|
||||
// Skip poll edits, matching the unstable poll handling.
|
||||
@@ -435,7 +452,7 @@ fn handle_poll_start(event: SyncPollStartEvent) -> Option<RoomIndexOperation> {
|
||||
event.event_id.clone(),
|
||||
event.event_id.clone(),
|
||||
event.sender.clone(),
|
||||
event.origin_server_ts,
|
||||
timestamp,
|
||||
body,
|
||||
)))
|
||||
}
|
||||
@@ -453,19 +470,21 @@ async fn parse_timeline_event(
|
||||
return None;
|
||||
}
|
||||
|
||||
let timestamp = event.timestamp();
|
||||
|
||||
match event.raw().deserialize() {
|
||||
Ok(event) => match event {
|
||||
AnySyncTimelineEvent::MessageLike(event) => match event {
|
||||
AnySyncMessageLikeEvent::RoomMessage(event) => {
|
||||
handle_room_message(event, cache).await
|
||||
handle_room_message(event, timestamp, cache).await
|
||||
}
|
||||
AnySyncMessageLikeEvent::RoomRedaction(event) => {
|
||||
handle_room_redaction(event, cache, redaction_rules).await
|
||||
handle_room_redaction(event, timestamp, cache, redaction_rules).await
|
||||
}
|
||||
AnySyncMessageLikeEvent::Sticker(event) => handle_sticker(event),
|
||||
AnySyncMessageLikeEvent::PollStart(event) => handle_poll_start(event),
|
||||
AnySyncMessageLikeEvent::Sticker(event) => handle_sticker(event, timestamp),
|
||||
AnySyncMessageLikeEvent::PollStart(event) => handle_poll_start(event, timestamp),
|
||||
AnySyncMessageLikeEvent::UnstablePollStart(event) => {
|
||||
handle_unstable_poll_start(event)
|
||||
handle_unstable_poll_start(event, timestamp)
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user