2022-07-26 11:39:19 +01:00
|
|
|
package syncv3_test
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
2023-01-20 18:48:10 +00:00
|
|
|
"fmt"
|
2022-07-26 11:39:19 +01:00
|
|
|
"testing"
|
|
|
|
|
2023-10-11 12:23:46 +01:00
|
|
|
"github.com/matrix-org/complement/b"
|
|
|
|
"github.com/matrix-org/complement/client"
|
2022-12-15 11:08:50 +00:00
|
|
|
"github.com/matrix-org/sliding-sync/sync3"
|
|
|
|
"github.com/matrix-org/sliding-sync/testutils/m"
|
2022-07-26 11:39:19 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
// The purpose of these tests is to ensure that events for one user cannot leak to another user.
|
|
|
|
|
|
|
|
// Test that events do not leak to users who have left a room.
|
|
|
|
// Rationale: When a user leaves a room they should not receive events in that room anymore. However,
|
|
|
|
// the v3 server may still be receiving events in that room from other joined members. We need to
|
|
|
|
// make sure these events don't find their way to the client.
|
|
|
|
// Attack vector:
|
2022-12-14 18:53:55 +00:00
|
|
|
// - Alice is using the sync server and is in room !A.
|
|
|
|
// - Eve joins the room !A.
|
|
|
|
// - Alice kicks Eve.
|
|
|
|
// - Alice sends event $X in !A.
|
|
|
|
// - Ensure Eve does not see event $X.
|
2022-07-26 11:39:19 +01:00
|
|
|
func TestSecurityLiveStreamEventLeftLeak(t *testing.T) {
|
|
|
|
alice := registerNewUser(t)
|
|
|
|
eve := registerNewUser(t)
|
|
|
|
|
|
|
|
// Alice and Eve in the room
|
2023-10-11 12:23:46 +01:00
|
|
|
roomID := alice.MustCreateRoom(t, map[string]interface{}{
|
2022-07-26 11:39:19 +01:00
|
|
|
"preset": "public_chat",
|
|
|
|
})
|
|
|
|
eve.JoinRoom(t, roomID, nil)
|
|
|
|
|
|
|
|
// start sync streams for Alice and Eve
|
|
|
|
aliceRes := alice.SlidingSync(t, sync3.Request{
|
2022-12-20 13:32:39 +00:00
|
|
|
Lists: map[string]sync3.RequestList{
|
|
|
|
"a": {
|
|
|
|
Ranges: sync3.SliceRanges{
|
|
|
|
[2]int64{0, 10}, // doesn't matter
|
|
|
|
},
|
2023-01-23 13:25:30 +00:00
|
|
|
RoomSubscription: sync3.RoomSubscription{
|
|
|
|
RequiredState: [][2]string{
|
|
|
|
{"m.room.name", ""},
|
|
|
|
},
|
|
|
|
TimelineLimit: 2,
|
|
|
|
},
|
2022-12-20 13:32:39 +00:00
|
|
|
}},
|
2022-07-26 11:39:19 +01:00
|
|
|
})
|
2022-12-20 13:32:39 +00:00
|
|
|
m.MatchResponse(t, aliceRes, m.MatchList("a", m.MatchV3Count(1), m.MatchV3Ops(
|
2023-04-05 01:07:07 +01:00
|
|
|
m.MatchV3SyncOp(0, 0, []string{roomID}),
|
2022-07-26 11:39:19 +01:00
|
|
|
)))
|
|
|
|
eveRes := eve.SlidingSync(t, sync3.Request{
|
2022-12-20 13:32:39 +00:00
|
|
|
Lists: map[string]sync3.RequestList{
|
|
|
|
"a": {
|
|
|
|
Ranges: sync3.SliceRanges{
|
|
|
|
[2]int64{0, 10}, // doesn't matter
|
|
|
|
},
|
|
|
|
}},
|
2022-07-26 11:39:19 +01:00
|
|
|
})
|
2022-12-20 13:32:39 +00:00
|
|
|
m.MatchResponse(t, eveRes, m.MatchList("a", m.MatchV3Count(1), m.MatchV3Ops(
|
2023-04-05 01:07:07 +01:00
|
|
|
m.MatchV3SyncOp(0, 0, []string{roomID}),
|
2022-07-26 11:39:19 +01:00
|
|
|
)))
|
|
|
|
|
|
|
|
// kick Eve
|
2023-10-11 12:23:46 +01:00
|
|
|
alice.MustDo(t, "POST", []string{"_matrix", "client", "v3", "rooms", roomID, "kick"}, client.WithJSONBody(t, map[string]interface{}{
|
2022-07-26 11:39:19 +01:00
|
|
|
"user_id": eve.UserID,
|
|
|
|
}))
|
|
|
|
|
|
|
|
// send message as Alice, note it shouldn't go down Eve's v2 stream
|
2023-10-11 12:23:46 +01:00
|
|
|
sensitiveEventID := alice.SendEventSynced(t, roomID, b.Event{
|
2022-07-26 11:39:19 +01:00
|
|
|
Type: "m.room.name",
|
|
|
|
StateKey: ptr(""),
|
|
|
|
Content: map[string]interface{}{
|
|
|
|
"name": "I hate Eve",
|
|
|
|
},
|
|
|
|
})
|
|
|
|
|
2023-01-20 18:48:10 +00:00
|
|
|
// Ensure Alice sees both events, wait till she gets them
|
|
|
|
var timeline []json.RawMessage
|
|
|
|
aliceRes = alice.SlidingSyncUntil(t, aliceRes.Pos, sync3.Request{
|
2022-12-20 13:32:39 +00:00
|
|
|
Lists: map[string]sync3.RequestList{
|
|
|
|
"a": {
|
|
|
|
Ranges: sync3.SliceRanges{
|
|
|
|
[2]int64{0, 10}, // doesn't matter
|
2022-07-26 11:39:19 +01:00
|
|
|
},
|
2022-12-20 13:32:39 +00:00
|
|
|
RoomSubscription: sync3.RoomSubscription{
|
|
|
|
RequiredState: [][2]string{
|
|
|
|
{"m.room.name", ""},
|
|
|
|
},
|
2023-01-23 13:25:30 +00:00
|
|
|
TimelineLimit: 2,
|
2022-12-20 13:32:39 +00:00
|
|
|
},
|
|
|
|
}},
|
2023-01-20 18:48:10 +00:00
|
|
|
}, func(r *sync3.Response) error {
|
2023-05-17 10:11:54 +01:00
|
|
|
// keep syncing until we see 2 events in the timeline
|
2023-01-20 18:48:10 +00:00
|
|
|
timeline = append(timeline, r.Rooms[roomID].Timeline...)
|
|
|
|
if len(timeline) != 2 {
|
|
|
|
return fmt.Errorf("waiting for more messages, got %v", len(timeline))
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
2022-07-26 11:39:19 +01:00
|
|
|
|
2023-05-17 10:11:54 +01:00
|
|
|
// check Alice sees both events
|
2023-07-31 17:53:15 +01:00
|
|
|
kickEvent := Event{
|
|
|
|
Type: "m.room.member",
|
|
|
|
StateKey: ptr(eve.UserID),
|
|
|
|
Content: map[string]interface{}{
|
|
|
|
"membership": "leave",
|
2022-07-26 11:39:19 +01:00
|
|
|
},
|
2023-07-31 17:53:15 +01:00
|
|
|
Sender: alice.UserID,
|
|
|
|
}
|
|
|
|
assertEventsEqual(t, []Event{
|
|
|
|
kickEvent,
|
2022-07-26 11:39:19 +01:00
|
|
|
{
|
|
|
|
Type: "m.room.name",
|
|
|
|
StateKey: ptr(""),
|
|
|
|
Content: map[string]interface{}{
|
|
|
|
"name": "I hate Eve",
|
|
|
|
},
|
|
|
|
Sender: alice.UserID,
|
|
|
|
ID: sensitiveEventID,
|
|
|
|
},
|
2023-05-17 10:11:54 +01:00
|
|
|
}, timeline)
|
2022-07-26 11:39:19 +01:00
|
|
|
|
|
|
|
// Ensure Eve doesn't see this message in the timeline, name calc or required_state
|
|
|
|
eveRes = eve.SlidingSync(t, sync3.Request{
|
2022-12-20 13:32:39 +00:00
|
|
|
Lists: map[string]sync3.RequestList{
|
|
|
|
"a": {
|
|
|
|
Ranges: sync3.SliceRanges{
|
|
|
|
[2]int64{0, 10}, // doesn't matter
|
2022-07-26 11:39:19 +01:00
|
|
|
},
|
2023-01-23 13:25:30 +00:00
|
|
|
// Note we are _adding_ this to the list which will kick in logic to return required state / timeline limits
|
|
|
|
// so we need to make sure that this returns no data.
|
2022-12-20 13:32:39 +00:00
|
|
|
RoomSubscription: sync3.RoomSubscription{
|
|
|
|
RequiredState: [][2]string{
|
|
|
|
{"m.room.name", ""},
|
|
|
|
},
|
2023-01-23 13:25:30 +00:00
|
|
|
TimelineLimit: 2,
|
2022-12-20 13:32:39 +00:00
|
|
|
},
|
|
|
|
}},
|
2022-07-26 17:54:58 +01:00
|
|
|
}, WithPos(eveRes.Pos))
|
2022-07-26 11:39:19 +01:00
|
|
|
// the room is deleted from eve's point of view and she sees up to and including her kick event
|
2022-12-20 13:32:39 +00:00
|
|
|
m.MatchResponse(t, eveRes, m.MatchList("a", m.MatchV3Count(0), m.MatchV3Ops(m.MatchV3DeleteOp(0))), m.MatchRoomSubscription(
|
2023-07-31 17:53:15 +01:00
|
|
|
roomID, m.MatchRoomName(""), m.MatchRoomRequiredState(nil), MatchRoomTimelineMostRecent(1, []Event{kickEvent}),
|
2022-07-26 11:39:19 +01:00
|
|
|
))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test that events do not leak via direct room subscriptions.
|
|
|
|
// Rationale: Unlike sync v2, in v3 clients can subscribe to any room ID they want as a room_subscription.
|
|
|
|
// We need to make sure that the user is allowed to see events in that room before delivering those events.
|
|
|
|
// Attack vector:
|
2022-12-14 18:53:55 +00:00
|
|
|
// - Alice is using the sync server and is in room !A.
|
|
|
|
// - Eve works out the room ID !A (this isn't sensitive information).
|
|
|
|
// - Eve starts using the sync server and makes a room_subscription for !A.
|
|
|
|
// - Ensure that Eve does not see any events in !A.
|
2022-07-26 11:39:19 +01:00
|
|
|
func TestSecurityRoomSubscriptionLeak(t *testing.T) {
|
|
|
|
alice := registerNewUser(t)
|
|
|
|
eve := registerNewUser(t)
|
|
|
|
|
|
|
|
// Alice in the room
|
2023-10-11 12:23:46 +01:00
|
|
|
alicePrivateRoomID := alice.MustCreateRoom(t, map[string]interface{}{
|
2022-07-26 11:39:19 +01:00
|
|
|
"preset": "private_chat",
|
|
|
|
})
|
|
|
|
|
|
|
|
// Eve is in an unrelated room
|
2023-10-11 12:23:46 +01:00
|
|
|
eveUnrelatedRoomID := eve.MustCreateRoom(t, map[string]interface{}{
|
2022-07-26 11:39:19 +01:00
|
|
|
"preset": "private_chat",
|
|
|
|
})
|
|
|
|
|
2022-07-29 16:22:26 +01:00
|
|
|
// seed the proxy with alice's data
|
|
|
|
alice.SlidingSync(t, sync3.Request{})
|
|
|
|
|
2022-07-26 11:39:19 +01:00
|
|
|
// start sync streams for Eve, with a room subscription to alice's private room
|
|
|
|
eveRes := eve.SlidingSync(t, sync3.Request{
|
2022-12-20 13:32:39 +00:00
|
|
|
Lists: map[string]sync3.RequestList{
|
|
|
|
"a": {
|
|
|
|
Ranges: sync3.SliceRanges{
|
|
|
|
[2]int64{0, 10}, // doesn't matter
|
|
|
|
},
|
|
|
|
}},
|
2022-07-26 11:39:19 +01:00
|
|
|
RoomSubscriptions: map[string]sync3.RoomSubscription{
|
|
|
|
alicePrivateRoomID: {
|
|
|
|
TimelineLimit: 5,
|
|
|
|
RequiredState: [][2]string{
|
|
|
|
{"m.room.join_rules", ""},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
// Assert that Eve doesn't see anything
|
2022-12-20 13:32:39 +00:00
|
|
|
m.MatchResponse(t, eveRes, m.MatchList("a", m.MatchV3Count(1), m.MatchV3Ops(
|
2023-04-05 01:07:07 +01:00
|
|
|
m.MatchV3SyncOp(0, 0, []string{eveUnrelatedRoomID}),
|
2022-07-26 11:39:19 +01:00
|
|
|
)), m.MatchRoomSubscriptionsStrict(map[string][]m.RoomMatcher{
|
|
|
|
eveUnrelatedRoomID: {},
|
|
|
|
}))
|
|
|
|
|
|
|
|
// Assert that live updates still don't feed through to Eve
|
2023-10-11 12:23:46 +01:00
|
|
|
alice.SendEventSynced(t, alicePrivateRoomID, b.Event{
|
2022-07-26 11:39:19 +01:00
|
|
|
Type: "m.room.name",
|
|
|
|
StateKey: ptr(""),
|
|
|
|
Content: map[string]interface{}{
|
|
|
|
"name": "I hate Eve",
|
|
|
|
},
|
|
|
|
})
|
|
|
|
eveRes = eve.SlidingSync(t, sync3.Request{
|
2022-12-20 13:32:39 +00:00
|
|
|
Lists: map[string]sync3.RequestList{
|
|
|
|
"a": {
|
|
|
|
Ranges: sync3.SliceRanges{
|
|
|
|
[2]int64{0, 10}, // doesn't matter
|
|
|
|
},
|
|
|
|
}},
|
2022-07-26 17:54:58 +01:00
|
|
|
}, WithPos(eveRes.Pos))
|
2022-07-26 11:39:19 +01:00
|
|
|
|
|
|
|
// Assert that Eve doesn't see anything
|
2022-12-20 13:32:39 +00:00
|
|
|
m.MatchResponse(t, eveRes, m.MatchList("a", m.MatchV3Count(1)), m.MatchNoV3Ops(), m.MatchRoomSubscriptionsStrict(map[string][]m.RoomMatcher{}))
|
2022-07-26 11:39:19 +01:00
|
|
|
}
|
2022-07-29 16:22:26 +01:00
|
|
|
|
|
|
|
// Test that events do not leak via direct space subscriptions.
|
|
|
|
// Rationale: Unlike sync v2, in v3 clients can subscribe to any room ID they want as a space.
|
|
|
|
// We need to make sure that the user is allowed to see events in that room before delivering those events.
|
|
|
|
// Attack vector:
|
2022-12-14 18:53:55 +00:00
|
|
|
// - Alice is using the sync server and is in space !A with room !B.
|
|
|
|
// - Eve works out the room ID !A (this isn't sensitive information).
|
|
|
|
// - Eve starts using the sync server and makes a request for !A as the space filter.
|
|
|
|
// - Ensure that Eve does not see any events in !A or !B.
|
2022-07-29 16:22:26 +01:00
|
|
|
func TestSecuritySpaceDataLeak(t *testing.T) {
|
|
|
|
alice := registerNewUser(t)
|
|
|
|
eve := registerNewUser(t)
|
|
|
|
|
2023-10-11 12:23:46 +01:00
|
|
|
roomA := alice.MustCreateRoom(t, map[string]interface{}{
|
2022-07-29 16:22:26 +01:00
|
|
|
"preset": "public_chat",
|
|
|
|
"creation_content": map[string]string{
|
|
|
|
"type": "m.space",
|
|
|
|
},
|
|
|
|
})
|
2023-10-11 12:23:46 +01:00
|
|
|
roomB := alice.MustCreateRoom(t, map[string]interface{}{
|
2022-07-29 16:22:26 +01:00
|
|
|
"preset": "private_chat",
|
|
|
|
})
|
2023-10-11 12:23:46 +01:00
|
|
|
alice.SendEventSynced(t, roomA, b.Event{
|
2022-07-29 16:22:26 +01:00
|
|
|
Type: "m.space.child",
|
|
|
|
StateKey: &roomB,
|
|
|
|
Content: map[string]interface{}{
|
|
|
|
"via": []string{"example.com"},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
// seed the proxy with alice's data
|
|
|
|
alice.SlidingSync(t, sync3.Request{})
|
|
|
|
|
|
|
|
// ensure eve sees nothing
|
|
|
|
res := eve.SlidingSync(t, sync3.Request{
|
2022-12-20 13:32:39 +00:00
|
|
|
Lists: map[string]sync3.RequestList{
|
|
|
|
"a": {
|
2022-07-29 16:22:26 +01:00
|
|
|
Ranges: [][2]int64{{0, 20}},
|
|
|
|
Filters: &sync3.RequestFilters{
|
|
|
|
Spaces: []string{roomA},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
m.MatchResponse(t, res, m.MatchNoV3Ops(), m.MatchRoomSubscriptionsStrict(nil))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test that knowledge of a room being in a hidden space does not leak via direct space subscriptions.
|
|
|
|
// Rationale: Unlike sync v2, in v3 clients can subscribe to any room ID they want as a space.
|
|
|
|
// We need to make sure that if a user is in a room in multiple spaces (only 1 of them the user is joined to)
|
|
|
|
// then they cannot see the room if they apply a filter for a parent space they are not joined to.
|
|
|
|
// Attack vector:
|
2022-12-14 18:53:55 +00:00
|
|
|
// - Alice is using the sync server and is in space !A with room !B.
|
|
|
|
// - Eve is using the sync server and is in space !C with room !B as well.
|
|
|
|
// - Eve works out the room ID !A (this isn't sensitive information).
|
|
|
|
// - Eve starts using the sync server and makes a request for !A as the space filter.
|
|
|
|
// - Ensure that Eve does not see anything, even though they are joined to !B and the proxy knows it.
|
2022-07-29 16:22:26 +01:00
|
|
|
func TestSecuritySpaceMetadataLeak(t *testing.T) {
|
|
|
|
alice := registerNewUser(t)
|
|
|
|
eve := registerNewUser(t)
|
|
|
|
|
2023-10-11 12:23:46 +01:00
|
|
|
roomA := alice.MustCreateRoom(t, map[string]interface{}{
|
2022-07-29 16:22:26 +01:00
|
|
|
"preset": "public_chat",
|
|
|
|
"creation_content": map[string]string{
|
|
|
|
"type": "m.space",
|
|
|
|
},
|
|
|
|
})
|
2023-10-11 12:23:46 +01:00
|
|
|
roomB := alice.MustCreateRoom(t, map[string]interface{}{
|
2022-07-29 16:22:26 +01:00
|
|
|
"preset": "public_chat",
|
|
|
|
})
|
|
|
|
// Alice has a space A -> B
|
2023-10-11 12:23:46 +01:00
|
|
|
alice.SendEventSynced(t, roomA, b.Event{
|
2022-07-29 16:22:26 +01:00
|
|
|
Type: "m.space.child",
|
|
|
|
StateKey: &roomB,
|
|
|
|
Content: map[string]interface{}{
|
|
|
|
"via": []string{"example.com"},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
// seed the proxy with alice's data
|
|
|
|
alice.SlidingSync(t, sync3.Request{})
|
|
|
|
|
|
|
|
// now Eve also has a space... C -> B
|
2023-10-11 12:23:46 +01:00
|
|
|
roomC := eve.MustCreateRoom(t, map[string]interface{}{
|
2022-07-29 16:22:26 +01:00
|
|
|
"preset": "public_chat",
|
|
|
|
"creation_content": map[string]string{
|
|
|
|
"type": "m.space",
|
|
|
|
},
|
|
|
|
})
|
|
|
|
eve.JoinRoom(t, roomB, nil)
|
2023-10-11 12:23:46 +01:00
|
|
|
eve.SendEventSynced(t, roomC, b.Event{
|
2022-07-29 16:22:26 +01:00
|
|
|
Type: "m.space.child",
|
|
|
|
StateKey: &roomB,
|
|
|
|
Content: map[string]interface{}{
|
|
|
|
"via": []string{"example.com"},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
|
|
|
|
// ensure eve sees nothing
|
|
|
|
res := eve.SlidingSync(t, sync3.Request{
|
2022-12-20 13:32:39 +00:00
|
|
|
Lists: map[string]sync3.RequestList{
|
|
|
|
"a": {
|
2022-07-29 16:22:26 +01:00
|
|
|
Ranges: [][2]int64{{0, 20}},
|
|
|
|
Filters: &sync3.RequestFilters{
|
|
|
|
Spaces: []string{roomA},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
m.MatchResponse(t, res, m.MatchNoV3Ops(), m.MatchRoomSubscriptionsStrict(nil))
|
|
|
|
}
|
2024-01-12 12:15:38 +00:00
|
|
|
|
|
|
|
// Test that adding a child room to a space does not leak global room metadata about that
|
|
|
|
// child room to users in the parent space. This information isn't strictly confidential as
|
|
|
|
// the /rooms/{roomId}/hierarchy endpoint will include such metadata (room name, avatar, join count, etc)
|
|
|
|
// because the user is part of the parent space. There isn't an attack vector here, but repro steps:
|
|
|
|
// - Alice and Bob are in a parent space.
|
|
|
|
// - Bob has a poller on SS running.
|
|
|
|
// - Alice is live streaming from SS.
|
|
|
|
// - Bob creates a child room in that space, and sends both the m.space.parent in the child room AND
|
|
|
|
// the m.space.child in the parent space.
|
|
|
|
// - Ensure that no information about the child room comes down Alice's connection.
|
|
|
|
func TestSecuritySpaceChildMetadataLeakFromParent(t *testing.T) {
|
|
|
|
alice := registerNewUser(t)
|
|
|
|
bob := registerNewUser(t)
|
|
|
|
parentName := "The Parent Room Name"
|
|
|
|
childName := "The Child Room Name"
|
|
|
|
|
|
|
|
// Alice and Bob are in a parent space.
|
|
|
|
parentSpace := bob.MustCreateRoom(t, map[string]interface{}{
|
|
|
|
"preset": "public_chat",
|
|
|
|
"name": parentName,
|
|
|
|
"creation_content": map[string]string{
|
|
|
|
"type": "m.space",
|
|
|
|
},
|
|
|
|
})
|
|
|
|
alice.MustJoinRoom(t, parentSpace, []string{"hs1"})
|
|
|
|
|
|
|
|
// Bob has a poller on SS running.
|
|
|
|
bobRes := bob.SlidingSync(t, sync3.Request{})
|
|
|
|
|
|
|
|
// Alice is live streaming from SS.
|
|
|
|
aliceRes := alice.SlidingSync(t, sync3.Request{
|
|
|
|
Lists: map[string]sync3.RequestList{
|
|
|
|
"a": {
|
|
|
|
Ranges: [][2]int64{{0, 20}},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
m.MatchResponse(t, aliceRes, m.MatchRoomSubscriptionsStrict(map[string][]m.RoomMatcher{
|
|
|
|
parentSpace: {
|
|
|
|
m.MatchJoinCount(2),
|
|
|
|
m.MatchRoomName(parentName),
|
|
|
|
},
|
|
|
|
}))
|
|
|
|
|
|
|
|
// Bob creates a child room in that space, and sends both the m.space.parent in the child room AND
|
|
|
|
// the m.space.child in the parent space.
|
|
|
|
childRoom := bob.MustCreateRoom(t, map[string]interface{}{
|
|
|
|
"preset": "public_chat",
|
|
|
|
"name": childName,
|
|
|
|
"initial_state": []map[string]interface{}{
|
|
|
|
{
|
|
|
|
"type": "m.space.parent",
|
|
|
|
"state_key": parentSpace,
|
|
|
|
"content": map[string]interface{}{
|
|
|
|
"canonical": true,
|
|
|
|
"via": []string{"hs1"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
chlidEventID := bob.SendEventSynced(t, parentSpace, b.Event{
|
|
|
|
Type: "m.space.child",
|
|
|
|
StateKey: ptr(childRoom),
|
|
|
|
Content: map[string]interface{}{
|
|
|
|
"via": []string{"hs1"},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
// wait for SS to process it
|
|
|
|
bob.SlidingSyncUntilEventID(t, bobRes.Pos, parentSpace, chlidEventID)
|
|
|
|
|
|
|
|
// Ensure that no information about the child room comes down Alice's connection.
|
|
|
|
aliceRes = alice.SlidingSync(t, sync3.Request{}, WithPos(aliceRes.Pos))
|
|
|
|
m.MatchResponse(t, aliceRes, m.MatchRoomSubscriptionsStrict(map[string][]m.RoomMatcher{
|
|
|
|
parentSpace: {
|
|
|
|
MatchRoomTimeline([]Event{{
|
|
|
|
Type: "m.space.child",
|
|
|
|
StateKey: ptr(childRoom),
|
|
|
|
Content: map[string]interface{}{
|
|
|
|
"via": []interface{}{"hs1"},
|
|
|
|
},
|
|
|
|
}}),
|
|
|
|
},
|
|
|
|
}))
|
|
|
|
}
|