* 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:
Jack 2022-05-16 21:03:44 +02:00 committed by GitHub
parent 71564c30f8
commit 46e0c5c960
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 124 additions and 1 deletions

View file

@ -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]);
});
});

View file

@ -9,7 +9,7 @@ export default {
// These percentages should never decrease
statements: 37,
branches: 38,
functions: 19,
functions: 20,
lines: 40,
},
},

View file

@ -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");
}

View file

@ -29,6 +29,11 @@ const BASE_CONFIGURATION: MonkeyTypes.Configuration = {
favoriteQuotes: {
maxFavorites: 100,
},
autoBan: {
enabled: false,
maxCount: 5,
maxHours: 1,
},
};
export default BASE_CONFIGURATION;

View file

@ -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 });
}

View file

@ -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;