Files
synapse/tests/events/test_validator.py
T
Gaëtan d8b4ffdf2d Fix validation of frozen message event with mentions. (#19634)
Fixes: #19689

# What

This PR fixes a bug I found when I run synapse (from dockerhub) and
register a `check_event_allowed` callback and my client makes use of the
mentions field in messages (`cinny:latest`). The bug doesn't appear when
the `check_event_allowed` callback is not loaded.

After some digging I noticed that the current validation of the mentions
doesn't work when an event has been frozen with `event.freeze()`. For
the messages this seems to happen when a the `check_event_allowed` is
registered (but not otherwise), see [where the event is frozen for
check_event_allowed
callback](https://github.com/element-hq/synapse/blob/b0fc0b7a612a42e6f15b87dee2a1db4c383645fb/synapse/module_api/callbacks/third_party_event_rules_callbacks.py#L289)
and [where the validation function is
called](https://github.com/element-hq/synapse/blob/b0fc0b7a612a42e6f15b87dee2a1db4c383645fb/synapse/handlers/message.py#L1404).

To have a minimal reproduction example, the following scripts fails on
`develop` but succeeds in this branch:

``` python
from synapse.api.room_versions import RoomVersions
from synapse.events import EventBase, make_event_from_dict
from synapse.events.validator import EventValidator

from tests.utils import default_config


def make_message_event(content: dict) -> EventBase:
    return make_event_from_dict(
        {
            "room_id": "!room:test",
            "type": "m.room.message",
            "sender": "@alice:test",
            "content": content,
            "auth_events": [],
            "prev_events": [],
            "hashes": {"sha256": "aGVsbG8="},
            "signatures": {},
            "depth": 1,
            "origin_server_ts": 1000,
        },
        room_version=RoomVersions.V9,
    )


event = make_message_event(
    {
        "msgtype": "m.text",
        "body": "@moderator:example.com hello",
        "m.mentions": {"user_ids": ["@moderator:jailbreak-challenge.aqtiveguard.com"]},
    }
)

EventValidator().validate_new(event, default_config)  # Ok
event.freeze()
EventValidator().validate_new(event, default_config)  # throws
# pydantic_core._pydantic_core.ValidationError: 1 validation error for Mentions
#   Input should be a valid dictionary or instance of Mentions [type=model_type, input_value=immutabledict({'user_ids'...nge.aqtiveguard.com',)}), input_type=immutabledict]
#     For further information visit https://errors.pydantic.dev/2.12/v/model_type
```

# How

I made the validation logic also validate the transformation performed
by the freezing process, namely:
- `immutabledict` validates as `dict`. (was already implemented for
POWER_LEVELS)
- `tuple` validates as array (added this to the validator in this PR).


---------

Co-authored-by: Eric Eastwood <madlittlemods@gmail.com>
Co-authored-by: Olivier 'reivilibre <oliverw@matrix.org>
2026-05-18 10:27:10 +01:00

51 lines
1.9 KiB
Python

#
# This file is licensed under the Affero General Public License (AGPL) version 3.
#
# Copyright (C) 2026 Element Creations Ltd
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# See the GNU Affero General Public License for more details:
# <https://www.gnu.org/licenses/agpl-3.0.html>.
#
from synapse.api.room_versions import RoomVersions
from synapse.events import make_event_from_dict
from synapse.events.validator import EventValidator
from tests.unittest import HomeserverTestCase
class EventValidatorTestCase(HomeserverTestCase):
def test_validate_new_with_mentions_succeeds_even_when_frozen(self) -> None:
"""
Test that `EventValidator.validate_new` accepts an event with valid `m.mentions`
content even when the event is frozen.
"""
event = make_event_from_dict(
{
"room_id": "!room:test",
"type": "m.room.message",
"sender": "@alice:example.com",
"content": {
"msgtype": "m.text",
"body": "@alice:example.com hello",
"m.mentions": {"user_ids": ["@alice:example.com"]},
},
"auth_events": [],
"prev_events": [],
"hashes": {"sha256": "aGVsbG8="},
"signatures": {},
"depth": 1,
"origin_server_ts": 1000,
},
room_version=RoomVersions.V9,
)
# Sanity check that the event is valid before freezing
EventValidator().validate_new(event, self.hs.config)
event.freeze()
# Event should still be valid after freezing
EventValidator().validate_new(event, self.hs.config)