diff --git a/synapse/handlers/devicemessage.py b/synapse/handlers/devicemessage.py index 6a64c1ccf7..f6196be0a4 100644 --- a/synapse/handlers/devicemessage.py +++ b/synapse/handlers/devicemessage.py @@ -42,7 +42,14 @@ from synapse.logging.opentracing import ( log_kv, set_tag, ) -from synapse.types import JsonDict, Requester, StreamKeyType, UserID, get_domain_from_id +from synapse.types import ( + JsonDict, + Requester, + StreamKeyType, + UserID, + get_domain_from_id, + is_compliant_user_id, +) from synapse.util import split_dict_to_fit_to_size from synapse.util.json import json_encoder from synapse.util.stringutils import random_string_insecure_fast @@ -122,6 +129,14 @@ class DeviceMessageHandler: sender_user_id, ) return + + if not is_compliant_user_id(sender_user_id): + logger.warning( + "Dropping device message from %r with non-compliant sender %r", + origin, + sender_user_id, + ) + return message_type = content["type"] message_id = content["message_id"] for user_id, by_device in content["messages"].items(): diff --git a/tests/rest/client/test_sendtodevice.py b/tests/rest/client/test_sendtodevice.py index e9b607cf23..21aa754cea 100644 --- a/tests/rest/client/test_sendtodevice.py +++ b/tests/rest/client/test_sendtodevice.py @@ -543,6 +543,48 @@ class SendToDeviceTestCase(HomeserverTestCase): ], ) + def test_remote_non_compliant_sender(self) -> None: + """ + Tests that a to-device message from a non-compliant (grandfathered + historical) sender is dropped, while one from a compliant sender on the + same server is delivered. + """ + user2 = self.register_user("u2", "pass") + user2_tok = self.login("u2", "pass", "d2") + + federation_registry = self.hs.get_federation_registry() + + for sender, content in ( + ("@héllo:example.org", {"foo": "bar"}), + ("@user:example.org", {"hiss": "meow"}), + ): + self.get_success( + federation_registry.on_edu( + EduTypes.DIRECT_TO_DEVICE, + "example.org", + { + "sender": sender, + "type": "org.example.test", + "messages": {user2: {"d2": content}}, + "message_id": "1", + }, + ) + ) + + channel = self.make_request("GET", "/sync", access_token=user2_tok) + self.assertEqual(channel.code, 200, channel.result) + messages = channel.json_body.get("to_device", {}).get("events", []) + self.assertEqual( + messages, + [ + { + "content": {"hiss": "meow"}, + "sender": "@user:example.org", + "type": "org.example.test", + } + ], + ) + def test_limited_sync(self) -> None: """If a limited sync for to-devices happens the next /sync should respond immediately."""