From 0b0c0a5ed65f0953f12e5ce1107710d686fe4650 Mon Sep 17 00:00:00 2001 From: dakrk Date: Sun, 22 Oct 2023 15:42:39 +0100 Subject: [PATCH] Handle ban command arguments as array This provides a much more reliable way to handle arguments with spaces, such as usernames. The ban process's stdout and stderr are now redirected to the server, in case they have output that would be good in the server's logs. --- src/IConfig.ts | 2 +- src/User.ts | 34 +++++++++++++++++++++++++++++----- src/log.ts | 4 ++-- 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/IConfig.ts b/src/IConfig.ts index 9e2323d..6552948 100644 --- a/src/IConfig.ts +++ b/src/IConfig.ts @@ -20,7 +20,7 @@ export default interface IConfig { node : string; displayname : string; motd : string; - bancmd : string; + bancmd : string | string[]; moderatorEnabled : boolean; usernameblacklist : string[]; maxChatLength : number; diff --git a/src/User.ts b/src/User.ts index 8be7808..b9e9ad6 100644 --- a/src/User.ts +++ b/src/User.ts @@ -4,7 +4,9 @@ import {WebSocket} from 'ws'; import {IPData} from './IPData.js'; import IConfig from './IConfig.js'; import RateLimiter from './RateLimiter.js'; -import { execaCommand } from 'execa'; +import { execa, execaCommand, ExecaSyncError } from 'execa'; +import log from './log.js'; + export class User { socket : WebSocket; nopSendInterval : NodeJS.Timeout; @@ -100,13 +102,35 @@ export class User { this.sendMsg(guacutils.encode("chat", "", "You are no longer muted.")); } + private banCmdArgs(arg: string) : string { + return arg.replace(/\$IP/g, this.IP.address).replace(/\$NAME/g, this.username || ""); + } + async ban() { // Prevent the user from taking turns or chatting, in case the ban command takes a while this.IP.muted = true; - //@ts-ignore - var cmd = this.Config.collabvm.bancmd.replace(/\$IP/g, this.IP.address).replace(/\$NAME/g, this.username); - await execaCommand(cmd); - this.kick(); + + try { + if (Array.isArray(this.Config.collabvm.bancmd)) { + let args: string[] = this.Config.collabvm.bancmd.map((a: string) => this.banCmdArgs(a)); + if (args.length || args[0].length) { + await execa(args.shift()!, args, {stdout: process.stdout, stderr: process.stderr}); + this.kick(); + } else { + log("ERROR", `Failed to ban ${this.IP.address} (${this.username}): Empty command`); + } + } else if (typeof this.Config.collabvm.bancmd == "string") { + let cmd: string = this.banCmdArgs(this.Config.collabvm.bancmd); + if (cmd.length) { + await execaCommand(cmd, {stdout: process.stdout, stderr: process.stderr}); + this.kick(); + } else { + log("ERROR", `Failed to ban ${this.IP.address} (${this.username}): Empty command`); + } + } + } catch (e) { + log("ERROR", `Failed to ban ${this.IP.address} (${this.username}): ${(e as ExecaSyncError).shortMessage}`); + } } async kick() { diff --git a/src/log.ts b/src/log.ts index 5c32f66..ee1928c 100644 --- a/src/log.ts +++ b/src/log.ts @@ -1,7 +1,7 @@ -export default function log(loglevel : string, message : string) { +export default function log(loglevel : string, ...message : string[]) { console[ (loglevel === "ERROR" || loglevel === "FATAL") ? "error" : (loglevel === "WARN") ? "warn" : "log" - ](`[${new Date().toLocaleString()}] [${loglevel}] ${message}`); + ](`[${new Date().toLocaleString()}] [${loglevel}]`, ...message); } \ No newline at end of file