Sanitize channel settings on channel creation

This commit is contained in:
Hri7566 2024-07-12 18:11:55 -04:00
parent adf3a2d3fe
commit 621131699d
3 changed files with 55 additions and 26 deletions

View File

@ -60,20 +60,20 @@ export class Channel extends EventEmitter {
) {
super();
this.settings = {};
mixin(this.settings, config.defaultSettings);
this.logger = new Logger("Channel - " + _id);
this.settings = {};
// Validate settings in set
// Set the verified settings
// Copy default settings
mixin(this.settings, config.defaultSettings);
if (!this.isLobby()) {
if (set) {
//this.logger.debug("Passed settings:", set);
// Validate settings in set
const validatedSet = validateChannelSettings(set);
//this.logger.debug("Validated settings:", validatedSet);
for (const key of Object.keys(set)) {
// Set the verified settings
for (const key of Object.keys(validatedSet)) {
this.logger.debug(`${key}: ${(validatedSet as any)[key]}`);
if ((validatedSet as any)[key] === false) continue;
(this.settings as any)[key] = (set as any)[key];
}
@ -95,6 +95,7 @@ export class Channel extends EventEmitter {
ChannelList.add(this);
// TODO implement owner_id
this.settings.owner_id = owner_id;
this.logger.info("Created");
}
@ -382,10 +383,6 @@ export class Channel extends EventEmitter {
* @returns undefined
*/
public join(socket: Socket, force: boolean = false): void {
//! /!\ Players are forced to join the same channel on two different tabs!
//? TODO Should this be a bug or a feature?
//this.logger.debug("join triggered");
if (this.isDestroyed()) return;
const part = socket.getParticipant() as Participant;
@ -400,6 +397,18 @@ export class Channel extends EventEmitter {
for (const ch of chs) {
const chid = ch.getID();
if (chid == config.fullChannel) {
const banTime = this.getBanTime(socket.getUserID());
this.logger.debug("Ban time:", banTime);
if (banTime) {
const minutes = Math.floor((banTime.endTime - banTime.startTime) / 1000 / 60);
socket.sendNotification({
class: "short",
duration: 7000,
target: "#room",
text: `Currently banned from "${this.getID()}" for ${minutes} minutes.`
});
}
return socket.setChannel(chid)
}
}
@ -859,10 +868,7 @@ export class Channel extends EventEmitter {
uuidsToKick = [...uuidsToKick, ...bannedUUIDs];
this.logger.debug("Banned UUIDs:", uuidsToKick);
for (const socket of socketsBySocketID.values()) {
this.logger.debug("Checking UUID:", socket.getUUID(), "| Result:", uuidsToKick.indexOf(socket.getUUID()) !== -1);
if (uuidsToKick.indexOf(socket.getUUID()) !== -1) {
socket.sendNotification({
title: "Notice",
@ -875,7 +881,6 @@ export class Channel extends EventEmitter {
// If they are here, move them to the ban channel
const ch = socket.getCurrentChannel();
if (ch) {
this.logger.debug("Current channel:", ch.getID(), "| We are:", this.getID());
if (ch.getID() == this.getID())
socket.setChannel(banChannel.getID());
}
@ -1028,6 +1033,19 @@ export class Channel extends EventEmitter {
return config.fullChannel;
}
}
/**
* Get the amount of time someone is banned in this channel for.
* @param userId User ID
* @returns Object containing endTime and startTime of the ban
**/
public getBanTime(userId: string) {
for (const ban of this.bans) {
if (userId == ban.userId) {
return { endTime: ban.endTime, startTime: ban.startTime };
}
}
}
}
export default Channel;

View File

@ -36,6 +36,7 @@ export function loadDefaultForcedChannels() {
}
if (!hasFullChannel) {
new Channel(config.fullChannel, undefined, undefined, undefined, true);
//new Channel(config.fullChannel, undefined, undefined, undefined, true);
forceloadChannel(config.fullChannel);
}
}

View File

@ -1,12 +1,11 @@
import { Logger } from "../util/Logger";
import { IChannelSettings } from "../util/types";
type Validator = "boolean" | "string" | "number" | ((val: unknown) => boolean);
// This record contains the exact data Brandon used to check channel settings, down to the regex.
// It also contains things that might be useful to other people in the future (things that make me vomit)
// This record contains almost the exact code Brandon used to check channel settings, down to the regex.
// It also contains things that might be useful in the future, like MPPNet settings
const validationRecord: Record<keyof IChannelSettings, Validator> = {
// Brandon
// Brandon's stuff
lobby: "boolean",
visible: "boolean",
chat: "boolean",
@ -21,17 +20,25 @@ const validationRecord: Record<keyof IChannelSettings, Validator> = {
},
owner_id: "string",
// MPPClone (why?)
limit: "number",
// MPPNet's stuff
limit: val => {
return typeof val === "number" && val <= 99 && val >= 0
},
noindex: "boolean"
};
// i made this
const adminOnlyKeys = [
"lobby",
"owner_id"
];
/**
* Check the validity of channel settings
* @param set Dirty settings
* @returns Record of which settings are correct
* @returns Record of which settings are correct (true) and which ones aren't (false)
*/
export function validateChannelSettings(set: Partial<IChannelSettings>) {
export function validateChannelSettings(set: Partial<IChannelSettings>, admin = false) {
// Create record
let record: Partial<Record<keyof IChannelSettings, boolean>> = {};
@ -47,6 +54,9 @@ export function validateChannelSettings(set: Partial<IChannelSettings>) {
continue;
}
// Are we allowed?
if (adminOnlyKeys.indexOf(key) !== -1 && !admin) continue;
// Set valid status
record[key as keyof IChannelSettings] = validate(val, validator);
}
@ -58,8 +68,8 @@ export default validateChannelSettings;
export function validate(value: any, validator: Validator) {
// What type of validator?
if (typeof validator == "function") {
// We are copying Zod's functionality
if (typeof validator === "function") {
// Run the function
return validator(value) === true;
} else if (typeof value === validator) {
return true;