Drop federation read receipts from non-compliant user IDs

This commit is contained in:
Paul Chobert committed 2026-10-09 15:32:42 +02:00
1 parent 1110cf2d73
commit 9e2da8b0a6
2 files changed
+80 -1

No files matched your search

+9
View File
@@ -32,6 +32,7 @@ from synapse.types import (
StreamKeyType, StreamKeyType,
UserID, UserID,
get_domain_from_id, get_domain_from_id,
is_compliant_user_id,
) )
if TYPE_CHECKING: if TYPE_CHECKING:
@@ -116,6 +117,14 @@ class ReceiptsHandler:
) )
continue continue
if not is_compliant_user_id(user_id):
logger.info(
"Received receipt for non-compliant user ID %r from server %s, ignoring",
user_id,
origin,
)
continue
# Check if these receipts apply to a thread. # Check if these receipts apply to a thread.
data = user_values.get("data", {}) data = user_values.get("data", {})
thread_id = data.get("thread_id") thread_id = data.get("thread_id")
+71 -1
View File
@@ -23,7 +23,9 @@ from copy import deepcopy
from twisted.internet.testing import MemoryReactor from twisted.internet.testing import MemoryReactor
from synapse.api.constants import EduTypes, ReceiptTypes from synapse.api.constants import EduTypes, Membership, ReceiptTypes
from synapse.rest import admin
from synapse.rest.client import login, room
from synapse.server import HomeServer from synapse.server import HomeServer
from synapse.types import JsonDict from synapse.types import JsonDict
from synapse.util.clock import Clock from synapse.util.clock import Clock
@@ -340,3 +342,71 @@ class ReceiptsTestCase(unittest.HomeserverTestCase):
events, "@me:server.org" events, "@me:server.org"
) )
self.assertEqual(filtered_events, expected_output) self.assertEqual(filtered_events, expected_output)
class FederationReceiptsTestCase(unittest.FederatingHomeserverTestCase):
servlets = [
admin.register_servlets,
login.register_servlets,
room.register_servlets,
]
def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
super().prepare(reactor, clock, hs)
hs.get_receipts_handler()
def test_non_compliant_user_id_receipt_dropped(self) -> None:
"""A remote server sends read receipts for a compliant and a
non-compliant (grandfathered historical) user ID, both joined to a room
we share. Only the compliant one is stored.
"""
local_user_id = self.register_user("alice", "pass")
local_user_token = self.login("alice", "pass")
room_id = self.helper.create_room_as(local_user_id, tok=local_user_token)
event_id = self.helper.send(room_id, tok=local_user_token)["event_id"]
compliant_user_id = f"@john:{self.OTHER_SERVER_NAME}"
non_compliant_user_id = f"@héllo:{self.OTHER_SERVER_NAME}"
for user_id in (compliant_user_id, non_compliant_user_id):
self.inject_room_member(room_id, user_id, Membership.JOIN)
channel = self.make_signed_federation_request(
"PUT",
"/_matrix/federation/v1/send/txn",
{
"edus": [
{
"edu_type": EduTypes.RECEIPT,
"content": {
room_id: {
ReceiptTypes.READ: {
user_id: {
"event_ids": [event_id],
"data": {"ts": 1436451550453},
}
for user_id in (
compliant_user_id,
non_compliant_user_id,
)
}
}
},
}
]
},
)
self.assertEqual(channel.code, 200, channel.result)
store = self.hs.get_datastores().main
self.assertEqual(
self.get_success(
store.get_receipts_for_user(compliant_user_id, [ReceiptTypes.READ])
),
{room_id: event_id},
)
self.assertEqual(
self.get_success(
store.get_receipts_for_user(non_compliant_user_id, [ReceiptTypes.READ])
),
{},
)