Compare commits

...

3 commits

Author SHA1 Message Date
Johannes Marbach 9834f1e352
Merge 60f4e5d61d into 6b6d351ef8 2026-02-24 13:35:17 +00:00
Tulir Asokan 6b6d351ef8
Specify basic validation for federation membership endpoints (#2284)
Signed-off-by: Tulir Asokan <tulir@maunium.net>
Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
2026-02-24 13:35:05 +00:00
Johannes Marbach 60f4e5d61d Spec for MSC4277: Harmonizing the reporting endpoints
Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
2026-02-12 14:07:30 +01:00
12 changed files with 230 additions and 49 deletions

View file

@ -0,0 +1 @@
`/_matrix/client/v3/rooms/{roomId}/report` and `/_matrix/client/v3/rooms/{roomId}/report/{eventId}` may respond with HTTP 200 regardless of the reported subject's existence or add a random delay when generating responses as per [MSC4277](https://github.com/matrix-org/matrix-spec-proposals/pull/4277).

View file

@ -0,0 +1 @@
The `score` request parameter on `/_matrix/client/v3/rooms/{roomId}/report/{eventId}` was removed as per [MSC4277](https://github.com/matrix-org/matrix-spec-proposals/pull/4277).

View file

@ -0,0 +1 @@
Specify validation for PDUs passed to and returned from federation membership endpoints.

View file

@ -868,8 +868,10 @@ selecting a resident from the candidate list, and using the
enough information for the joining server to fill in the event. enough information for the joining server to fill in the event.
The joining server is expected to add or replace the `origin`, The joining server is expected to add or replace the `origin`,
`origin_server_ts`, and `event_id` on the templated event received by `origin_server_ts`, and `event_id` on the templated event received by the
the resident server. This event is then signed by the joining server. resident server. The joining server MUST also verify that the `type`, `room_id`,
`sender`, `state_key` and `content.membership` fields have the expected values.
This event is then signed by the joining server.
To complete the join handshake, the joining server submits this new event To complete the join handshake, the joining server submits this new event
to the resident server it used for `GET /make_join`, using the `PUT /send_join` to the resident server it used for `GET /make_join`, using the `PUT /send_join`

View file

@ -25,6 +25,15 @@ paths:
the appropriate people. How such information is delivered is left up to the appropriate people. How such information is delivered is left up to
implementations. The caller is not required to be joined to the room to implementations. The caller is not required to be joined to the room to
report it. report it.
Clients could infer whether a reported room exists based on the 404 response.
Homeservers that wish to conceal this information MAY return 200 responses
regardless of the existence of the reported room.
Furthermore, it might be possible for clients to deduce whether a reported
room exists by timing the response. This is because only a report for an
existing room will require the homeserver to do further processing. To
combat this, homeservers MAY add a random delay when generating a response.
operationId: reportRoom operationId: reportRoom
parameters: parameters:
- in: path - in: path
@ -52,6 +61,11 @@ paths:
security: security:
- accessTokenQuery: [] - accessTokenQuery: []
- accessTokenBearer: [] - accessTokenBearer: []
x-changedInMatrixVersion:
1.18: |
Servers MAY deny room ID enumeration by using the 200 response
regardless of the existence of the reported room and/or by adding
a random delay when generating responses.
responses: responses:
"200": "200":
description: The room has been reported successfully. description: The room has been reported successfully.
@ -91,6 +105,10 @@ paths:
the appropriate people. The caller must be joined to the room to report the appropriate people. The caller must be joined to the room to report
it. it.
Clients could infer whether a reported event exists based on the 404 response.
Homeservers that wish to conceal this information MAY return 200 responses
regardless of the existence of the reported event.
Furthermore, it might be possible for clients to deduce whether a reported Furthermore, it might be possible for clients to deduce whether a reported
event exists by timing the response. This is because only a report for an event exists by timing the response. This is because only a report for an
existing event will require the homeserver to do further processing. To existing event will require the homeserver to do further processing. To
@ -117,15 +135,9 @@ paths:
schema: schema:
type: object type: object
example: { example: {
"score": -100,
"reason": "this makes me sad" "reason": "this makes me sad"
} }
properties: properties:
score:
type: integer
description: |-
The score to rate this content as where -100 is most offensive
and 0 is inoffensive.
reason: reason:
type: string type: string
description: The reason the content is being reported. description: The reason the content is being reported.
@ -136,6 +148,10 @@ paths:
x-changedInMatrixVersion: x-changedInMatrixVersion:
1.8: | 1.8: |
This endpoint now requires the user to be joined to the room. This endpoint now requires the user to be joined to the room.
1.18: |
The `score` request parameter was removed. Additionally, servers
may deny event ID enumeration by using the 200 response
regardless of the existence of the reported event.
responses: responses:
"200": "200":
description: The event has been reported successfully. description: The event has been reported successfully.

View file

@ -36,6 +36,28 @@ paths:
Also note that if the remote homeserver is already in the room, it will receive the Also note that if the remote homeserver is already in the room, it will receive the
invite event twice; once through this endpoint, and again through a [federation invite event twice; once through this endpoint, and again through a [federation
transaction](/server-server-api/#transactions). transaction](/server-server-api/#transactions).
Servers MUST apply certain validation to ensure they don't accidentally sign non-invite
events from a malicious server. A specific error code is not mandated, but servers SHOULD
return `M_INVALID_PARAM` if:
* The invite event fails a [signature check](/server-server-api/#validating-hashes-and-signatures-on-received-events).
* The event type is not `m.room.member`.
* The `membership` field inside the event content is not `invite`.
* The event sender is not a user ID on the origin server.
* The `state_key` is not a user ID on the receiving server.
The `invite_room_state` has additional validation, which servers MAY apply to room versions
1 through 11 and SHOULD apply to all other room versions. As with the above errors, servers
SHOULD return `M_INVALID_PARAM` if:
* The `m.room.create` event is missing from `invite_room_state`.
* One or more entries in `invite_room_state` are not formatted according
to the room's version.
* One or more events fails a [signature check](/server-server-api/#validating-hashes-and-signatures-on-received-events).
* One or more events does not reside in the same room as the invite.
Note: Some room versions may require calculating the room ID for an
event rather than relying on the presence of `room_id`.
operationId: sendInviteV1 operationId: sendInviteV1
security: security:
- signedRequest: [] - signedRequest: []
@ -83,8 +105,7 @@ paths:
MUST additionally be formatted according to the room version specification. MUST additionally be formatted according to the room version specification.
Servers might need to apply validation to the `invite_room_state` depending Servers might need to apply validation to the `invite_room_state` depending
on room version. See the `400 M_MISSING_PARAM` error definition for more on room version. See endpoint description for more information.
information.
Note that events have a different format depending on the room Note that events have a different format depending on the room
version - check the [room version specification](/rooms) for version - check the [room version specification](/rooms) for
@ -178,23 +199,7 @@ paths:
} }
"400": "400":
description: |- description: |-
The `M_MISSING_PARAM` error code is used to indicate one or more of The request is invalid in some way.
the following:
* The `m.room.create` event is missing from `invite_room_state`.
* One or more entries in `invite_room_state` are not formatted according
to the room's version.
* One or more events fails a [signature check](/server-server-api/#validating-hashes-and-signatures-on-received-events).
* One or more events does not reside in the same room as the invite.
Note: Some room versions may require calculating the room ID for an
event rather than relying on the presence of `room_id`.
Servers MAY apply the validation above to room versions 1 through 11,
and SHOULD apply the validation above to all other room versions.
If `M_MISSING_PARAM` is returned and the request is associated with a
Client-Server API request, the Client-Server API request SHOULD fail
with a 5xx error rather than being passed through.
content: content:
application/json: application/json:
schema: schema:
@ -202,7 +207,7 @@ paths:
examples: examples:
response: response:
value: { value: {
"errcode": "M_MISSING_PARAM", "errcode": "M_INVALID_PARAM",
"error": "Create event not among invite state entries." "error": "Create event not among invite state entries."
} }
servers: servers:

View file

@ -40,6 +40,28 @@ paths:
Also note that if the remote homeserver is already in the room, it will receive the Also note that if the remote homeserver is already in the room, it will receive the
invite event twice; once through this endpoint, and again through a [federation invite event twice; once through this endpoint, and again through a [federation
transaction](/server-server-api/#transactions). transaction](/server-server-api/#transactions).
Servers MUST apply certain validation to ensure they don't accidentally sign non-invite
events from a malicious server. A specific error code is not mandated, but servers SHOULD
return `M_INVALID_PARAM` if:
* The invite event fails a [signature check](/server-server-api/#validating-hashes-and-signatures-on-received-events).
* The event type is not `m.room.member`.
* The `membership` field inside the event content is not `invite`.
* The event sender is not a user ID on the origin server.
* The `state_key` is not a user ID on the receiving server.
The `invite_room_state` has additional validation, which servers MAY apply to room versions
1 through 11 and SHOULD apply to all other room versions. As with the above errors, servers
SHOULD return `M_INVALID_PARAM` if:
* The `m.room.create` event is missing from `invite_room_state`.
* One or more entries in `invite_room_state` are not formatted according
to the room's version.
* One or more events fails a [signature check](/server-server-api/#validating-hashes-and-signatures-on-received-events).
* One or more events does not reside in the same room as the invite.
Note: Some room versions may require calculating the room ID for an
event rather than relying on the presence of `room_id`.
operationId: sendInviteV2 operationId: sendInviteV2
security: security:
- signedRequest: [] - signedRequest: []
@ -84,8 +106,7 @@ paths:
MUST additionally be formatted according to the room version specification. MUST additionally be formatted according to the room version specification.
Servers might need to apply validation to the `invite_room_state` depending Servers might need to apply validation to the `invite_room_state` depending
on room version. See the `400 M_MISSING_PARAM` error definition for more on room version. See the endpoint description for more information.
information.
Note that events have a different format depending on the room Note that events have a different format depending on the room
version - check the [room version specification](/rooms) for version - check the [room version specification](/rooms) for
@ -154,22 +175,8 @@ paths:
The error should be passed through to clients so that they The error should be passed through to clients so that they
may give better feedback to users. may give better feedback to users.
The `M_MISSING_PARAM` error code is used to indicate one or more of If `M_MISSING_PARAM` or `M_INVALID_PARAM` is returned and the request is associated
the following: with a Client-Server API request, the Client-Server API request SHOULD fail
* The `m.room.create` event is missing from `invite_room_state`.
* One or more entries in `invite_room_state` are not formatted according
to the room's version.
* One or more events fails a [signature check](/server-server-api/#validating-hashes-and-signatures-on-received-events).
* One or more events does not reside in the same room as the invite.
Note: Some room versions may require calculating the room ID for an
event rather than relying on the presence of `room_id`.
Servers MAY apply the validation above to room versions 1 through 11,
and SHOULD apply the validation above to all other room versions.
If `M_MISSING_PARAM` is returned and the request is associated with a
Client-Server API request, the Client-Server API request SHOULD fail
with a 5xx error rather than being passed through. with a 5xx error rather than being passed through.
content: content:
application/json: application/json:

View file

@ -23,6 +23,17 @@ paths:
description: |- description: |-
Asks the receiving server to return information that the sending Asks the receiving server to return information that the sending
server will need to prepare a join event to get into the room. server will need to prepare a join event to get into the room.
Before signing the returned template and calling `/send_join`,
the sending server MUST verify that:
* the `room_id` is equal to the `roomId` path parameter.
* both the `sender` and `state_key` are equal to the `userId` path parameter.
* the `type` of the event is `m.room.member`.
* the `membership` field inside `content` is `join`.
In case any of the above checks fail, the response MUST be treated as malformed and
discarded. The caller MAY try to join through another server.
operationId: makeJoin operationId: makeJoin
security: security:
- signedRequest: [] - signedRequest: []
@ -36,7 +47,7 @@ paths:
type: string type: string
- in: path - in: path
name: userId name: userId
description: The user ID the join event will be for. description: The user ID the join event will be for. This MUST be a user ID on the origin server.
required: true required: true
example: "@someone:example.org" example: "@someone:example.org"
schema: schema:
@ -238,6 +249,15 @@ paths:
**The request and response body here describe the common **The request and response body here describe the common
event fields in more detail and may be missing other required event fields in more detail and may be missing other required
fields for a PDU.** fields for a PDU.**
The receiving server MUST apply certain validation before accepting the event.
A specific error code is not mandated, but servers SHOULD return `M_INVALID_PARAM` if:
* The join event fails a [signature check](/server-server-api/#validating-hashes-and-signatures-on-received-events).
* The event type is not `m.room.member`.
* The `membership` field inside the event content is not `join`.
* The event sender is not a user ID on the origin server.
* The `state_key` is not equal to the `sender`.
operationId: sendJoinV1 operationId: sendJoinV1
security: security:
- signedRequest: [] - signedRequest: []
@ -388,6 +408,33 @@ paths:
} }
} }
] ]
"400":
description: |-
The request is invalid in some way.
content:
application/json:
schema:
$ref: ../client-server/definitions/errors/error.yaml
examples:
response:
value: {
"errcode": "M_INVALID_PARAM",
"error": "Not a join event."
}
"403":
description: |-
The room that the joining server is attempting to join does not permit the user
to join.
content:
application/json:
schema:
$ref: ../client-server/definitions/errors/error.yaml
examples:
response:
value: {
"errcode": "M_FORBIDDEN",
"error": "You are not invited to this room"
}
servers: servers:
- url: "{protocol}://{hostname}{basePath}" - url: "{protocol}://{hostname}{basePath}"
variables: variables:

View file

@ -38,6 +38,15 @@ paths:
**The request and response body here describe the common **The request and response body here describe the common
event fields in more detail and may be missing other required event fields in more detail and may be missing other required
fields for a PDU.** fields for a PDU.**
The receiving server MUST apply certain validation before accepting the event.
A specific error code is not mandated, but servers SHOULD return `M_INVALID_PARAM` if:
* The join event fails a [signature check](/server-server-api/#validating-hashes-and-signatures-on-received-events).
* The event type is not `m.room.member`.
* The `membership` field inside the event content is not `join`.
* The event sender is not a user ID on the origin server.
* The `state_key` is not equal to the `sender`.
operationId: sendJoinV2 operationId: sendJoinV2
security: security:
- signedRequest: [] - signedRequest: []
@ -247,6 +256,10 @@ paths:
The error should be passed through to clients so that they The error should be passed through to clients so that they
may give better feedback to users. may give better feedback to users.
If `M_MISSING_PARAM` or `M_INVALID_PARAM` is returned and the request is associated
with a Client-Server API request, the Client-Server API request SHOULD fail
with a 5xx error rather than being passed through.
New in `v1.2`, the following error conditions might happen: New in `v1.2`, the following error conditions might happen:
If the room is [restricted](/client-server-api/#restricted-rooms) If the room is [restricted](/client-server-api/#restricted-rooms)

View file

@ -23,6 +23,17 @@ paths:
description: |- description: |-
Asks the receiving server to return information that the sending Asks the receiving server to return information that the sending
server will need to prepare a knock event for the room. server will need to prepare a knock event for the room.
Before signing the returned template and calling `/send_knock`,
the sending server MUST verify that:
* the `room_id` is equal to the `roomId` path parameter.
* both the `sender` and `state_key` are equal to the `userId` path parameter.
* the `type` of the event is `m.room.member`.
* the `membership` field inside `content` is `knock`.
In case any of the above checks fail, the response MUST be treated as malformed and
discarded. The caller MAY try to knock through another server.
operationId: makeKnock operationId: makeKnock
security: security:
- signedRequest: [] - signedRequest: []
@ -36,7 +47,7 @@ paths:
type: string type: string
- in: path - in: path
name: userId name: userId
description: The user ID the knock event will be for. description: The user ID the knock event will be for. This MUST be a user ID on the origin server.
required: true required: true
example: "@someone:example.org" example: "@someone:example.org"
schema: schema:
@ -204,6 +215,15 @@ paths:
**The request and response body here describe the common **The request and response body here describe the common
event fields in more detail and may be missing other required event fields in more detail and may be missing other required
fields for a PDU.** fields for a PDU.**
The receiving server MUST apply certain validation before accepting the event.
A specific error code is not mandated, but servers SHOULD return `M_INVALID_PARAM` if:
* The knock event fails a [signature check](/server-server-api/#validating-hashes-and-signatures-on-received-events).
* The event type is not `m.room.member`.
* The `membership` field inside the event content is not `knock`.
* The event sender is not a user ID on the origin server.
* The `state_key` is not equal to the `sender`.
operationId: sendKnock operationId: sendKnock
security: security:
- signedRequest: [] - signedRequest: []
@ -330,6 +350,19 @@ paths:
"$ref": "./examples/invite_or_knock_state.json" "$ref": "./examples/invite_or_knock_state.json"
} }
} }
"400":
description: |-
The request is invalid in some way.
content:
application/json:
schema:
$ref: ../client-server/definitions/errors/error.yaml
examples:
response:
value: {
"errcode": "M_INVALID_PARAM",
"error": "Not a knock event."
}
"403": "403":
description: |- description: |-
The knocking server or user is not permitted to knock on the room, such as when the The knocking server or user is not permitted to knock on the room, such as when the

View file

@ -23,6 +23,17 @@ paths:
description: |- description: |-
Asks the receiving server to return information that the sending Asks the receiving server to return information that the sending
server will need to prepare a leave event to get out of the room. server will need to prepare a leave event to get out of the room.
Before signing the returned template and calling `/send_leave`,
the sending server MUST verify that:
* the `room_id` is equal to the `roomId` path parameter.
* both the `sender` and `state_key` are equal to the `userId` path parameter.
* the `type` of the event is `m.room.member`.
* the `membership` field inside `content` is `leave`.
In case any of the above checks fail, the response MUST be treated as malformed and
discarded. The caller MAY try to leave through another server.
operationId: makeLeave operationId: makeLeave
security: security:
- signedRequest: [] - signedRequest: []
@ -36,7 +47,7 @@ paths:
type: string type: string
- in: path - in: path
name: userId name: userId
description: The user ID the leave event will be for. description: The user ID the leave event will be for. This MUST be a user ID on the origin server.
required: true required: true
example: "@someone:example.org" example: "@someone:example.org"
schema: schema:
@ -153,6 +164,15 @@ paths:
**The request and response body here describe the common **The request and response body here describe the common
event fields in more detail and may be missing other required event fields in more detail and may be missing other required
fields for a PDU.** fields for a PDU.**
The receiving server MUST apply certain validation before accepting the event.
A specific error code is not mandated, but servers SHOULD return `M_INVALID_PARAM` if:
* The leave event fails a [signature check](/server-server-api/#validating-hashes-and-signatures-on-received-events).
* The event type is not `m.room.member`.
* The `membership` field inside the event content is not `leave`.
* The event sender is not a user ID on the origin server.
* The `state_key` is not equal to the `sender`.
operationId: sendLeaveV1 operationId: sendLeaveV1
security: security:
- signedRequest: [] - signedRequest: []
@ -249,6 +269,19 @@ paths:
200, 200,
{} {}
] ]
"400":
description: |-
The request is invalid in some way.
content:
application/json:
schema:
$ref: ../client-server/definitions/errors/error.yaml
examples:
response:
value: {
"errcode": "M_INVALID_PARAM",
"error": "Not a leave event."
}
servers: servers:
- url: "{protocol}://{hostname}{basePath}" - url: "{protocol}://{hostname}{basePath}"
variables: variables:

View file

@ -38,6 +38,15 @@ paths:
**The request and response body here describe the common **The request and response body here describe the common
event fields in more detail and may be missing other required event fields in more detail and may be missing other required
fields for a PDU.** fields for a PDU.**
The receiving server MUST apply certain validation before accepting the event.
A specific error code is not mandated, but servers SHOULD return `M_INVALID_PARAM` if:
* The leave event fails a [signature check](/server-server-api/#validating-hashes-and-signatures-on-received-events).
* The event type is not `m.room.member`.
* The `membership` field inside the event content is not `leave`.
* The event sender is not a user ID on the origin server.
* The `state_key` is not equal to the `sender`.
operationId: sendLeaveV2 operationId: sendLeaveV2
security: security:
- signedRequest: [] - signedRequest: []
@ -134,6 +143,19 @@ paths:
examples: examples:
response: response:
value: {} value: {}
"400":
description: |-
The request is invalid in some way.
content:
application/json:
schema:
$ref: ../client-server/definitions/errors/error.yaml
examples:
response:
value: {
"errcode": "M_INVALID_PARAM",
"error": "Not a leave event."
}
servers: servers:
- url: "{protocol}://{hostname}{basePath}" - url: "{protocol}://{hostname}{basePath}"
variables: variables: