Description
When unstableMSC1763Retention is enabled and a room has an effective
m.room.retention policy with max_lifetime, expired events can still
become visible after backward pagination.
This can be reproduced in Element Web with feature_retention enabled:
messages older than max_lifetime disappear initially, but scrolling
far enough backwards can load and display them again.
Suspected cause
There appear to be two related paths:
-
Room.addEventsToTimeline() checks
this.retention?.shouldEventBeRetained(event), but pagination code
calls EventTimelineSet.addEventsToTimeline() directly.
-
RoomRetentionPolicy.processTimeline() only scans
room.getLiveTimeline(). Events inserted into filtered or detached
timeline sets are therefore not found by the cleanup pass.
Minimal regression test
Tested against matrix-js-sdk develop commit:
2a625eb8f611a701867ac47f3e30345cfbb13f00
it("does not expose expired events paginated into a filtered timeline", async () => {
getCachedMock.mockReturnValue({
policies: {
[ROOM_ID]: { max_lifetime: ONE_DAY_MS },
},
});
await applyPolicy();
const filter = new Filter(USER_ID, "test_filter");
filter.setDefinition({});
const timelineSet = room.getOrCreateFilteredTimelineSet(filter, {
prepopulateTimeline: false,
useSyncEvents: false,
});
const expiredEvent = makeMessageEvent(
Date.now() - 2 * ONE_DAY_MS,
);
timelineSet.addEventsToTimeline(
[expiredEvent],
true,
false,
timelineSet.getLiveTimeline(),
null,
);
await vi.advanceTimersByTimeAsync(201);
expect(
timelineSet.getLiveTimeline().getEvents(),
).toHaveLength(0);
});
Result:
Expected: 0 events
Received: 1 event
All 31 existing room-retention tests passed; only this new pagination
regression test failed.
Runtime logging also reported:
Running processTimeline
Found no expired events
Expected behavior
Expired events should not be inserted into any visible timeline set, or
they should be synthetically redacted/removed immediately after
pagination.
Actual behavior
Expired events inserted through historical pagination remain in a
filtered timeline set and can be displayed.
Affected consumer
Reproduced in Element Web 1.12.25 with feature_retention enabled and a
room policy using max_lifetime.
Possible direction
Either retention should be enforced at the EventTimelineSet insertion
boundary, pagination should route through the retention-aware Room API,
or the cleanup process should cover all relevant room timeline sets.
Description
When
unstableMSC1763Retentionis enabled and a room has an effectivem.room.retentionpolicy withmax_lifetime, expired events can stillbecome visible after backward pagination.
This can be reproduced in Element Web with
feature_retentionenabled:messages older than
max_lifetimedisappear initially, but scrollingfar enough backwards can load and display them again.
Suspected cause
There appear to be two related paths:
Room.addEventsToTimeline()checksthis.retention?.shouldEventBeRetained(event), but pagination codecalls
EventTimelineSet.addEventsToTimeline()directly.RoomRetentionPolicy.processTimeline()only scansroom.getLiveTimeline(). Events inserted into filtered or detachedtimeline sets are therefore not found by the cleanup pass.
Minimal regression test
Tested against matrix-js-sdk develop commit:
2a625eb8f611a701867ac47f3e30345cfbb13f00Result:
All 31 existing room-retention tests passed; only this new pagination
regression test failed.
Runtime logging also reported:
Expected behavior
Expired events should not be inserted into any visible timeline set, or
they should be synthetically redacted/removed immediately after
pagination.
Actual behavior
Expired events inserted through historical pagination remain in a
filtered timeline set and can be displayed.
Affected consumer
Reproduced in Element Web 1.12.25 with
feature_retentionenabled and aroom policy using
max_lifetime.Possible direction
Either retention should be enforced at the
EventTimelineSetinsertionboundary, pagination should route through the retention-aware Room API,
or the cleanup process should cover all relevant room timeline sets.