matrix-org / matrix-org/matrix-spec
Auth rules for PL events don't say if the default value counts as "present" or not
- Dominant language
- HTML
- Stars
- 330
- Forks
- 150
- Avg merge
- 2h 21m
- Merged PRs (30d)
- 3
Description
**Link to problem area**:
https://spec.matrix.org/v1.17/rooms/v12/#authorization-rules
> For each entry being changed in, or removed from, the `events` or `notifications` properties:
> - If the current value is greater than the `sender`’s current power level, reject.
>
> For each entry being added to, or changed in, the `events` or `notifications` properties:
> - If the new value is greater than the `sender`’s current power level, reject.
**Issue**
What counts as "added"? There are two interpretations:
- strict: any new key in the JSON map
- loose: any semantic behaviour change. `foo: absent → 30` with `events_default = 30` is behaviourally the same, so not an "addition".
The loose reading is wrong, but the spec doesn't call this out. The problem with the loose reading is it allows unauthorised users to modify the PL event in two scenarios:
- Assigning a specific event type's PL value: If `events["foo"]` was absent, and `events_default: 30`, then a user can set `events["foo"] = 30` under the loose reading. Strict reading would forbid it, causing divergence.
- Removing a specific event type's PL value: If `events["m.room.name"] == 50` (it is set to this by default) and `state_default: 50` (it is by default), then a user can unset `events["m.room.name"]` since semantically the PL doesn't change.
This can cause state to diverge:
- Strict servers will reject all subsequent PL events from loose servers due to the PL event referencing the previous PL event in `auth_events`, causing PL events to diverge.
- The mainline ordering uses PL events _under the assumption_ that only authorised users can modify specific event values, which isn't true under the loose reading. This provides a mechanism for non-auth room state to diverge, as loose servers will see a much longer mainline than strict servers.
In _practice_ this is quite hard to do because by default in order for the unauthorised user to no-op the `events` map they need to be authorised to send PL events, which by default is `"m.room.power_levels": 100,`, which likely limits the impact of this.
It's primarily an issue when implementations write things like:
```
prev_value = existing_state.get_or_default("some.type", 50)
next_value = pl_event.get_or_default("some.type", 50)
if prev_value != next_value {
// permission checks
}
```
^ is wrong.
Contributor guide
Research direction
Start with the linked Matrix v1.17 authorization-rules section and compare its wording with the issue's strict interpretation of “added” and “removed.” Update the specification to make clear that presence is based on JSON map keys rather than semantic changes caused by default values, and verify that the two described power-level examples are unambiguous.
Written by the indexing model from the issue text.
Assessment
- Domain
- documentation
- Issue type
- Documentation
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100