mirror of
https://github.com/monkeytypegame/monkeytype.git
synced 2025-12-09 12:56:07 +08:00
Auto ban (#2984)
* added auto ban if user triggers anticheat too much * updated base configuration * autoban is optional * storing updated autoban array * only checking autoban in time 15 and 60 * miliseconds * removed unnecessary if check * returning if already banned * storing now in variable greater not greater equal * moved constant outside function * using partial type * renamed variables * renamed to autoBanTimestamps * added autoban tests * increased coverage minimum * slightly better test * added autoBanTimestamps checks to other tests
This commit is contained in:
parent
71564c30f8
commit
46e0c5c960
6 changed files with 124 additions and 1 deletions
|
|
@ -4,6 +4,7 @@ import {
|
|||
clearPb,
|
||||
getUser,
|
||||
getUsersCollection,
|
||||
recordAutoBanEvent,
|
||||
updateName,
|
||||
} from "../../src/dal/user";
|
||||
|
||||
|
|
@ -186,4 +187,71 @@ describe("UserDal", () => {
|
|||
5
|
||||
);
|
||||
});
|
||||
|
||||
it("autoBan should automatically ban after configured anticheat triggers", async () => {
|
||||
// given
|
||||
const testUser = {
|
||||
name: "Test",
|
||||
email: "mockemail@email.com",
|
||||
uid: "userId",
|
||||
};
|
||||
|
||||
await addUser(testUser.name, testUser.email, testUser.uid);
|
||||
|
||||
// when
|
||||
Date.now = jest.fn(() => 0);
|
||||
await recordAutoBanEvent(testUser.uid, 2, 1);
|
||||
await recordAutoBanEvent(testUser.uid, 2, 1);
|
||||
await recordAutoBanEvent(testUser.uid, 2, 1);
|
||||
|
||||
// then
|
||||
const updatedUser = await getUser(testUser.uid, "test");
|
||||
expect(updatedUser.banned).toBe(true);
|
||||
expect(updatedUser.autoBanTimestamps).toEqual([0, 0, 0]);
|
||||
});
|
||||
|
||||
it("autoBan should not ban ban if triggered once", async () => {
|
||||
// given
|
||||
const testUser = {
|
||||
name: "Test",
|
||||
email: "mockemail@email.com",
|
||||
uid: "userId",
|
||||
};
|
||||
|
||||
await addUser(testUser.name, testUser.email, testUser.uid);
|
||||
|
||||
// when
|
||||
Date.now = jest.fn(() => 0);
|
||||
await recordAutoBanEvent(testUser.uid, 2, 1);
|
||||
|
||||
// then
|
||||
const updatedUser = await getUser(testUser.uid, "test");
|
||||
expect(updatedUser.banned).toBe(undefined);
|
||||
expect(updatedUser.autoBanTimestamps).toEqual([0]);
|
||||
});
|
||||
|
||||
it("autoBan should correctly remove old anticheat triggers", async () => {
|
||||
// given
|
||||
const testUser = {
|
||||
name: "Test",
|
||||
email: "mockemail@email.com",
|
||||
uid: "userId",
|
||||
};
|
||||
|
||||
await addUser(testUser.name, testUser.email, testUser.uid);
|
||||
|
||||
// when
|
||||
Date.now = jest.fn(() => 0);
|
||||
await recordAutoBanEvent(testUser.uid, 2, 1);
|
||||
await recordAutoBanEvent(testUser.uid, 2, 1);
|
||||
|
||||
Date.now = jest.fn(() => 36000000);
|
||||
|
||||
await recordAutoBanEvent(testUser.uid, 2, 1);
|
||||
|
||||
// then
|
||||
const updatedUser = await getUser(testUser.uid, "test");
|
||||
expect(updatedUser.banned).toBe(undefined);
|
||||
expect(updatedUser.autoBanTimestamps).toEqual([36000000]);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ export default {
|
|||
// These percentages should never decrease
|
||||
statements: 37,
|
||||
branches: 38,
|
||||
functions: 19,
|
||||
functions: 20,
|
||||
lines: 40,
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import {
|
|||
checkIfTagPb,
|
||||
incrementBananas,
|
||||
updateTypingStats,
|
||||
recordAutoBanEvent,
|
||||
} from "../../dal/user";
|
||||
import * as PublicStatsDAL from "../../dal/public-stats";
|
||||
import { roundTo2, stdDev } from "../../utils/misc";
|
||||
|
|
@ -221,6 +222,15 @@ export async function addResult(
|
|||
}
|
||||
if (anticheatImplemented()) {
|
||||
if (!validateKeys(result, uid)) {
|
||||
//autoban
|
||||
const autoBanConfig = req.ctx.configuration.autoBan;
|
||||
if (autoBanConfig.enabled) {
|
||||
await recordAutoBanEvent(
|
||||
uid,
|
||||
autoBanConfig.maxCount,
|
||||
autoBanConfig.maxHours
|
||||
);
|
||||
}
|
||||
const status = MonkeyStatusCodes.BOT_DETECTED;
|
||||
throw new MonkeyError(status.code, "Possible bot detected");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,11 @@ const BASE_CONFIGURATION: MonkeyTypes.Configuration = {
|
|||
favoriteQuotes: {
|
||||
maxFavorites: 100,
|
||||
},
|
||||
autoBan: {
|
||||
enabled: false,
|
||||
maxCount: 5,
|
||||
maxHours: 1,
|
||||
},
|
||||
};
|
||||
|
||||
export default BASE_CONFIGURATION;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ import {
|
|||
WithId,
|
||||
} from "mongodb";
|
||||
|
||||
const SECONDS_PER_HOUR = 3600;
|
||||
|
||||
// Export for use in tests
|
||||
export const getUsersCollection = (): Collection<WithId<MonkeyTypes.User>> =>
|
||||
db.collection<MonkeyTypes.User>("users");
|
||||
|
|
@ -595,3 +597,35 @@ export async function removeFavoriteQuote(
|
|||
{ $pull: { [`favoriteQuotes.${language}`]: quoteId } }
|
||||
);
|
||||
}
|
||||
|
||||
export async function recordAutoBanEvent(
|
||||
uid: string,
|
||||
maxCount: number,
|
||||
maxHours: number
|
||||
): Promise<void> {
|
||||
const user = await getUser(uid, "record auto ban event");
|
||||
|
||||
if (user.banned) return;
|
||||
|
||||
const autoBanTimestamps = user.autoBanTimestamps ?? [];
|
||||
|
||||
const now = Date.now();
|
||||
|
||||
//remove any old events
|
||||
const recentAutoBanTimestamps = autoBanTimestamps.filter(
|
||||
(timestamp) => timestamp >= now - maxHours * SECONDS_PER_HOUR * 1000
|
||||
);
|
||||
|
||||
//push new event
|
||||
recentAutoBanTimestamps.push(now);
|
||||
|
||||
//update user, ban if needed
|
||||
const updateObj: Partial<MonkeyTypes.User> = {
|
||||
autoBanTimestamps: recentAutoBanTimestamps,
|
||||
};
|
||||
if (recentAutoBanTimestamps.length > maxCount) {
|
||||
updateObj.banned = true;
|
||||
}
|
||||
|
||||
await getUsersCollection().updateOne({ uid }, { $set: updateObj });
|
||||
}
|
||||
|
|
|
|||
6
backend/src/types/types.d.ts
vendored
6
backend/src/types/types.d.ts
vendored
|
|
@ -29,6 +29,11 @@ declare namespace MonkeyTypes {
|
|||
favoriteQuotes: {
|
||||
maxFavorites: number;
|
||||
};
|
||||
autoBan: {
|
||||
enabled: boolean;
|
||||
maxCount: number;
|
||||
maxHours: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface DecodedToken {
|
||||
|
|
@ -49,6 +54,7 @@ declare namespace MonkeyTypes {
|
|||
// Data Model
|
||||
|
||||
interface User {
|
||||
autoBanTimestamps?: number[];
|
||||
addedAt: number;
|
||||
verified?: boolean;
|
||||
bananas?: number;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue