Skip to content

Add support for MSC4293 - Redact on Kick/Ban #18540

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: develop
Choose a base branch
from

Conversation

H-Shay
Copy link
Contributor

@H-Shay H-Shay commented Jun 12, 2025

Adds support for MSC4293

@H-Shay H-Shay requested a review from a team as a code owner June 12, 2025 00:57
@H-Shay H-Shay marked this pull request as draft June 12, 2025 00:59
Copy link
Contributor

@tulir tulir left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does synapse already pass through the field from /ban requests to the member event?

@H-Shay
Copy link
Contributor Author

H-Shay commented Jun 12, 2025

Does synapse already pass through the field from /ban requests to the member event?

it does not, I have added support for that as well, missed it the first go around.

@H-Shay H-Shay marked this pull request as ready for review June 12, 2025 23:16
Copy link
Member

@anoadragon453 anoadragon453 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a complete review, as I still need to look at tests and the DB updates. But a solid start!

Also be aware that I typically advise people to aim for writing Complement tests when implementing unstable MSCs, such that other homeserver implementations can test against it when it comes time for them to implement the feature.

You don't need to do it here as you've already written a lot of unit tests, but for future reference.

@@ -2065,6 +2068,42 @@ async def _check_for_soft_fail(
soft_failed_event_counter.inc()
event.internal_metadata.soft_failed = True

if self._config.experimental.msc4293_enabled:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're not actually setting internal_metadata.soft_failed in the msc4293_enabled-related code, I'm not sure if it makes sense for the checks to be in this function.

We might even still hit _persist_events_and_state_updates code later on, event if an event is soft failed(?).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My rationale for putting this check here was that this check only applies to events coming in over federation - if a request to send a message into a room the user is banned from comes over the cs api it will just be denied - thus we don't need to worry about redacting it. In this case, however, since the request comes over federation the event is soft-failed but still created, so we need to redact it. I chose this particular location because I was looking for a place where we had already pulled and calculated the state for event, as I was trying to avoid adding database calls if at all possible. If that's not a strong enough rationale for adding it here I can find another place!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, that is very handy. My hangup is that the logic we're doing for redactions here doesn't result in any soft fails occurring.

One way we could refactor this is to take all of the auth DAG calculation out of _check_for_soft_fail and put that in a _get_current_auth_events function. Then pass the result of that to _check_for_soft_fail and, i.e. _check_for_redacted_sender.

@H-Shay
Copy link
Contributor Author

H-Shay commented Jun 16, 2025

Build errors are odd, I wonder if they are related to #18559 , as the errors mention base64:

Building editable for matrix-synapse (pyproject.toml) did not run successfully.
    │ exit code: 1
    ╰─> [48 lines of output]
        A setup.py file already exists. Using it.
        running build_ext
        running build_rust
        error: failed to parse lock file at: /home/runner/work/synapse/synapse/Cargo.lock
        
        Caused by:
          package `base64` is specified twice in the lockfile
        error: `cargo metadata --manifest-path rust/Cargo.toml --format-version 1` failed with code 101

Otherwise I am not sure what I did to cause these

@H-Shay H-Shay requested a review from anoadragon453 June 16, 2025 23:23
@erikjohnston
Copy link
Member

Sorry, I broke develop :(

This PR should fix it #18561

Copy link
Member

@turt2live turt2live left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

drive-by SCT review to meet implementation requirements on the MSC. Overall, looks great - thank you!

The major detail would be removal of the event type filters, but that looks trivial enough to consider the MSC implemented regardless.

@H-Shay
Copy link
Contributor Author

H-Shay commented Jun 23, 2025

@anoadragon453 just checking that this is still on your radar

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if this is the right architecture for dealing with such redactions. Pulling out all events sent by the user has a few potential issues: a) if there are a lot of events it might take a lot of time, and b) won't redact events that we haven't backfilled over federation yet. The MSC also seems to suggest that events should get unredacted if the ban is replaced with another state event without a redactions?

An alternate method may be to have a separate table that is room_redactions(room_id, sender), which we then check (as well as redactions table) when fetching the event? The new room_redactions would be updated when we add the ban/kick to the current_state_events table, and removed if it gets replaced?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MSC also seems to suggest that events should get unredacted if the ban is replaced with another state event without a redactions?

They shouldn't get unredacted, but the auto-redaction should cease for newly received events. I'll try to clarify this in the MSC itself.

An example series of events:

  1. Alice sends a message
  2. Bob bans Alice, with redact_events: true
  3. [Servers and clients redact Alice's message in step 1]
  4. Due to propagation delays, Alice's second message arrives after the ban. The server (and client, if it received it) redacts that message too.
  5. Later, Bob unbans Alice, setting redact_events: false
  6. Again due to propagation delays, or because Alice rejoined, Alice's third message arrives. It is not redacted.

The messages in steps 1 and 4 remain redacted even after step 5.

Copy link
Member

@anoadragon453 anoadragon453 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with Erik's comment above.

Otherwise some minor notes on the current code - which is a lot more readable, thank you!

Comment on lines +2088 to +2089
# self-bans currently are not authorized so we don't check for that
# case
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So self-bans would never appear in auth_events, is that correct?

# check for msc4293 redact_events flag and apply if found
if (
not self.hs.config.experimental.msc4293_enabled
or event.type != EventTypes.Member
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sanity check:

Suggested change
or event.type != EventTypes.Member
or event.type != EventTypes.Member
or event.state_key is None

if sender_level < room_redaction_level:
continue

if sender_level > redact_level:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if sender_level > redact_level:
if sender_level >= redact_level:

Comment on lines +433 to +435
value_values = [
(self._clock.time_msec(),) for x in ids_to_redact
]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
value_values = [
(self._clock.time_msec(),) for x in ids_to_redact
]
time_now_ms = self._clock.time_msec()
value_values = [
(time_now_ms,) for x in ids_to_redact
]

rather than calling self._clock.time_msec() repeatedly.

@@ -2768,9 +2850,8 @@ def _store_redaction(self, txn: LoggingTransaction, event: EventBase) -> None:
self.db_pool.simple_upsert_txn(
txn,
table="redactions",
keyvalues={"event_id": event.event_id},
keyvalues={"event_id": event.event_id, "redacts": event.redacts},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is redacts unique? I feel you could have multiple redactions redacting a single event.

@@ -2065,6 +2068,42 @@ async def _check_for_soft_fail(
soft_failed_event_counter.inc()
event.internal_metadata.soft_failed = True

if self._config.experimental.msc4293_enabled:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, that is very handy. My hangup is that the logic we're doing for redactions here doesn't result in any soft fails occurring.

One way we could refactor this is to take all of the auth DAG calculation out of _check_for_soft_fail and put that in a _get_current_auth_events function. Then pass the result of that to _check_for_soft_fail and, i.e. _check_for_redacted_sender.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants