Repository: matrix-org/synapse

Issue:
Extra fields in space hierarchy listing response
### Description

Synapse includes at least two fields in the `GET …/hierarchy` space listing response that it probably shouldn't:

* `join_rules` which seems to be duplicating `join_rule`
* `allowed_room_ids`, for restricted rooms

I originally reported this in the spec since I assumed `allowed_room_ids` was actually meant to be received by clients: https://github.com/matrix-org/matrix-spec/issues/984#issuecomment-1059750228

### Steps to reproduce

- Open Element dev Console (Ctrl+Shift+I), Network tab
- List rooms of a space with restricted rooms
- Observe a `GET …/hierarchy` request and its response

### Version information

- **Homeserver**: flipdot.org

If not matrix.org:

- **Version**: 1.53.0
- **Install method**: 🤷🏼
- **Platform**: 🤷🏼


Discussion:
Noting that this seems to be about the *Client-Server* `/hierarchy` API (though maybe it's also worth checking the Server-Server one).
> though maybe it's also worth checking the Server-Server one

These fields are used by the Server-Server API to pass additional information. They should not be passed down via the Client-Server API.


I'm unable to reproduce this -- can you provide (privately, if needed) an example API call / response?
We debugged this a little bit and it seems to only happen for restricted rooms that the user is not in.
I was able to reproduce this in a unit test, it seemed we did the proper thing for the `/spaces` API (which was now removed in #12073), but the `/hierarchy` API has always returned this *if the result was retrieved over federation*.

Resolving patch:
diff --git a/synapse/handlers/room_summary.py b/synapse/handlers/room_summary.py
--- a/synapse/handlers/room_summary.py
+++ b/synapse/handlers/room_summary.py
@@ -295,7 +295,7 @@ async def _get_room_hierarchy(
             # inaccessible to the requesting user.
             if room_entry:
                 # Add the room (including the stripped m.space.child events).
-                rooms_result.append(room_entry.as_json())
+                rooms_result.append(room_entry.as_json(for_client=True))
 
                 # If this room is not at the max-depth, check if there are any
                 # children to process.
@@ -843,14 +843,25 @@ class _RoomEntry:
     # This may not include all children.
     children_state_events: Sequence[JsonDict] = ()
 
-    def as_json(self) -> JsonDict:
+    def as_json(self, for_client: bool = False) -> JsonDict:
         """
         Returns a JSON dictionary suitable for the room hierarchy endpoint.
 
         It returns the room summary including the stripped m.space.child events
         as a sub-key.
+
+        Args:
+            for_client: If true, any server-server only fields are stripped from
+                the result.
+
         """
         result = dict(self.room)
+
+        # Before returning to the client, remove the allowed_room_ids key, if it
+        # exists.
+        if for_client:
+            result.pop("allowed_room_ids", False)
+
         result["children_state"] = self.children_state_events
         return result
 


Test patch:
diff --git a/tests/handlers/test_room_summary.py b/tests/handlers/test_room_summary.py
--- a/tests/handlers/test_room_summary.py
+++ b/tests/handlers/test_room_summary.py
@@ -172,6 +172,9 @@ def _assert_hierarchy(
         result_room_ids = []
         result_children_ids = []
         for result_room in result["rooms"]:
+            # Ensure federation results are not leaking over the client-server API.
+            self.assertNotIn("allowed_room_ids", result_room)
+
             result_room_ids.append(result_room["room_id"])
             result_children_ids.append(
                 [