/*
* CSB Anti Exploit
* Copyright (C) 2026 counter-strike-boost.com
*
* Validates client input to block classic HLDS/AMXX abuse: over-long client
* commands, malformed setinfo keys, empty/over-long names and the fullupdate
* spam vector. Offenders are logged and optionally kicked.
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version. It is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See for details.
*/
#include
new const PLUGIN[] = "CSB Anti Exploit"
new const VERSION[] = "1.0.0"
new const AUTHOR[] = "counter-strike-boost.com"
new g_pEnabled, g_pMaxCmdLen, g_pMaxNameLen, g_pAction
public plugin_init()
{
register_plugin(PLUGIN, VERSION, AUTHOR)
g_pEnabled = register_cvar("csb_antiexploit_enabled", "1")
g_pMaxCmdLen = register_cvar("csb_antiexploit_maxcmd", "128")
g_pMaxNameLen = register_cvar("csb_antiexploit_maxname", "32")
g_pAction = register_cvar("csb_antiexploit_action", "1")
// fullupdate spam is a well-known server-stress vector
register_clcmd("fullupdate", "blockFullUpdate")
}
public blockFullUpdate(id)
{
return get_pcvar_num(g_pEnabled) ? PLUGIN_HANDLED : PLUGIN_CONTINUE
}
public client_command(id)
{
if (!get_pcvar_num(g_pEnabled) || !is_user_connected(id))
return PLUGIN_CONTINUE
new cmd[64]
read_argv(0, cmd, charsmax(cmd))
// chat lines can legitimately be long, so don't length-check them
if (!equali(cmd, "say") && !equali(cmd, "say_team"))
{
new args[256]
read_args(args, charsmax(args))
new maxLen = get_pcvar_num(g_pMaxCmdLen)
if (maxLen > 0 && strlen(args) > maxLen)
{
punish(id, "over-long client command")
return PLUGIN_HANDLED
}
}
// malformed setinfo: empty key, or absurd key/value length
if (equali(cmd, "setinfo"))
{
new key[128], val[128]
read_argv(1, key, charsmax(key))
read_argv(2, val, charsmax(val))
if (!key[0] || strlen(key) > 63 || strlen(val) > 100)
{
punish(id, "malformed setinfo key/value")
return PLUGIN_HANDLED
}
}
return PLUGIN_CONTINUE
}
public client_infochanged(id)
{
if (!get_pcvar_num(g_pEnabled) || !is_user_connected(id))
return
new newName[64]
get_user_info(id, "name", newName, charsmax(newName))
// empty-name exploit: reset to a safe placeholder
if (!newName[0])
{
set_user_info(id, "name", "unnamed")
log_amx("[CSB AntiExploit] reset an empty name from slot %d", id)
return
}
new maxName = get_pcvar_num(g_pMaxNameLen)
if (maxName > 0 && strlen(newName) > maxName)
{
// truncating re-fires client_infochanged, but the shorter name passes
newName[maxName] = 0
set_user_info(id, "name", newName)
log_amx("[CSB AntiExploit] truncated an over-long name from slot %d", id)
}
}
punish(id, const reason[])
{
new name[32], authid[35], ip[24]
get_user_name(id, name, charsmax(name))
get_user_authid(id, authid, charsmax(authid))
get_user_ip(id, ip, charsmax(ip), 1)
log_amx("[CSB AntiExploit] %s <%s> (%s) blocked: %s", name, authid, ip, reason)
if (get_pcvar_num(g_pAction) == 1)
{
server_cmd("kick #%d ^"CSB Anti-Exploit: %s^"", get_user_userid(id), reason)
server_exec()
}
}