import { strict as assert } from "assert"; import { newTestUser } from "./clientHelper"; import { Mjolnir } from "../../src/Mjolnir"; import { read as configRead } from "../../src/config"; import { getRequestFn, LogService } from "@vector-im/matrix-bot-sdk"; import { createBanList, getFirstReaction } from "./commands/commandUtils"; /** * Get a copy of the rules from the ruleserver. */ async function currentRules(mjolnir: Mjolnir): Promise<{ start: object, stop: object, since: string }> { return await new Promise((resolve, reject) => getRequestFn()({ uri: `http://${mjolnir.config.web.address}:${mjolnir.config.web.port}/api/1/ruleserver/updates/`, method: "GET" }, (error: object, _response: any, body: string) => { if (error) { reject(error) } else { resolve(JSON.parse(body)) } })); } /** * Wait for the rules to change as a result of the thunk. The returned promise will resolve when the rules being served have changed. * @param thunk Should cause the rules the RuleServer is serving to change some way. */ async function waitForRuleChange(mjolnir: Mjolnir, thunk: any): Promise { const initialRules = await currentRules(mjolnir); let rules = initialRules; // We use JSON.stringify like this so that it is pretty printed in the log and human readable. LogService.debug('policyConsumptionTest', `Rules before we wait for them to change: ${JSON.stringify(rules, null, 2)}`); await thunk(); while (rules.since === initialRules.since) { await new Promise(resolve => { setTimeout(resolve, 500); }) rules = await currentRules(mjolnir); }; // The problem is, we have no idea how long a consumer will take to process the changed rules. // We know the pull peroid is 1 second though. await new Promise(resolve => { setTimeout(resolve, 1500); }) LogService.debug('policyConsumptionTest', `Rules after they have changed: ${JSON.stringify(rules, null, 2)}`); } describe("Test: that policy lists are consumed by the associated synapse module", function () { this.afterEach(async function () { if (this.config.web.ruleServer.enabled) { this.timeout(5000) LogService.debug('policyConsumptionTest', `Rules at end of test ${JSON.stringify(await currentRules(this.mjolnir), null, 2)}`); // Clear any state associated with the account. await this.mjolnir.client.setAccountData('org.matrix.mjolnir.watched_lists', { references: [], }); } }) this.beforeAll(async function() { let config = configRead(); if (!config?.web?.ruleServer?.enabled) { LogService.warn("policyConsumptionTest", "Skipping policy consumption test because the ruleServer is not enabled") this.skip(); } }) it('blocks users in antispam when they are banned from sending messages and invites serverwide.', async function() { this.timeout(20000); // Create a few users and a room. let badUser = await newTestUser(this.config.homeserverUrl, { name: { contains: "spammer" }}); let badUserId = await badUser.getUserId(); let mjolnirUserId = await this.mjolnir.client.getUserId(); let moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" }}); this.moderator = moderator; await moderator.joinRoom(this.mjolnir.managementRoomId); let unprotectedRoom = await badUser.createRoom({ invite: [await moderator.getUserId()]}); // We do this so the moderator can send invites, no other reason. await badUser.setUserPowerLevel(await moderator.getUserId(), unprotectedRoom, 100); await moderator.joinRoom(unprotectedRoom); const banList = await createBanList(this.mjolnir.managementRoomId, this.mjolnir.client, moderator); await badUser.sendMessage(unprotectedRoom, {msgtype: 'm.text', body: 'Something bad and mean'}); await waitForRuleChange(this.mjolnir, async () => { await getFirstReaction(this.mjolnir.client, this.mjolnir.managementRoomId, '✅', async () => { return await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text', body: `!mjolnir ban ${banList} ${badUserId}` }); }); }); await assert.rejects(badUser.sendMessage(unprotectedRoom, { msgtype: 'm.text', body: 'test'}), 'The bad user should be banned and unable to send messages.'); await assert.rejects(badUser.inviteUser(mjolnirUserId, unprotectedRoom), 'They should also be unable to send invitations.'); assert.ok(await moderator.inviteUser('@test:localhost:9999', unprotectedRoom), 'The moderator is not banned though so should still be able to invite'); assert.ok(await moderator.sendMessage(unprotectedRoom, { msgtype: 'm.text', body: 'test'}), 'They should be able to send messages still too.'); // Test we can remove the rules. await waitForRuleChange(this.mjolnir, async () => { await getFirstReaction(this.mjolnir.client, this.mjolnir.managementRoomId, '✅', async () => { return await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text', body: `!mjolnir unban ${banList} ${badUserId}` }); }); }); assert.ok(await badUser.sendMessage(unprotectedRoom, { msgtype: 'm.text', body: 'test'})); assert.ok(await badUser.inviteUser(mjolnirUserId, unprotectedRoom)); }) it('Test: Cannot send message to a room that is listed in a policy list and cannot invite a user to the room either', async function () { this.timeout(20000); let badUser = await newTestUser(this.config.homeserverUrl, { name: { contains: "spammer" }}); let moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" }}); await moderator.joinRoom(this.mjolnir.managementRoomId); const banList = await createBanList(this.mjolnir.managementRoomId, this.mjolnir.client, moderator); let badRoom = await badUser.createRoom(); let unrelatedRoom = await badUser.createRoom(); await badUser.sendMessage(badRoom, {msgtype: 'm.text', body: "Very Bad Stuff in this room"}); await waitForRuleChange(this.mjolnir, async () => { await getFirstReaction(this.mjolnir.client, this.mjolnir.managementRoomId, '✅', async () => { return await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text', body: `!mjolnir ban ${banList} ${badRoom}` }); }); }); await assert.rejects(badUser.sendMessage(badRoom, { msgtype: 'm.text', body: 'test'}), 'should not be able to send messagea to a room which is listed.'); await assert.rejects(badUser.inviteUser(await moderator.getUserId(), badRoom), 'should not be able to invite people to a listed room.'); assert.ok(await badUser.sendMessage(unrelatedRoom, { msgtype: 'm.text.', body: 'hey'}), 'should be able to send messages to unrelated room'); assert.ok(await badUser.inviteUser(await moderator.getUserId(), unrelatedRoom), 'They should still be able to invite to other rooms though'); // Test we can remove these rules. await waitForRuleChange(this.mjolnir, async () => { await getFirstReaction(this.mjolnir.client, this.mjolnir.managementRoomId, '✅', async () => { return await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text', body: `!mjolnir unban ${banList} ${badRoom}` }); }); }); assert.ok(await badUser.sendMessage(badRoom, { msgtype: 'm.text', body: 'test'}), 'should now be able to send messages to the room.'); assert.ok(await badUser.inviteUser(await moderator.getUserId(), badRoom), 'should now be able to send messages to the room.'); }) it('Test: When a list becomes unwatched, the associated policies are stopped.', async function () { this.timeout(20000); let moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" }}); await moderator.joinRoom(this.mjolnir.managementRoomId); const banList = await createBanList(this.mjolnir.managementRoomId, this.mjolnir.client, moderator); let targetRoom = await moderator.createRoom(); await moderator.sendMessage(targetRoom, {msgtype: 'm.text', body: "Fluffy Foxes."}); await waitForRuleChange(this.mjolnir, async () => { await getFirstReaction(this.mjolnir.client, this.mjolnir.managementRoomId, '✅', async () => { return await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text', body: `!mjolnir ban ${banList} ${targetRoom}` }); }); }); await assert.rejects(moderator.sendMessage(targetRoom, { msgtype: 'm.text', body: 'test'}), 'should not be able to send messages to a room which is listed.'); await waitForRuleChange(this.mjolnir, async () => { await getFirstReaction(this.mjolnir.client, this.mjolnir.managementRoomId, '✅', async () => { return await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text', body: `!mjolnir unwatch #${banList}:localhost:9999` }); }); }); assert.ok(await moderator.sendMessage(targetRoom, { msgtype: 'm.text', body: 'test'}), 'should now be able to send messages to the room.'); }) });