Added unconfigurable wordlist protection

This commit is contained in:
Emi Simpson 2020-06-21 13:56:11 -04:00
parent 84c4ba830c
commit bccb18225b
No known key found for this signature in database
GPG Key ID: 68FAB2E2E6DFC98B
2 changed files with 94 additions and 0 deletions

View File

@ -0,0 +1,88 @@
/*
Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { IProtection } from "./IProtection";
import { Mjolnir } from "../Mjolnir";
import { LogLevel, LogService } from "matrix-bot-sdk";
import { logMessage } from "../LogProxy";
import config from "../config";
import { isTrueJoinEvent } from "../utils";
export class WordList implements IProtection {
private justJoined: { [roomId: string]: { [username: string]: Date} } = {};
private badWords: RegExp = new RegExp(/.*(poopyhead).*/i)
constructor() {
}
public get name(): string {
return 'WordList';
}
public async handleEvent(mjolnir: Mjolnir, roomId: string, event: any): Promise<any> {
if (!this.justJoined[roomId]) this.justJoined[roomId] = {};
const content = event['content'] || {};
// When a new member logs in, store the time they joined. This will be useful
// when we need to check if a message was sent within 20 minutes of joining
if (event['type'] === 'm.room.member') {
if (isTrueJoinEvent(event)) {
const now = new Date();
this.justJoined[roomId][event['state_key']] = now;
LogService.info("WordList", `${event['state_key']} joined ${roomId} at ${now.toDateString()}`);
} else if (content['membership'] == 'leave' || content['membership'] == 'ban') {
delete this.justJoined[roomId][event['sender']]
}
return; // stop processing (membership event spam is another problem)
}
if (event['type'] === 'm.room.message') {
const message = content['formatted_body'] || content['body'] || null;
const joinTime = this.justJoined[roomId][event['sender']]
if (joinTime) { // Disregard if the user isn't recently joined
// Check if they did join recently, was it within 20 minutes
const now = new Date();
if (now.valueOf() - joinTime.valueOf() > 20 * 60 * 1000) {
delete this.justJoined[roomId][event['sender']] // Remove the user
LogService.info("WordList", `${event['sender']} is no longer considered suspect`);
return
}
// Perform the test
if (message && this.badWords.test(message)) {
await logMessage(LogLevel.WARN, "WordList", `Banning ${event['sender']} for word list violation in ${roomId}.`);
if (!config.noop) {
await mjolnir.client.banUser(event['sender'], roomId, "Word list violation");
} else {
await logMessage(LogLevel.WARN, "WordList", `Tried to ban ${event['sender']} in ${roomId} but Mjolnir is running in no-op mode`, roomId);
}
// Redact the event
if (!config.noop) {
await mjolnir.client.redactEvent(roomId, event['event_id'], "spam");
} else {
await logMessage(LogLevel.WARN, "WordList", `Tried to redact ${event['event_id']} in ${roomId} but Mjolnir is running in no-op mode`, roomId);
}
}
}
}
}
}

View File

@ -17,6 +17,7 @@ limitations under the License.
import { FirstMessageIsImage } from "./FirstMessageIsImage";
import { IProtection } from "./IProtection";
import { BasicFlooding, MAX_PER_MINUTE } from "./BasicFlooding";
import { WordList } from "./WordList";
export const PROTECTIONS: PossibleProtections = {
[new FirstMessageIsImage().name]: {
@ -28,6 +29,11 @@ export const PROTECTIONS: PossibleProtections = {
description: "If a user posts more than " + MAX_PER_MINUTE + " messages in 60s they'll be " +
"banned for spam. This does not publish the ban to any of your ban lists.",
factory: () => new BasicFlooding(),
},
[new WordList().name]: {
description: "If a user posts a monitored word within 20 minutes of joining, they " +
"will be banned from that room. This will not publish the ban to a ban list.",
factory: () => new WordList(),
}
};