Commit Graph
1013 Commits
Author SHA1 Message Date
Johannes Marbach f8f7738f5d Add experimental support for sending federation requests from app services as per MSC4512 (#19977)
This implements the outgoing part of
[MSC4512](https://github.com/matrix-org/matrix-spec-proposals/pull/4512)
and is another stopgap towards
https://github.com/element-hq/voip-internal/issues/641.

This adds the ability for proxying app services
(https://github.com/element-hq/synapse/pull/19972) to trigger federation
requests under their own proxy prefix.

This depends on https://github.com/element-hq/synapse/pull/19972 and
cannot make progress before it lands.
2026-09-08 13:29:03 +01:00
Paul ChobertandQuentin Gliech e35c8f0eff Fix client_secret param validator (#20104)
The validation of `client_secret` params accepts invalid values: any
value that includes a char in `[0-9a-zA-Z.=_-]` is accepted, for example
`"café"` is accepted.

Instead synapse should only accept if all chars are within
`[0-9a-zA-Z.=_-]`, not just one.

This is the current validator:

```python
ClientSecretStr = Annotated[
    str,
    StringConstraints(
        pattern="[0-9a-zA-Z.=_-]",
        min_length=1,
        max_length=255,
        strict=True,
    ),
]
```

Unfortunately, Pydantic only defines the `pattern` argument as:

> ### pattern
> A regex pattern that the string must match.

Which is extremely imprecise.

Here is a little script to verify the behavior:

```python
from typing import Annotated
from pydantic import BaseModel, StringConstraints, ValidationError


def check(pattern: str) -> None:
    ClientSecretStr = Annotated[
        str,
        StringConstraints(pattern=pattern, min_length=1, max_length=255, strict=True),
    ]

    class Body(BaseModel):
        client_secret: ClientSecretStr

    try:
        Body.model_validate({"client_secret": "café"})
        print(f"pattern = {pattern!r}: 'café' ACCEPTED  <-- should have been rejected")
    except ValidationError:
        print(f"pattern = {pattern!r}: 'café' rejected")


check("[0-9a-zA-Z.=_-]")  # before the fix (unanchored)
check("^[0-9a-zA-Z.=_-]+$")  # after the fix
```

which would output


```
pattern = '[0-9a-zA-Z.=_-]': 'café' ACCEPTED  <-- should have been rejected
pattern = '^[0-9a-zA-Z.=_-]+$': 'café' rejected
```

## History

This is a regression of a previously reported and fixed bug:

- matrix-org/synapse#6766 (2020) reported that Synapse did not enforce
the spec's `client_secret` regex at all — with real-world fallout:
FluffyChat had started sending secrets containing `:` because nothing
rejected them. Fixed by introducing `assert_valid_client_secret`
(matrix-org/synapse#6767).
- matrix-org/synapse#13188 (Synapse 1.66.0) ported the account endpoints
to Pydantic and transcribed the regex without anchors/quantifier;
Pydantic v1's `re.match` semantics meant only the *first* character was
validated.
- #19071 (Synapse 1.142.0) migrated to Pydantic v2, whose *search*
semantics weakened it further to "any one character anywhere".


---

### Pull Request Checklist

<!-- Please read
https://element-hq.github.io/synapse/latest/development/contributing_guide.html
before submitting your pull request -->

* [x] Pull request is based on the develop branch
* [x] Pull request includes a [changelog
file](https://element-hq.github.io/synapse/latest/development/contributing_guide.html#changelog).
The entry should:
- Be a short description of your change which makes sense to users.
"Fixed a bug that prevented receiving messages from other servers."
instead of "Moved X method from `EventStore` to `EventWorkerStore`.".
  - Use markdown where necessary, mostly for `code blocks`.
  - End with either a period (.) or an exclamation mark (!).
  - Start with a capital letter.
- Feel free to credit yourself, by adding a sentence "Contributed by
@github_username." or "Contributed by [Your Name]." to the end of the
entry.
* [x] [Code
style](https://element-hq.github.io/synapse/latest/code_style.html) is
correct (run the
[linters](https://element-hq.github.io/synapse/latest/development/contributing_guide.html#run-the-linters))

---------

Co-authored-by: Quentin Gliech <quenting@element.io>
2026-09-07 10:35:33 +02:00
Paul Chobert bd21823818 Fix 500 error when setting a custom profile field for a user with no profile row (#20172)
Follow-up to #20149, which fixed the 500 when *getting* a profile field
for a user with no `profiles` row. The same crash was still reachable
when *setting* one via `PUT /_matrix/client/v3/profile/{userId}/{field}`
(as server admin). This PR splits that case in two:

* **The user exists but has no `profiles` row** (e.g. profile erased
upon deactivation):
* Before: `500 M_UNKNOWN` (`TypeError: cannot unpack non-sequence
NoneType` in the profile size check).
  * After: `200`, the profile row is recreated with the field set.
* **The user does not exist at all**:
  * Before: `500 M_UNKNOWN` (same crash).
* After: `404 M_NOT_FOUND`, without conjuring up an orphan profile row.

Fixing the crash also surfaced a latent SQLite-only bug where a field
set on a freshly created profile row was stored under the wrong key,
making it 404 on `GET` right after a successful `PUT`.



### Pull Request Checklist

<!-- Please read
https://element-hq.github.io/synapse/latest/development/contributing_guide.html
before submitting your pull request -->

* [x] Pull request is based on the develop branch
* [x] Pull request includes a [changelog
file](https://element-hq.github.io/synapse/latest/development/contributing_guide.html#changelog).
The entry should:
- Be a short description of your change which makes sense to users.
"Fixed a bug that prevented receiving messages from other servers."
instead of "Moved X method from `EventStore` to `EventWorkerStore`.".
  - Use markdown where necessary, mostly for `code blocks`.
  - End with either a period (.) or an exclamation mark (!).
  - Start with a capital letter.
- Feel free to credit yourself, by adding a sentence "Contributed by
@github_username." or "Contributed by [Your Name]." to the end of the
entry.
* [x] [Code
style](https://element-hq.github.io/synapse/latest/code_style.html) is
correct (run the
[linters](https://element-hq.github.io/synapse/latest/development/contributing_guide.html#run-the-linters))
2026-09-07 10:34:30 +02:00
Jason Little a0b5a45c91 fix MSC3912-relation-based-redaction for room versions > 10 (#19782) 2026-09-04 08:35:28 +00:00
Paul Chobert 207f6560ce Stabilize the M_APPSERVICE_LOGIN_UNSUPPORTED error code (#20180)
Part of: https://github.com/element-hq/synapse/issues/19415

Return `M_APPSERVICE_LOGIN_UNSUPPORTED` error code instead of the
unstable `IO.ELEMENT.MSC4190.M_APPSERVICE_LOGIN_UNSUPPORTED` identifier.

> Servers MUST still allow application services to use the `/register`
endpoint with a login type of `m.login.application_service` even if they
don't support the Legacy Authentication API. In that case application
services MUST set the `"inhibit_login": true` parameter as they cannot
use it to log in as users. If the `inhibit_login` parameter is not set
to `true`, the server MUST return a 400 HTTP status code with an
`M_APPSERVICE_LOGIN_UNSUPPORTED` error code.
>
> [...]
>
> Application services MUST NOT use the `/login` endpoint if the server
doesn't support the Legacy authentication API. If `/login` is called
with the `m.login.application_service` login type the server MUST return
a 400 HTTP status code with an `M_APPSERVICE_LOGIN_UNSUPPORTED` error
code.
>
> — [Matrix v1.19, Application Service
API](https://spec.matrix.org/v1.19/application-service-api/#registration)

Synapse returns the correct 400 on both endpoints, but with the unstable
identifier.

Before:

```
POST /_matrix/client/v3/login    {"type": "m.login.application_service", ...}  # appservice with MSC4190 device management
POST /_matrix/client/v3/register {"type": "m.login.application_service", ...}  # without "inhibit_login": true
  400 {"errcode": "IO.ELEMENT.MSC4190.M_APPSERVICE_LOGIN_UNSUPPORTED"}
```

After:

```
POST /_matrix/client/v3/login    {"type": "m.login.application_service", ...}  # appservice with MSC4190 device management
POST /_matrix/client/v3/register {"type": "m.login.application_service", ...}  # without "inhibit_login": true
  400 {"errcode": "M_APPSERVICE_LOGIN_UNSUPPORTED"}
```

A Sister PR exists in MAS:
https://github.com/element-hq/matrix-authentication-service/pull/5961 ;
when delegation is enabled, `/login` reaches MAS instead of Synapse, and
MAS currently answers `m.login.application_service` with `M_UNKNOWN`.
2026-09-03 15:07:51 +01:00
Paul Chobert 366a020f79 Fix MSC4178 error codes on /account/3pid/{email,msisdn}/requestToken (#20101)
Part of https://github.com/element-hq/synapse/issues/18118

## What the spec says

Since Matrix v1.13 (introduced by
[MSC4178](https://github.com/matrix-org/matrix-spec-proposals/pull/4178)),
the `400` response of [`POST
/_matrix/client/v3/account/3pid/email/requestToken`](https://spec.matrix.org/v1.19/client-server-api/#post_matrixclientv3account3pidemailrequesttoken)
lists, among the "Error codes that can be returned":

> - `M_THREEPID_MEDIUM_NOT_SUPPORTED`: The homeserver does not support
adding email addresses.
> - `M_INVALID_PARAM`: The email address given was not valid.

and [`POST
/_matrix/client/v3/account/3pid/msisdn/requestToken`](https://spec.matrix.org/v1.19/client-server-api/#post_matrixclientv3account3pidmsisdnrequesttoken)
likewise:

> - `M_THREEPID_MEDIUM_NOT_SUPPORTED`: The homeserver does not support
adding phone numbers.
> - `M_INVALID_PARAM`: The phone number given was not valid.

## What was missing

Synapse implemented the headline case (unsupported medium), but around
it:

- A malformed email address or country code was reported with the
generic `M_BAD_JSON` instead of `M_INVALID_PARAM`. The email validator
deliberately kept `M_BAD_JSON` "to ensure backward compatibility of HTTP
error codes" (matrix-org/synapse#13687, 2022) — that predates Matrix
v1.13, which now lists `M_INVALID_PARAM` for this case.
- On the msisdn variant, the unsupported-medium check ran after the
denied/in-use checks, so a request wrong in two ways reported the other
fault; the email variant checks it first.

## What this PR changes

- Malformed email addresses and country codes on
`/account/3pid/{email,msisdn}/requestToken` are reported with
`M_INVALID_PARAM`. Both flow through the existing errcode translation as
`value_error`: the email validator raises a plain `ValueError`, and the
country-code constraint (`ISO3166_1_Alpha_2`) declares its own error via
pydantic-core's `custom_error_schema`.
- On the msisdn variant the unsupported-medium check now runs before the
denied/in-use checks, as on the email variant.
- The country-code type is renamed from `ISO3116_1_Alpha_2` to
`ISO3166_1_Alpha_2` (typo in the standard's number).

[`/account/password/email/requestToken`](https://spec.matrix.org/v1.19/client-server-api/#post_matrixclientv3accountpasswordemailrequesttoken)
(not covered by the v1.13 change) shares the email request body model,
so a malformed email there is now also reported with `M_INVALID_PARAM`
instead of `M_BAD_JSON`. Its `400` response is described as "the request
was invalid" and only names `M_SERVER_NOT_TRUSTED` explicitly ("can be
returned if…") rather than restricting the server to a fixed list, and
`M_INVALID_PARAM` is the spec's generic code for "A parameter that was
specified has the wrong value" ([other error
codes](https://spec.matrix.org/v1.19/client-server-api/#other-error-codes)).
2026-09-03 12:05:08 +01:00
Paul Chobert 1727ceee7b Ratelimit the room reporting endpoint (MSC4151) (#20036)
Apply the `rc_reports` rate limit to the room reporting endpoint, [`POST
/_matrix/client/v3/rooms/{roomId}/report`](https://spec.matrix.org/v1.19/client-server-api/#post_matrixclientv3roomsroomidreport)
(added in Matrix v1.13).

The spec marks this endpoint as **Rate-limited: Yes** (clients must
expect a `429 M_LIMIT_EXCEEDED`), and homeservers [SHOULD implement rate
limiting](https://spec.matrix.org/v1.19/client-server-api/#rate-limiting)
in general, but Synapse currently applies no limit here. The sibling
user reporting endpoint already uses `rc_reports`, so this reuses the
same limit instead of introducing a new config option.

Changes:

- Move the room report logic from `ReportRoomRestServlet` into a new
`ReportsHandler.report_room`, mirroring the existing `report_user`. The
rate limit is checked before the room existence lookup, so it bounds the
DB work a caller can trigger and cannot be used to tell existing rooms
from non-existing ones.
- The servlet keeps the existing behaviour of returning `200` regardless
of room existence when `msc4277_enabled` is set (the spec allows this
since v1.18).
- Add a regression test covering the `429` response and the per-user
rate limit override.
2026-09-03 12:01:21 +01:00
Paul Chobert d4de2ae1c5 Fix state_after for left rooms including post-leave lazy-loaded memberships (#20169)
## The bug

With the experimental
[MSC4222](https://github.com/matrix-org/matrix-spec-proposals/pull/4222)
implementation enabled (`use_state_after`) and lazy-loading of room
members, an incremental `/sync` could disclose state from **after** the
user's leave in a left room's `state_after`.

1. Alice syncs with `lazy_load_members: true` and
`use_state_after=true`.
2. Bob sends a message in a room they share.
3. Alice leaves the room.
4. Bob updates his per-room displayname
5. Alice does an incremental sync covering steps 2–3. Alice's
`state_after` contains Bob's post-leave membership event from step 4

Alice should not see the new per-room display name of Bob.


## The fix

Copy what has been done for `_compute_state_delta_for_full_sync`: pass
`joined` down and, for rooms the user is no longer joined to, fetch the
memberships as of `end_token` via state groups (`get_state_ids_at`)
instead of current state.
2026-09-03 11:58:56 +01:00
dea059a94d Restrict message events from being redacted by local users if past allowed period. (#20138)
Closes: #20065 

---------

Co-authored-by: defaultdino <adve@adisve.net>
Co-authored-by: Olivier 'reivilibre <oliverw@element.io>
2026-09-02 16:36:35 +01:00
Paul Chobert f94abb6924 Return 403 instead of 400 when profile changes are disabled (#20173)
With `enable_set_displayname: false` (or `enable_set_avatar_url:
false`), refusing a profile change returned the right errcode with the
wrong status:

```
PUT /_matrix/client/v3/profile/@alice:example.com/displayname   (displayname already set)
→ 400 {"errcode": "M_FORBIDDEN", "error": "Changing display name is disabled on this server"}

DELETE /_matrix/client/v3/profile/@alice:example.com/displayname
→ 400 {"errcode": "M_FORBIDDEN", "error": "Changing display name is disabled on this server"}
```

With this fix:

```
PUT /_matrix/client/v3/profile/@alice:example.com/displayname   (displayname already set)
→ 403 {"errcode": "M_FORBIDDEN", "error": "Changing display name is disabled on this server"}

DELETE /_matrix/client/v3/profile/@alice:example.com/displayname
→ 403 {"errcode": "M_FORBIDDEN", "error": "Changing display name is disabled on this server"}
```

The spec defines the [403 response of `PUT
/_matrix/client/v3/profile/{userId}/{keyName}`](https://spec.matrix.org/v1.19/client-server-api/#put_matrixclientv3profileuseridkeyname)
as "The server is unwilling to perform the operation, either due to
insufficient permissions or **because profile modifications are
disabled**", while 400 is reserved for malformed input (`M_BAD_JSON`,
`M_MISSING_PARAM`, …).

Clients seem to rely on `errcode` field more than the HTTP Status Code,
that change seems safe.
2026-09-02 14:29:08 +01:00
Andrew Morgan 652558495f Remove unstable /auth_issuer endpoint (#20163) 2026-09-01 16:45:39 +00:00
Soyoung Kim 30355721f6 Fix public_chat preset and deepmerge when it is needed (#20050) 2026-08-28 21:34:27 +00:00
Andrew Ferrazzutti a92a21600c MSC4140: update error for exceeding delay maximum (#20156)
Use the HTTP code & errcode specified in the latest revision of the MSC

(The changes were added by
https://github.com/matrix-org/matrix-spec-proposals/pull/4140/changes/0c7193aaa07c9a2634dccd7cf3bd83e2733a806e)
2026-08-28 08:53:30 -04:00
Johannes Marbach 57d6da409c Add experimental support for letting application services proxy namespaces in the C-S and S-S API as per MSC4512 (#19972)
This implements the proxying part of
[MSC4512](https://github.com/matrix-org/matrix-spec-proposals/pull/4512)
and is a stopgap towards
https://github.com/element-hq/voip-internal/issues/641. It introduces a
new configuration property `io.element.msc4512.proxy` that allows
application services to claim namespaces in the C-S and S-S API. For
requests underneath a claimed namespace, Synapse first authorizes the
request and then reverse-proxies it to the application services. For
now, the only allowed namespace that can be claimed is
`unstable/io.element.msc4195/rtc/livekit`.

This pull request can be reviewed by commits.

### Pull Request Checklist

<!-- Please read
https://element-hq.github.io/synapse/latest/development/contributing_guide.html
before submitting your pull request -->

* [x] Pull request is based on the develop branch
* [x] Pull request includes a [changelog
file](https://element-hq.github.io/synapse/latest/development/contributing_guide.html#changelog).
The entry should:
- Be a short description of your change which makes sense to users.
"Fixed a bug that prevented receiving messages from other servers."
instead of "Moved X method from `EventStore` to `EventWorkerStore`.".
  - Use markdown where necessary, mostly for `code blocks`.
  - End with either a period (.) or an exclamation mark (!).
  - Start with a capital letter.
- Feel free to credit yourself, by adding a sentence "Contributed by
@github_username." or "Contributed by [Your Name]." to the end of the
entry.
* [x] [Code
style](https://element-hq.github.io/synapse/latest/code_style.html) is
correct (run the
[linters](https://element-hq.github.io/synapse/latest/development/contributing_guide.html#run-the-linters))

---------

Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
2026-08-28 13:45:54 +01:00
Andrew FerrazzuttiandOlivier 'reivilibre' c3bec60936 MSC4140: support getting a single delayed event (#19926)
See
https://github.com/matrix-org/matrix-spec-proposals/blob/toger5/expiring-events-keep-alive/proposals/4140-delayed-events-futures.md#getting-a-single-delayed-event

Co-authored-by: Olivier 'reivilibre' <olivier@librepush.net>
2026-08-28 02:05:59 -04:00
Johannes MarbachandAndrew Morgan c246d81dec Add support for specifying the SFU WebSocket URL together with configured LiveKit transports (#20146)
Co-authored-by: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com>
Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
2026-08-27 11:21:47 +00:00
4bb07152c8 Support for profile updates over Sliding Sync (MSC4262) (#20003)
This PR implements support for profile updates over Sliding Sync:
https://github.com/matrix-org/matrix-spec-proposals/pull/4262. This pr
may be easier to review as a whole than commit by commit.

This builds on the legacy sync profile updates feature
https://github.com/element-hq/synapse/pull/19556, specifically the
profile updates stream it added.

Submitting for early review to get consensus on implementation. There
are some things we would like to add still, from spec, mainly:

* > Homeservers should only consider a profile field update "accepted"
by a client
> once the client returns with a new /sync request with the next /sync
token,
> NOT just after sending down the profile update. The client may never
receive
> response due to network conditions, or a bug in the client
implementation.
* > When a room enters this subset in this connection for the first
time, all requested
> fields from profiles of users in that room MAY be sent down. This
gives the client
> a base set of information for which future field updates can be
applied on top of.
> The homeserver MAY omit some fields and profiles if it believes that
the client has
> already received them, likewise repeat profiles MAY be sent down based
on homeserver
  > implementation.
* > Finally, if the list of fields expands to cover a new field ID,
those fields should
> be sent down for all users that are within the current room subset.
Future incremental
  > updates will then include changes to this field.
* Additionally, we would need to implement a lazy loading cache similar
to the legacy sync. (not part of MSC as such)

Depending on review these could either be added to this pr, or to keep
this pr from not growing too much, be added in a follow-up pr, as they
are more enhancement to this base sliding sync profile updates
functionality than a part of the core functionality.

### Pull Request Checklist

<!-- Please read
https://element-hq.github.io/synapse/latest/development/contributing_guide.html
before submitting your pull request -->

* [x] Pull request is based on the develop branch
* [x] Pull request includes a [changelog
file](https://element-hq.github.io/synapse/latest/development/contributing_guide.html#changelog).
The entry should:
- Be a short description of your change which makes sense to users.
"Fixed a bug that prevented receiving messages from other servers."
instead of "Moved X method from `EventStore` to `EventWorkerStore`.".
  - Use markdown where necessary, mostly for `code blocks`.
  - End with either a period (.) or an exclamation mark (!).
  - Start with a capital letter.
- Feel free to credit yourself, by adding a sentence "Contributed by
@github_username." or "Contributed by [Your Name]." to the end of the
entry.
* [x] [Code
style](https://element-hq.github.io/synapse/latest/code_style.html) is
correct (run the
[linters](https://element-hq.github.io/synapse/latest/development/contributing_guide.html#run-the-linters))

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Olivier 'reivilibre' <olivier@librepush.net>
Co-authored-by: Olivier 'reivilibre <oliverw@element.io>
2026-08-25 14:37:03 +00:00
Johannes Marbach d0b105757e Add experimental support for MSC4502: Targeted and unrestricted room member queries (#19974)
This is another stopgap towards
https://github.com/element-hq/voip-internal/issues/641 and adds
experimental support for
[MSC4502](https://github.com/matrix-org/matrix-spec-proposals/pull/4502).

This adds a new endpoint `/rooms/{roomId}/is_joined` to query if a user
or server is joined to a room known to the homeserver. Access to the
endpoint is guarded behind a new OAuth scope that can be assigned when
registering application services.

Tracking issue: https://github.com/element-hq/synapse/issues/20118

---------

Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
2026-08-20 15:27:11 +01:00
catfromplan9 0b086d1531 Fix thumbnailing MPO images (#20025)
Signed-off-by: cat <cat@plan9.rocks>
2026-08-11 13:52:49 +00:00
c5f02a9313 Introduce support for MSC4429: Profile Updates for Legacy Sync (#19556)
Implements support for [MSC4429: Profile Updates for Legacy
Sync](https://github.com/matrix-org/matrix-spec-proposals/pull/4429).

Paired with https://github.com/matrix-org/complement/pull/849 and
https://github.com/matrix-org/sytest/tree/anoa/msc4429

Tracking issue for removing unstable identifiers in Synapse:
https://github.com/element-hq/synapse/issues/19891
Further improvements tracked in:
https://github.com/element-hq/synapse/issues/19981

---------

Co-authored-by: Half-Shot <will@half-shot.uk>
Co-authored-by: Jason Robinson <jasonr@element.io>
Co-authored-by: Olivier 'reivilibre' <oliverw@element.io>
2026-08-06 15:35:07 +01:00
Olivier 'reivilibre 9761f9bc42 Merge branch 'master' into develop 2026-07-28 14:39:05 +01:00
Olivier 'reivilibre 0a2456fef3 Validate room name, avatar and heroes' profile fields before sending down sliding sync as trusted fields
Fixes: https://github.com/element-hq/synapse/security/advisories/GHSA-jhcg-5392-5mjw
Fixes: https://github.com/matrix-org/internal-config/issues/1751

I introduce some stricter JSON types that don't break down to `Any` — it seems these have become possible since our last attempt.
(I'm pretty sure mypy wouldn't let you do this a few years ago.)
Our `dict[str, Any]` type is such a footgun. I'd like to spread this out further, but will do so after the security release.

I then use these stricter JSON types on everything the sliding sync handler pulls out of `event.content` and therefore get forced into a bare minimum level of validation, by the type checker.

-----

Reviewed-on: https://github.com/element-hq/synapse-private/pull/151
2026-07-28 13:58:06 +01:00
Olivier 'reivilibre c3adee3509 Restrict what errors we forward to clients from federation and policy servers
Fixes: https://github.com/element-hq/synapse/security/advisories/GHSA-95fh-hv8c-chvq
Fixes: https://github.com/matrix-org/internal-config/issues/1721

https://github.com/element-hq/synapse-private/pull/158 is the end goal, I think

-----

Reviewed-on: https://github.com/element-hq/synapse-private/pull/159
2026-07-28 13:58:06 +01:00
Olivier 'reivilibre 44216bf2b6 Limit the size of push rules
Fixes: https://github.com/element-hq/synapse/security/advisories/GHSA-fp53-rw9v-hcf9
Fixes: https://github.com/matrix-org/internal-config/issues/1071

Because this is an out-of-spec limit and could break someone's workflow on release day, I've opted to make it configurable.

-----

Reviewed-on: https://github.com/element-hq/synapse-private/pull/149
2026-07-28 13:58:06 +01:00
Olivier 'reivilibre 51988c509f Fix spoofed to-device messages over federation not being dropped.
Fixes: https://github.com/element-hq/synapse/security/advisories/GHSA-rgv2-84w7-5j9p
Fixes: https://github.com/matrix-org/internal-config/issues/1520
Introduced in: d4a35ada28

The code was obviously intended to drop them,
but the if block only logged without actually taking any action.

-----

Reviewed-on: https://github.com/element-hq/synapse-private/pull/145
2026-07-28 13:58:06 +01:00
Hugh Nimmo-Smith 97fb38eca6 Return M_USER_LIMIT_EXCEEDED error code for media upload limits from MSC4335 (#18876)
This PR uses the new `M_USER_LIMIT_EXCEEDED` common error code instead
of the malformed `M_RESOURCE_LIMIT_EXCEEDED` error code (as reported by
#18749) (spec: MSC4335).

The error code is also changed from `400` to `403` as this matches what
is in the spec for the endpoints:
https://spec.matrix.org/v1.18/client-server-api/#post_matrixmediav3upload
and
https://spec.matrix.org/v1.18/client-server-api/#put_matrixmediav3uploadservernamemediaid
(albeit the latter says that `M_FORBIDDEN` would be returned)

By default a new built-in `media_upload_limit_exceeded.html` template
will be served for the `info_uri`. Administrators can specify an
external URI in config instead.

Compatibility is retained for any existing modules making use the
`MediaUploadLimit` (e.g. via the `get_media_upload_limits_for_user`
callback).
2026-07-24 16:39:04 -05:00
夜坂雅 5ed830b3b4 Change default room version to 11 (MSC4239) (#18680)
Fix #18530

Complement test changes: https://github.com/matrix-org/complement/pull/858

SyTest changes: https://github.com/matrix-org/sytest/pull/1422
2026-07-16 12:33:13 -05:00
catfromplan9 30088f38c0 Implement support for generating animated thumbnails in the media thumbnailer (#18831)
Signed-off-by: cat <cat@plan9.rocks>
2026-07-15 15:37:09 +00:00
Andrew Morgan 6bfec47c47 Revert "Fix flaky 3PID inhibit error unit tests" (#19916) 2026-07-14 09:46:28 +00:00
SpiritCrocandAndrew Morgan bf0f4f6a9d Support MSC4446: allow moving fully read markers backwards (#19663)
Co-authored-by: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com>
2026-07-13 16:12:26 +00:00
Andrew FerrazzuttiandEric Eastwood c0c2b37d5e MSC4140: update error responses (#19539)
- Impose limit of scheduled delayed events
- Update error codes to match latest draft of MSC4140 

---------

Co-authored-by: Eric Eastwood <madlittlemods@gmail.com>
2026-07-11 01:23:33 +00:00
Eric Eastwood c63d77a79d Rust database access via Python database connection pool v2 (#19878)
This is a stepping stone before we can go full Rust everywhere. We're
providing a generic interface as we want database access to work in
Synapse and `synapse-rust-apps`. In `synapse-rust-apps`, we will use a
`tokio-postgres` based database connection pool so it's full Rust.

We want to avoid the situation where we have two database connection
pools (one for Python, one for Rust) as we've run into connection
exhaustion problems on Matrix.org before.

As an example of using it and sanity check for all this work (including
tests), I've also ported over the `/versions` handler to the Rust side
with database access. The `/versions` endpoint is the simplest endpoint
I could find that still had some database access. Hopefully the refactor
on `/versions` isn't that controversial as it's not really the point of
this PR. We can always remove it from this PR but it's just here as a
sanity check that all of this works.


### Why `runInteraction(...)`?

Using the same `runInteraction` pattern that we already have in Synapse
means we can port over existing Synapse code/endpoints without much
thought. But this pattern also makes sense because we want[^1]
transactions to have repeatable-read isolation (easy to think about,
less foot-guns). Having everything thappen in a function callback means
we can do retries for serialization/deadlock errors.

[^1]: To note: Ideally, we'd want the least isolation possible but the
problem is that there is no tooling to yell at you when your
queries/logic is wrong so repeatable-read isolation is a great balance.


> When an application receives this error message, it should abort the
current transaction and retry the whole transaction from the beginning.
The second time through, the transaction will see the
previously-committed change as part of its initial view of the database,
so there is no logical conflict in using the new version of the row as
the starting point for the new transaction's update.
> 
> Note that only updating transactions might need to be retried;
read-only transactions will never have serialization conflicts.
>
> *--
https://www.postgresql.org/docs/current/transaction-iso.html#XACT-REPEATABLE-READ*


As a note, this strategy is less of an impedance mismatch (aligns more
closely) with Synapse so the glue code for the `python_db_pool` should
also be simpler.


### How does this interact with logcontext (`LoggingContext`)?

See [docs on log
contexts](https://github.com/element-hq/synapse/blob/4e9f7757f17ba81b8747b7f8f9646d17df145aa3/docs/log_contexts.md)
for more background.

We already support normal logging from Rust -> Python with `pyo3-log`
and `log` but as soon as we pass a thread boundary, everything is logged
against the `sentinel` log context. Normally, we want logs and CPU/DB
usage correlated with the request that spawned the work.

You can see how I took a stab at fixing this in
https://github.com/element-hq/synapse/pull/19846 by capturing the
logcontext in a Tokio task local and re-activating as necessary. For
example, in that PR, I reactivated the logcontext in
`run_python_awaitable(...)` which we use to call `runInteraction(...)`
from the Rust side which means all of the database usage is correlated
with the request as expected. It also means any `log:info!(...)` done in
`run_interaction(...)` is correlated correctly. But there needs to be a
better story for when you want to log everywhere else.

I haven't explored tracking CPU usage on the Rust side.

I've left all of this out of this PR as I think it will be better to
tackle this as a dedicated follow-up. For example, I'm thinking about
instead creating a new `LoggingContext` with the `parent_context` set to
the calling context and try to avoid needing to call
`set_current_context(...)` on the Python side where possible (like
tracking CPU).



### Testing strategy

Added some tests that exercise some `async` Rust handlers for the
`/versions` endpoint:
```
SYNAPSE_TEST_LOG_LEVEL=INFO poetry run trial tests.rest.client.test_versions.VersionsTestCase
```

Real-world:

 1. `poetry run synapse_homeserver --config-path homeserver.yaml`
 1. `GET http://localhost:8008/_matrix/client/versions`
2026-07-07 13:07:35 -05:00
Eric Eastwood 026937b251 Update FakeChannel.await_result(...) to drive async Rust (Tokio runtime/thread pool) (#19879)
Split off from https://github.com/element-hq/synapse/pull/19878 (needed
to drive requests in tests that use async Rust)

Follow-up to https://github.com/element-hq/synapse/pull/19871
2026-07-07 04:07:19 -05:00
Andrew Morgan c7a47bc121 Fix flaky 3PID inhibit error unit tests (#19913) 2026-07-06 11:34:41 +00:00
807795e5ee Change the MSC3814 dehydrated device /events endpoint paging to match spec conventions (#19897)
To match [the spec requirements for
pagination](https://spec.matrix.org/v1.9/appendices/#pagination):

* rename the query parameter of
`/org.matrix.msc3814.v1/dehydrated_device/[device_id]/events` to `from`,
and
* omit `next_batch` from the response content if there are no more
events to fetch

This matches [the changes that are coming in
MSC3814](https://github.com/matrix-org/matrix-spec-proposals/pull/3814/files#r1540896531).

The previous change https://github.com/element-hq/synapse/pull/19896
kept the old POST API to support old clients. This preserves that
compatibility, so the POST API still works how it used to.

Depends on https://github.com/element-hq/synapse/pull/19896
Part of https://github.com/element-hq/element-meta/issues/2799


### Pull Request Checklist

* [x] Pull request is based on the develop branch
* [x] Pull request includes a [changelog
file](https://element-hq.github.io/synapse/latest/development/contributing_guide.html#changelog).
The entry should:
* [x] [Code
style](https://element-hq.github.io/synapse/latest/code_style.html) is
correct (run the
[linters](https://element-hq.github.io/synapse/latest/development/contributing_guide.html#run-the-linters))

---------

Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
Co-authored-by: Quentin Gliech <quenting@element.io>
2026-07-03 14:48:43 +02:00
Hugh Nimmo-Smith 4104058e83 Make MSC4388 (sign-in with QR code) PUT requests idempotent (#19808) 2026-07-03 12:09:16 +00:00
Olivier 'reivilibre 5df6d1be65 Remove legacy MSC3861 auth delegation support (#19895)
Modern 'MAS integration' (as we call it now) is, of course, preserved.

Fixes: #19549

---------

Signed-off-by: Olivier 'reivilibre <oliverw@matrix.org>
2026-07-02 12:25:11 +01:00
Andy BalaamandMatthew Hodgson aa97687305 Change MSC3814 dehydrated device /events endpoint from POST to GET (#19896)
Change `/org.matrix.msc3814.v1/dehydrated_device/[device_id]/events` to
accept GET requests instead of POST.

The original version of
[MSC3814](https://github.com/matrix-org/matrix-spec-proposals/pull/3814)
said we should delete keys after returning them from this endpoint, but
it is being updated to say we should not delete them, and therefore the
appropriate verb is GET.

Synapse already doesn't delete anything, so we just need to change to a
GET with a `next_batch` query param. (Currently it is a POST with
`next_batch` in the JSON content.)

This code was initially written by @ara4n and Claude, but both he and I
have read it and think it makes sense. I am far from a Synapse expert,
so feel free to tell me it's all wrong and point me in the right
direction.

I don't know what system tests will be affected by this, but I guess we
will see when the CI runs (right?).

This is a change to an unstable endpoint so no need for notifications
about breaking changes or similar.

Part of https://github.com/element-hq/element-meta/issues/2704

### Pull Request Checklist

* [x] Pull request is based on the develop branch
* [x] Pull request includes a [changelog
file](https://element-hq.github.io/synapse/latest/development/contributing_guide.html#changelog).
The entry should:
* [x] [Code
style](https://element-hq.github.io/synapse/latest/code_style.html) is
correct (run the
[linters](https://element-hq.github.io/synapse/latest/development/contributing_guide.html#run-the-linters))

---------

Co-authored-by: Matthew Hodgson <matthew@matrix.org>
2026-06-30 21:20:26 +00:00
toonbr1meandtoonbr1me 5edcb0f542 MSC4143: Advertise org.matrix.msc4143 in /versions unstable_features (#19646)
Closes #19580

---------

Co-authored-by: toonbr1me <toonbr1me@users.noreply.github.com>
2026-06-29 14:07:43 +01:00
Olivier 'reivilibreandEric Eastwood 16fe5fb462 Expose MSC4354 Sticky Events over MSC4186 (Simplified) Sliding Sync. (#19591)
Follows: #19487
Part of: MSC4354 whose experimental feature tracking issue is #19409

This PR implements the Sliding Sync (MSC4186) extension described in
MSC4354, allowing sliding sync clients
to receive sticky events in a reliable way.

The logic is much the same as for oldschool sync (implementation in
#19487),
although in the sliding sync extension, the client can choose their own
limit
and must control their own pagination through an extra token in the
extension request/response bodies.

Note this does not yet send down existing sticky events in the
room when the room has been newly-joined.
This newly-discovered gap is tracked at #19662 and will be addressed for
both current sync and MSC4186 SSS soon.

---------

Signed-off-by: Olivier 'reivilibre <oliverw@matrix.org>
Co-authored-by: Eric Eastwood <erice@element.io>
2026-06-23 13:04:36 +00:00
Will HuntandCopilot Autofix powered by AI d3fc81974a MSC1763: Implement /_matrix/client/unstable/org.matrix.msc1763/retention/configuration (#19853)
Implements https://github.com/matrix-org/matrix-spec-proposals/pull/1763
Tracking issue https://github.com/element-hq/synapse/issues/19852

This only implements the MSC's configuration endpoint and does not add
new configuration fields or options. The reason for including this is to
allow clients to run global retention against locally stored events, and
to be able to offer configuration UI.

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-18 17:37:02 +00:00
2487b31422 MSC4140: allow auth on management endpoints for delayed events (#19794)
Co-authored-by: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-18 13:27:37 +00:00
Eric Eastwood d11b012a81 Force keyword-args for clear default_config(server_name="test") usage (#19849)
I.e. `default_config("test")` and `default_config("test", False)` are so
opaque and hard to connect the dots with.

Spawning from trying to figure out what the `server_name` is set as for
our `HomeserverTestCase` in order to reference it in
https://github.com/element-hq/synapse/pull/19848#discussion_r3397455309
2026-06-12 09:45:22 -05:00
Erik Johnston ac21bf08f3 Port Requester class to Rust. (#19828)
This is in prep for converting the event serialization to Rust.

This is a fairly mechanical port, except that we store the appservice ID
rather than the appservice object. This avoids us having to store a
`Py<..>` (or port the appservice object over).
2026-06-09 10:26:04 +01:00
8c9b1ff877 Reintroduce #19714 - Send a SSS response immediately if the config has changed (#19792)
Reintroduces #19714, after being reverted in #19784.

Fix https://github.com/element-hq/synapse/issues/18844
Fix https://github.com/element-hq/synapse/issues/19783
Fix https://github.com/element-hq/synapse/issues/18880

This PR also adds a fix so that we don't always return immediately when
using the e2ee extension.

---------

Co-authored-by: Benjamin Bouvier <benjamin@bouvier.cc>
Co-authored-by: Eric Eastwood <madlittlemods@gmail.com>
Co-authored-by: Eric Eastwood <erice@element.io>
2026-06-08 11:42:57 +01:00
5bf825c016 Limit to-device EDU sizes (#19617)
This is based on https://github.com/element-hq/synapse/pull/18416, which
got reverted (#19614) due to it incorrectly rejecting to-device messages
to users with many devices (and thus breaking message sending).

Fix https://github.com/element-hq/synapse/issues/17035

A to-device message content looks like:

```jsonc
{
  "@user:domain": {"device1": {...}, "device2": {...}},
  ...
}
```

The previous PR would split up into multiple EDUs, each with a subset of
the users. However, if one user's entry was too large it would not
further split it up and then error out.

The main change in this PR is to allow splitting up a single user into
multiple EDUs.

Other changes:
1. Rename to `SOFT_MAX_EDU_SIZE` to indicate that we sometimes send EDUs
with larger size than that, and its more a target than a hard limit.
2. Check early if any to-device message (to a specific device) is too
large to send, even if we're not going to send it over federation. This
ensures that we catch issues where clients try to send too large
to-device.

This still means that if a client send a large individual to-device
message it will fail, but I don't believe we ever send such large
to-device messages (normally they're in the range of a few KB).

---

I ended up changing the implementation a bunch to make it easy to reuse
the code to split up dictionaries. Instead of repeatedly splitting up
the EDU until each bit fits into the size, we instead record the size of
each entry in the dict and instead split up based on cumulative size.
This means we call `encode_canonical_json` on each entry rather than
once on the entire struct, but its not significantly slower to do so.

--

cc @MatMaul @MadLittleMods

---------

Co-authored-by: Mathieu Velten <matmaul@gmail.com>
Co-authored-by: mcalinghee <mcalinghee.dev@gmail.com>
Co-authored-by: Eric Eastwood <madlittlemods@gmail.com>
2026-06-02 15:57:55 +00:00
Erik JohnstonandEric Eastwood 9e2a076144 Port Event class to Rust (#19701)
Ports the event class to Rust.

The main difference here are:
1. There is now a single event class
2. We now validate a lot more at event construction time than we
previously did (we basically checked nothing before). This required some
changes to the tests, including
https://github.com/matrix-org/sytest/pull/1423

Reviewable commit-by-commit.

### Overview of Event Rust structure

The format of the event struct in Rust is quite different than that in
Python.

The top-level looks like:

```rust
pub struct Event {
    /// The parsed event JSON.
    fields: FormattedEvent,

    /// The event ID. For format v1 this is read directly from the JSON;
    /// for v2+ it is computed from the canonical-JSON hash at
    /// construction time and cached here.
    event_id: Arc<str>,

    /// Synapse-internal per-event state that lives outside the federated
    /// JSON (e.g. outlier flag, soft-failure, stream positions).
    #[pyo3(get)]
    internal_metadata: EventInternalMetadata,

    /// The room version this event was parsed for.
    #[pyo3(get)]
    room_version: &'static RoomVersion,

    /// `None` for accepted events; otherwise a short reason set by auth
    /// when the event was rejected.
    rejected_reason: Option<Box<str>>,
}
```

which includes the actual parsed event in `FormattedEvent`, plus the
rest of the event metadata.

```rust
pub struct FormattedEvent<E = Arc<EventFormatEnum>> {
    #[serde(default)]
    pub signatures: Signatures,

    #[serde(default)]
    pub unsigned: Unsigned,

    #[serde(flatten)]
    pub specific_fields: E,

    #[serde(flatten)]
    pub common_fields: Arc<EventCommonFields>,
}
```

The struct is further split into the common fields, format specific
fields, plus the signatures and unsigned. We split out the signature and
unsigned fields as they are mutable, so when we clone the event we can
still share the common and specific fields and only copy signature and
unsigned.

The `specific_fields` are the fields that depend on the format version.
They can either be a specific format (e.g. `E = EventFormatV1`) or a
type-erased enum `EventFormatEnum` that is across all room versions:

```rust
pub enum EventFormatEnum {
    V1(EventFormatV1),
    V2V3(EventFormatV2V3),
    V4(EventFormatV4),
    VMSC4242(EventFormatVMSC4242),
}
```

For example:

```rust
/// Shared flat-list encoding of `auth_events` and `prev_events`, reused
/// by every format from v2/v3 onwards.
#[derive(Serialize, Deserialize)]
pub struct SimpleAuthPrevEvents {
    pub auth_events: Vec<String>,
    pub prev_events: Vec<String>,
}

/// Version-specific fields for room versions 3-10.
#[derive(Serialize, Deserialize)]
pub struct EventFormatV2V3 {
    pub room_id: Box<str>,
    #[serde(flatten)]
    pub auth_prev_events: SimpleAuthPrevEvents,
}
```


### Dev notes

As discussed in
[`#element-backend-internal:matrix.org`](https://matrix.to/#/!SGNQGPGUwtcPBUotTL:matrix.org/$3gTjDO440GbAz57cXcCawwiyFLiD0crrarvS1uhzKOY?via=jki.re&via=element.io&via=matrix.org)

---------

Co-authored-by: Eric Eastwood <erice@element.io>
2026-06-02 11:05:38 +01:00
Joe Groocock 966e193e4e Fix sending heroes in SSS when m.room.name="" (#19468)
As per the spec, a room with m.room.name value that is absent, null or
empty should be treated as if there is no m.room.name event at all:
https://spec.matrix.org/v1.17/client-server-api/#mroomname

This fetches the full m.room.name event and checks the content.name
instead of only checking the existence of the m.room.name event. This
results in correctly sending heroes for those rooms.

Fixes: https://github.com/element-hq/synapse/issues/19447

Signed-off-by: Joe Groocock <me@frebib.net>
2026-05-20 12:10:54 +01:00
FrenchGithubUser 87095ae500 fix: invalidate access token cache on device deletion (#19483)
when an access token had a refresh token associated to it in the
database, deleting this refresh token (for example when deleting the
device using it) would cascade delete the access token, which wouldn't
be returned by the sql query that was supposed to delete it on its own,
and an empty array was passed to the cache invalidation function.
2026-05-20 11:06:16 +01:00
Eric Eastwood 8eb220a5e2 Replace wait_for_quarantined_media_stream_id(...) with standard wait_for_stream_token(...) (#19764)
In order to be able to use `wait_for_stream_token(...)`, we have to add
the `quarantined_media` stream to the `StreamToken`. Even though we
don't care about `/sync`'ing `quarantined_media`, this aligns with the
future where all endpoints should probably use `StreamToken`, see
https://github.com/element-hq/synapse/issues/19647

Follow-up to https://github.com/element-hq/synapse/pull/19558 and
https://github.com/element-hq/synapse/pull/19644
2026-05-15 13:51:03 -05:00