Refuse to operate on encrypted rooms

- Refuse to link to encrypted rooms
- Do not show encrypted rooms as link candidates (if server supports)
- Reject invites to encrypted rooms with message
- Unbridge and leave room if it becomes encrypted
This commit is contained in:
Cadence Ember
2026-03-17 12:35:42 +13:00
parent d3afa728ed
commit f1b111a8a4
8 changed files with 92 additions and 12 deletions
+5
View File
@@ -249,6 +249,11 @@ block body
ul.my8.ml24
each row in removedLinkedRooms
li: a(href=`https://matrix.to/#/${row.room_id}`)= row.name
h3.mt24 Unavailable rooms: Encryption not supported
.s-card.p0
ul.my8.ml24
each row in removedEncryptedRooms
li: a(href=`https://matrix.to/#/${row.room_id}`)= row.name
h3.mt24 Unavailable rooms: Wrong type
.s-card.p0
ul.my8.ml24
+2 -1
View File
@@ -123,13 +123,14 @@ function getChannelRoomsLinks(guild, rooms, roles) {
let unlinkedRooms = [...rooms]
let removedLinkedRooms = dUtils.filterTo(unlinkedRooms, r => !linkedRoomIDs.includes(r.room_id))
let removedWrongTypeRooms = dUtils.filterTo(unlinkedRooms, r => !r.room_type)
let removedEncryptedRooms = dUtils.filterTo(unlinkedRooms, r => !r.encryption && !r["im.nheko.summary.encryption"])
// https://discord.com/developers/docs/topics/threads#active-archived-threads
// need to filter out linked archived threads from unlinkedRooms, will just do that by comparing against the name
let removedArchivedThreadRooms = dUtils.filterTo(unlinkedRooms, r => r.name && !r.name.match(/^\[(🔒)?⛓️\]/))
return {
linkedChannelsWithDetails, unlinkedChannels, unlinkedRooms,
removedUncachedChannels, removedWrongTypeChannels, removedPrivateChannels, removedLinkedRooms, removedWrongTypeRooms, removedArchivedThreadRooms
removedUncachedChannels, removedWrongTypeChannels, removedPrivateChannels, removedLinkedRooms, removedWrongTypeRooms, removedArchivedThreadRooms, removedEncryptedRooms
}
}
+6
View File
@@ -204,6 +204,12 @@ as.router.post("/api/link", defineEventHandler(async event => {
throw createError({status: 403, message: e.errcode, data: `${e.errcode} - ${e.message}`})
}
// Check room is not encrypted
const encryption = await api.getStateEvent(parsedBody.matrix, "m.room.encryption", "").catch(() => null)
if (encryption) {
throw createError({status: 400, message: "Bad Request", data: "Encrypted rooms are not supported for bridging. Please replace it with an unencrypted room."})
}
// Check bridge has PL 100
const {powerLevels, powers: {[utils.bot]: selfPowerLevel}} = await utils.getEffectivePower(parsedBody.matrix, [utils.bot], api)
if (selfPowerLevel < (powerLevels?.state_default ?? 50) || selfPowerLevel < 100) throw createError({status: 400, message: "Bad Request", data: "OOYE needs power level 100 (admin) in the target Matrix room"})
+46 -4
View File
@@ -435,6 +435,47 @@ test("web link room: check that bridge can join room (uses via for join attempt)
t.equal(called, 2)
})
test("web link room: check that room is not encrypted", async t => {
let called = 0
const [error] = await tryToCatch(() => router.test("post", "/api/link", {
sessionData: {
managedGuilds: ["665289423482519565"]
},
body: {
discord: "665310973967597573",
matrix: "!NDbIqNpJyPvfKRnNcr:cadence.moe",
guild_id: "665289423482519565"
},
api: {
async joinRoom(roomID) {
called++
return roomID
},
async *generateFullHierarchy(spaceID) {
called++
t.equal(spaceID, "!zTMspHVUBhFLLSdmnS:cadence.moe")
yield {
room_id: "!NDbIqNpJyPvfKRnNcr:cadence.moe",
children_state: [],
guest_can_join: false,
num_joined_members: 2
}
/* c8 ignore next */
},
async getStateEvent(roomID, type, key) {
called++
t.equal(roomID, "!NDbIqNpJyPvfKRnNcr:cadence.moe")
if (type === "m.room.encryption" && key === "") {
return {algorithm: "m.megolm.v1.aes-sha2"}
}
throw new Error("Unknown state event")
}
}
}))
t.equal(error.data, "Encrypted rooms are not supported for bridging. Please replace it with an unencrypted room.")
t.equal(called, 3)
})
test("web link room: check that bridge has PL 100 in target room", async t => {
let called = 0
const [error] = await tryToCatch(() => router.test("post", "/api/link", {
@@ -465,9 +506,10 @@ test("web link room: check that bridge has PL 100 in target room", async t => {
async getStateEvent(roomID, type, key) {
called++
t.equal(roomID, "!NDbIqNpJyPvfKRnNcr:cadence.moe")
t.equal(type, "m.room.power_levels")
t.equal(key, "")
return {users_default: 50}
if (type === "m.room.power_levels" && key === "") {
return {users_default: 50}
}
throw new Error("Unknown state event")
},
async getStateEventOuter(roomID, type, key) {
called++
@@ -489,7 +531,7 @@ test("web link room: check that bridge has PL 100 in target room", async t => {
}
}))
t.equal(error.data, "OOYE needs power level 100 (admin) in the target Matrix room")
t.equal(called, 4)
t.equal(called, 5)
})
test("web link room: successfully calls createRoom", async t => {