From 591eac4367808cb2f9084ce9fed46efdeb1e4ad2 Mon Sep 17 00:00:00 2001 From: oranges Date: Sat, 4 Jan 2020 09:21:00 +1300 Subject: [PATCH] Add a spam limiter to the goonchat topic calls (#48524) --- code/modules/goonchat/browserOutput.dm | 32 ++++++++++++++++++- .../browserassets/js/browserOutput.js | 20 ++++++------ 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/code/modules/goonchat/browserOutput.dm b/code/modules/goonchat/browserOutput.dm index f3b0fbffc2f..ebe15c5e7c9 100644 --- a/code/modules/goonchat/browserOutput.dm +++ b/code/modules/goonchat/browserOutput.dm @@ -5,9 +5,16 @@ For the main html chat area //Precaching a bunch of shit GLOBAL_DATUM_INIT(iconCache, /savefile, new("tmp/iconCache.sav")) //Cache of icons for the browser output +//Should match the value set in the browser js +#define MAX_COOKIE_LENGTH 5 + //On client, created on login /datum/chatOutput var/client/owner //client ref + // How many times client data has been checked + var/total_checks = 0 + // When to next clear the client data checks counter + var/next_time_to_clear = 0 var/loaded = FALSE // Has the client loaded the browser output area? var/list/messageQueue //If they haven't loaded chat, this is where messages will go until they do var/cookieSent = FALSE // Has the client sent a cookie for analysis @@ -172,6 +179,18 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("tmp/iconCache.sav")) //Cache of ico //Called by client, sent data to investigate (cookie history so far) /datum/chatOutput/proc/analyzeClientData(cookie = "") + //Spam check + if(world.time > next_time_to_clear) + next_time_to_clear = world.time + (3 SECONDS) + total_checks = 0 + + total_checks += 1 + + if(total_checks > SPAM_TRIGGER_AUTOMUTE) + message_admins("[key_name(owner)] kicked for goonchat topic spam") + qdel(owner) + return + if(!cookie) return @@ -180,17 +199,26 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("tmp/iconCache.sav")) //Cache of ico if (connData && islist(connData) && connData.len > 0 && connData["connData"]) connectionHistory = connData["connData"] //lol fuck var/list/found = new() + + if(connectionHistory.len > MAX_COOKIE_LENGTH) + message_admins("[key_name(src.owner)] was kicked for an invalid ban cookie)") + qdel(owner) + return + for(var/i in connectionHistory.len to 1 step -1) + if(QDELETED(owner)) + //he got cleaned up before we were done + return var/list/row = src.connectionHistory[i] if (!row || row.len < 3 || (!row["ckey"] || !row["compid"] || !row["ip"])) //Passed malformed history object return if (world.IsBanned(row["ckey"], row["ip"], row["compid"], real_bans_only=TRUE)) found = row break + CHECK_TICK //Uh oh this fucker has a history of playing on a banned account!! if (found.len > 0) - //TODO: add a new evasion ban for the CURRENT client details, using the matched row details message_admins("[key_name(src.owner)] has a cookie from a banned account! (Matched: [found["ckey"]], [found["ip"]], [found["compid"]])") log_admin_private("[key_name(owner)] has a cookie from a banned account! (Matched: [found["ckey"]], [found["ip"]], [found["compid"]])") @@ -269,3 +297,5 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("tmp/iconCache.sav")) //Cache of ico /datum/chatOutput/proc/swaptodarkmode() owner.force_dark_theme() + +#undef MAX_COOKIE_LENGTH diff --git a/code/modules/goonchat/browserassets/js/browserOutput.js b/code/modules/goonchat/browserassets/js/browserOutput.js index f7551784847..cb384cc7aab 100644 --- a/code/modules/goonchat/browserassets/js/browserOutput.js +++ b/code/modules/goonchat/browserassets/js/browserOutput.js @@ -29,7 +29,7 @@ var opts = { 'scrollSnapTolerance': 10, //If within x pixels of bottom 'clickTolerance': 10, //Keep focus if outside x pixels of mousedown position on mouseup 'imageRetryDelay': 50, //how long between attempts to reload images (in ms) - 'imageRetryLimit': 50, //how many attempts should we make? + 'imageRetryLimit': 50, //how many attempts should we make? 'popups': 0, //Amount of popups opened ever 'wasd': false, //Is the user in wasd mode? 'priorChatHeight': 0, //Thing for height-resizing detection @@ -67,7 +67,7 @@ var opts = { 'updatedVolume': 0, //The volume level that is sent to the server 'musicStartAt': 0, //The position the music starts playing 'musicEndAt': 0, //The position the music... stops playing... if null, doesn't apply (so the music runs through) - + 'defaultMusicVolume': 25, 'messageCombining': true, @@ -161,7 +161,7 @@ function byondDecode(message) { // The replace for + is because FOR SOME REASON, BYOND replaces spaces with a + instead of %20, and a plus with %2b. // Marvelous. message = message.replace(/\+/g, "%20"); - try { + try { // This is a workaround for the above not always working when BYOND's shitty url encoding breaks. (byond bug id:2399401) if (decodeURIComponent) { message = decodeURIComponent(message); @@ -494,8 +494,8 @@ function handleClientData(ckey, ip, compid) { return; //Record already exists } } - - if (opts.clientData.length >= opts.clientDataLimit) { + //Lets make sure we obey our limit (can connect from server with higher limit) + while (opts.clientData.length >= opts.clientDataLimit) { opts.clientData.shift(); } } else { @@ -784,7 +784,7 @@ $(function() { else{ $('#adminMusic').prop('volume', opts.defaultMusicVolume / 100); } - + if (savedConfig.smessagecombining) { if (savedConfig.smessagecombining == 'false') { opts.messageCombining = false; @@ -1100,7 +1100,7 @@ $(function() { $messages.empty(); opts.messageCount = 0; }); - + $('#musicVolumeSpan').hover(function() { $('#musicVolumeText').addClass('hidden'); $('#musicVolume').removeClass('hidden'); @@ -1127,9 +1127,9 @@ $(function() { }); $('img.icon').error(iconError); - - - + + + /***************************************** *