From d42781ba98e1bd7ae9bb7dd41aa0a5bb356fa892 Mon Sep 17 00:00:00 2001 From: Kegan Dougal <7190048+kegsay@users.noreply.github.com> Date: Thu, 29 Oct 2026 14:20:32 +0000 Subject: [PATCH] Add check_room_id to more store functions which get events Use it when retrieving missing events in state DAG mode. --- synapse/handlers/federation.py | 5 +- .../storage/databases/main/events_worker.py | 12 +++++ tests/federation/test_federation_server.py | 46 +++++++++++++++++++ 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/synapse/handlers/federation.py b/synapse/handlers/federation.py index fab47968fa..45ea0bc25b 100644 --- a/synapse/handlers/federation.py +++ b/synapse/handlers/federation.py @@ -1983,7 +1983,7 @@ class FederationHandler: limit = min(limit, StateDag.MAX_MISSING_EVENTS) earliest_event_set = set(earliest_events) - seed_events = await self.store.get_events(latest_events) + seed_events = await self.store.get_events(latest_events, check_room_id=room_id) seed_event_ids: list[str] = [] first_hop_event_ids: set[str] = set() @@ -2007,7 +2007,8 @@ class FederationHandler: first_hop_events: list[EventBase] = [] if first_hop_event_ids: first_hop_events = await self.store.get_events_as_list( - sorted(first_hop_event_ids) + sorted(first_hop_event_ids), + check_room_id=room_id, ) # the sort exists to make [:limit] truncation deterministic first_hop_events.sort(key=lambda ev: ev.event_id) diff --git a/synapse/storage/databases/main/events_worker.py b/synapse/storage/databases/main/events_worker.py index a3ce4a883b..8cf8fb93c7 100644 --- a/synapse/storage/databases/main/events_worker.py +++ b/synapse/storage/databases/main/events_worker.py @@ -611,6 +611,7 @@ class EventsWorkerStore(SQLBaseStore): redact_behaviour: EventRedactBehaviour = EventRedactBehaviour.redact, get_prev_content: bool = False, allow_rejected: bool = False, + check_room_id: str | None = None, ) -> dict[str, EventBase]: """Get events from the database @@ -633,6 +634,9 @@ class EventsWorkerStore(SQLBaseStore): allow_rejected: If True, return rejected events. Otherwise, omits rejected events from the response. + check_room_id: if not None, check the room ID of the events in `event_ids`. + If there is a mismatch, drop that event. + Returns: A mapping from event_id to event. """ @@ -646,6 +650,7 @@ class EventsWorkerStore(SQLBaseStore): redact_behaviour=redact_behaviour, get_prev_content=get_prev_content, allow_rejected=allow_rejected, + check_room_id=check_room_id, ) return {e.event_id: e for e in events} @@ -659,6 +664,7 @@ class EventsWorkerStore(SQLBaseStore): redact_behaviour: EventRedactBehaviour = EventRedactBehaviour.redact, get_prev_content: bool = False, allow_rejected: bool = False, + check_room_id: str | None = None, ) -> list[EventBase]: """Get events from the database and return in a list in the same order as given by `event_ids` arg. @@ -681,6 +687,9 @@ class EventsWorkerStore(SQLBaseStore): allow_rejected: If True, return rejected events. Otherwise, omits rejected events from the response. + check_room_id: if not None, check the room ID of the events in `event_ids`. + If there is a mismatch, drop that event. + Returns: List of events fetched from the database. The events are in the same order as `event_ids` arg. @@ -707,6 +716,9 @@ class EventsWorkerStore(SQLBaseStore): if not entry: continue + if check_room_id is not None and entry.event.room_id != check_room_id: + continue + if not allow_rejected: assert not entry.event.rejected_reason, ( "rejected event returned from _get_events_from_cache_or_db despite " diff --git a/tests/federation/test_federation_server.py b/tests/federation/test_federation_server.py index bba12f5860..09940fc9ef 100644 --- a/tests/federation/test_federation_server.py +++ b/tests/federation/test_federation_server.py @@ -584,6 +584,52 @@ class GetMissingEventsStateDagTests(unittest.FederatingHomeserverTestCase): ) self.assertEqual(events, []) + def test_ignores_non_state_seed_from_other_state_dag_room(self) -> None: + """ + A non-state seed from another state DAG room, which the remote server is not + joined to, must not have its `prev_state_events` returned. + """ + other_room_id = self.helper.create_room_as( + room_creator=self.local_user_id, + tok=self.local_user_token, + room_version=RoomVersions.MSC4242v12.identifier, + ) + other_message_id = self.helper.send( + other_room_id, body="secret", tok=self.local_user_token + )["event_id"] + + # This uses self.room_id as the room ID + events = self._get_missing_events( + earliest_events=[], + latest_events=[other_message_id], + walk_state_dag=True, + limit=20, + ) + self.assertEqual(events, []) + + def test_ignores_non_state_seed_from_non_state_dag_room(self) -> None: + """ + A non-state seed from a room which has no state DAG is treated as unknown, + rather than failing the request. + """ + other_room_id = self.helper.create_room_as( + room_creator=self.local_user_id, + tok=self.local_user_token, + room_version=RoomVersions.V12.identifier, + ) + other_message_id = self.helper.send( + other_room_id, body="hello", tok=self.local_user_token + )["event_id"] + + # This uses self.room_id as the room ID + events = self._get_missing_events( + earliest_events=[], + latest_events=[other_message_id], + walk_state_dag=True, + limit=20, + ) + self.assertEqual(events, []) + class MessageAcceptTests(unittest.FederatingHomeserverTestCase): """