mirror of
https://github.com/matrix-org/mjolnir.git
synced 2024-10-01 01:36:06 -04:00
Add basic flooding protection too
This commit is contained in:
parent
d5f260b982
commit
1b795c7b17
72
src/protections/BasicFlooding.ts
Normal file
72
src/protections/BasicFlooding.ts
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
Copyright 2019 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";
|
||||
|
||||
export const MAX_PER_MINUTE = 10; // if this is exceeded, we'll ban the user for spam and redact their messages
|
||||
const TIMESTAMP_THRESHOLD = 30000; // 30s out of phase
|
||||
|
||||
export class BasicFlooding implements IProtection {
|
||||
|
||||
public lastEvents: { [roomId: string]: {[userId: string]: {originServerTs: number, eventId: string}[]} } = {};
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
public get name(): string {
|
||||
return 'BasicFloodingProtection';
|
||||
}
|
||||
|
||||
public async handleEvent(mjolnir: Mjolnir, roomId: string, event: any): Promise<any> {
|
||||
if (!this.lastEvents[roomId]) this.lastEvents[roomId] = {};
|
||||
|
||||
const forRoom = this.lastEvents[roomId];
|
||||
if (!forRoom[event['sender']]) forRoom[event['sender']] = [];
|
||||
let forUser = forRoom[event['sender']];
|
||||
|
||||
if ((new Date()).getTime() - event['origin_server_ts'] > TIMESTAMP_THRESHOLD) {
|
||||
LogService.warn("BasicFlooding", `${event['event_id']} is more than ${TIMESTAMP_THRESHOLD}ms out of phase - rewriting event time to be 'now'`);
|
||||
event['origin_server_ts'] = (new Date()).getTime();
|
||||
}
|
||||
|
||||
forUser.push({originServerTs: event['origin_server_ts'], eventId: event['event_id']});
|
||||
|
||||
// Do some math to see if the user is spamming
|
||||
let messageCount = 0;
|
||||
for (const prevEvent of forUser) {
|
||||
if ((new Date()).getTime() - prevEvent.originServerTs > 60000) continue; // not important
|
||||
messageCount++;
|
||||
}
|
||||
|
||||
if (messageCount >= MAX_PER_MINUTE) {
|
||||
await logMessage(LogLevel.WARN, "BasicFlooding", `Banning ${event['sender']} in ${roomId} for flooding (${messageCount} messages in the last minute)`);
|
||||
await mjolnir.client.banUser(event['sender'], roomId, "spam");
|
||||
// Redact all the things the user said too
|
||||
for (const eventId of forUser.map(e => e.eventId)) {
|
||||
await mjolnir.client.redactEvent(roomId, eventId, "spam");
|
||||
}
|
||||
forUser = forRoom[event['sender']] = []; // reset the user's list
|
||||
}
|
||||
|
||||
// Trim the oldest messages off the user's history if it's getting large
|
||||
if (forUser.length > MAX_PER_MINUTE*2) {
|
||||
forUser.splice(0, forUser.length - (MAX_PER_MINUTE*2) - 1);
|
||||
}
|
||||
}
|
||||
}
|
@ -16,6 +16,7 @@ limitations under the License.
|
||||
|
||||
import { FirstMessageIsImage } from "./FirstMessageIsImage";
|
||||
import { IProtection } from "./IProtection";
|
||||
import { BasicFlooding, MAX_PER_MINUTE } from "./BasicFlooding";
|
||||
|
||||
export const PROTECTIONS: PossibleProtections = {
|
||||
[new FirstMessageIsImage().name]: {
|
||||
@ -23,6 +24,11 @@ export const PROTECTIONS: PossibleProtections = {
|
||||
"they'll be banned for spam. This does not publish the ban to any of your ban lists.",
|
||||
factory: () => new FirstMessageIsImage(),
|
||||
},
|
||||
[new BasicFlooding().name]: {
|
||||
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(),
|
||||
}
|
||||
};
|
||||
|
||||
export interface PossibleProtections {
|
||||
|
Loading…
Reference in New Issue
Block a user