Tester made a file flooder... rate limits shall be implemented now!
This commit is contained in:
@@ -4,6 +4,7 @@ import io.github.itzispyder.pdk.utils.misc.config.JsonSerializable;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
public class NBTConfig implements JsonSerializable<NBTConfig> {
|
||||
@Override
|
||||
@@ -13,6 +14,16 @@ public class NBTConfig implements JsonSerializable<NBTConfig> {
|
||||
return file;
|
||||
}
|
||||
|
||||
public RateLimit rateLimit = new RateLimit();
|
||||
|
||||
public class RateLimit {
|
||||
public int rateLimitBytes = 16348;
|
||||
public int byteDecay = 1024;
|
||||
public int rateLimitItems = 10;
|
||||
public int itemDecay = 2;
|
||||
public List<String> punishmentCommands = List.of("kick %player% Internal Exception: io.netty.handler.codec.DecoderException: java.lang.RuntimeException: Tried to read NBT tag that was too big; tried to allocate 28391038bytes where max allowed: 16348");
|
||||
}
|
||||
|
||||
public boolean allowName = true;
|
||||
public boolean allowLore = true;
|
||||
public boolean allowAttributes = false;
|
||||
@@ -21,6 +32,8 @@ public class NBTConfig implements JsonSerializable<NBTConfig> {
|
||||
public boolean allowCustomTools = false;
|
||||
public boolean allowBooks = false;
|
||||
public boolean allowRecursion = true;
|
||||
public int maxCustomData = 64;
|
||||
|
||||
public int globalMaxEnchant = 5;
|
||||
public int maxMending = 1;
|
||||
public int maxUnbreaking = 3;
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package me.trouper.sentinel.server.events.violations.players;
|
||||
|
||||
import io.github.itzispyder.pdk.plugin.gui.CustomGui;
|
||||
import io.github.itzispyder.pdk.utils.misc.Pair;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.data.storage.NBTStorage;
|
||||
import me.trouper.sentinel.server.events.violations.AbstractViolation;
|
||||
import me.trouper.sentinel.server.functions.helpers.ActionConfiguration;
|
||||
import me.trouper.sentinel.server.functions.itemchecks.ItemCheck;
|
||||
import me.trouper.sentinel.server.functions.itemchecks.RateLimitCheck;
|
||||
import me.trouper.sentinel.server.gui.Items;
|
||||
import me.trouper.sentinel.server.gui.MainGUI;
|
||||
import me.trouper.sentinel.server.gui.config.AntiNukeGUI;
|
||||
@@ -28,18 +30,36 @@ public class CreativeHotbar extends AbstractViolation {
|
||||
private void onNBTPull(InventoryCreativeEvent e) {
|
||||
//ServerUtils.verbose("NBT: Detected creative mode action");
|
||||
if (!Sentinel.getInstance().getDirector().io.violationConfig.creativeHotbarAction.enabled) return;
|
||||
ServerUtils.verbose("NBT: Enabled");
|
||||
//ServerUtils.verbose("NBT: Enabled");
|
||||
if (!(e.getWhoClicked() instanceof Player p)) return;
|
||||
ServerUtils.verbose("NBT: Clicker is a player");
|
||||
//ServerUtils.verbose("NBT: Clicker is a player");
|
||||
if (e.getCursor() == null) return; // Well it threw an exception during testing, so it isn't always false!
|
||||
ServerUtils.verbose("NBT: Cursor isn't null");
|
||||
//ServerUtils.verbose("NBT: Cursor isn't null");
|
||||
ItemStack i = e.getCursor();
|
||||
if (PlayerUtils.isTrusted(p)) return;
|
||||
ServerUtils.verbose("NBT: Not trusted");
|
||||
//ServerUtils.verbose("NBT: Not trusted");
|
||||
if (e.getCursor().getItemMeta() == null) return;
|
||||
ServerUtils.verbose("NBT: Cursor has meta");
|
||||
//ServerUtils.verbose("NBT: Cursor has meta");
|
||||
if (!(i.hasItemMeta() && i.getItemMeta() != null)) return;
|
||||
ServerUtils.verbose("NBT: Item has meta");
|
||||
if (!new RateLimitCheck().passes(new Pair<>(p,i))) {
|
||||
ServerUtils.verbose("Player flags rate limit, performing action");
|
||||
ActionConfiguration.Builder config = new ActionConfiguration.Builder()
|
||||
.setEvent(e)
|
||||
.setPlayer(p)
|
||||
.cancel(true)
|
||||
.punish(true)
|
||||
.deop(Sentinel.getInstance().getDirector().io.violationConfig.creativeHotbarAction.deop)
|
||||
.setPunishmentCommands(Sentinel.getInstance().getDirector().io.nbtConfig.rateLimit.punishmentCommands);
|
||||
|
||||
runActions(
|
||||
Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.grab, Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.nbtItem),
|
||||
Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.rootNameFormatPlayer.formatted(p.getName(), Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.grab, Sentinel.getInstance().getDirector().io.lang.violations.protections.rootName.nbtItem),
|
||||
generatePlayerInfo(p),
|
||||
config
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
if (new ItemCheck().passes(i)) return;
|
||||
ServerUtils.verbose("NBT: Item doesn't pass, performing action");
|
||||
|
||||
|
||||
@@ -62,12 +62,6 @@ public class PluginCloakingPacket implements PacketListener {
|
||||
}
|
||||
}
|
||||
}
|
||||
case PacketType.Play.Client.CHAT_COMMAND, PacketType.Play.Client.CHAT_COMMAND_UNSIGNED -> {
|
||||
WrapperPlayClientChatCommandUnsigned wrapper = new WrapperPlayClientChatCommandUnsigned(event);
|
||||
WrapperPlayClientChatCommand wrappers = new WrapperPlayClientChatCommand(event);
|
||||
wrapper.getCommand();
|
||||
wrappers.getCommand();
|
||||
}
|
||||
default -> {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,6 @@ public class ActionConfiguration {
|
||||
this.punishmentCommands = builder.punishmentCommands;
|
||||
this.logToDiscord = builder.logToDiscord;
|
||||
this.actionNode = builder.actionNode;
|
||||
// Removed the actions being run here to prevent double execution
|
||||
}
|
||||
|
||||
public Player getPlayer() {
|
||||
|
||||
@@ -2,6 +2,7 @@ package me.trouper.sentinel.server.functions.itemchecks;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NBT;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.data.config.NBTConfig;
|
||||
import me.trouper.sentinel.utils.InventoryUtils;
|
||||
import me.trouper.sentinel.utils.ServerUtils;
|
||||
import org.bukkit.Material;
|
||||
@@ -17,6 +18,7 @@ public class ItemCheck extends AbstractCheck<ItemStack> {
|
||||
@Override
|
||||
public boolean passes(ItemStack item) {
|
||||
ServerUtils.verbose("Checking item: " + item.getType().name());
|
||||
NBTConfig config = Sentinel.getInstance().getDirector().io.nbtConfig;
|
||||
|
||||
// No metadata? Nothing to check.
|
||||
if (item.getItemMeta() == null) {
|
||||
@@ -38,11 +40,11 @@ public class ItemCheck extends AbstractCheck<ItemStack> {
|
||||
// NBT-based checks (e.g. custom consumables/tools).
|
||||
var nbt = NBT.itemStackToNBT(item);
|
||||
var components = nbt.getCompound("components");
|
||||
if (!Sentinel.getInstance().getDirector().io.nbtConfig.allowCustomConsumables && components.getCompound("minecraft:consumable") != null) {
|
||||
if (!config.allowCustomConsumables && components.getCompound("minecraft:consumable") != null) {
|
||||
ServerUtils.verbose("Item is consumable and not allowed.");
|
||||
return false;
|
||||
}
|
||||
if (!Sentinel.getInstance().getDirector().io.nbtConfig.allowCustomTools && components.getCompound("minecraft:tool") != null) {
|
||||
if (!config.allowCustomTools && components.getCompound("minecraft:tool") != null) {
|
||||
ServerUtils.verbose("Item is custom tool and not allowed.");
|
||||
return false;
|
||||
}
|
||||
@@ -148,32 +150,32 @@ public class ItemCheck extends AbstractCheck<ItemStack> {
|
||||
}
|
||||
|
||||
// Name, lore, potion, attribute and enchantment checks.
|
||||
if (!Sentinel.getInstance().getDirector().io.nbtConfig.allowName && meta.hasDisplayName()) {
|
||||
if (!config.allowName && meta.hasDisplayName()) {
|
||||
ServerUtils.verbose("Custom names not allowed.");
|
||||
return false;
|
||||
}
|
||||
if (!Sentinel.getInstance().getDirector().io.nbtConfig.allowLore && meta.hasLore()) {
|
||||
if (!config.allowLore && meta.hasLore()) {
|
||||
ServerUtils.verbose("Custom lore not allowed.");
|
||||
return false;
|
||||
}
|
||||
if (!Sentinel.getInstance().getDirector().io.nbtConfig.allowPotions &&
|
||||
if (!config.allowPotions &&
|
||||
(item.getType().equals(Material.POTION) ||
|
||||
item.getType().equals(Material.SPLASH_POTION) ||
|
||||
item.getType().equals(Material.LINGERING_POTION))) {
|
||||
ServerUtils.verbose("Potions not allowed.");
|
||||
return false;
|
||||
}
|
||||
if (!Sentinel.getInstance().getDirector().io.nbtConfig.allowAttributes && meta.hasAttributeModifiers()) {
|
||||
if (!config.allowAttributes && meta.hasAttributeModifiers()) {
|
||||
ServerUtils.verbose("Attribute modifiers not allowed.");
|
||||
return false;
|
||||
}
|
||||
if (Sentinel.getInstance().getDirector().io.nbtConfig.globalMaxEnchant != 0 && new EnchantmentCheck().hasIllegalEnchants(item)) {
|
||||
if (config.globalMaxEnchant != 0 && new EnchantmentCheck().hasIllegalEnchants(item)) {
|
||||
ServerUtils.verbose("Illegal enchantments found.");
|
||||
return false;
|
||||
}
|
||||
// Recursion check for use-remainder items.
|
||||
if (meta.hasUseRemainder()) {
|
||||
if (!Sentinel.getInstance().getDirector().io.nbtConfig.allowRecursion) {
|
||||
if (!config.allowRecursion) {
|
||||
ServerUtils.verbose("Recursion not allowed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
package me.trouper.sentinel.server.functions.itemchecks;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NBTItem;
|
||||
import io.github.itzispyder.pdk.utils.misc.Pair;
|
||||
import me.trouper.sentinel.Sentinel;
|
||||
import me.trouper.sentinel.utils.ServerUtils;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class RateLimitCheck extends AbstractCheck<Pair<Player,ItemStack>> {
|
||||
|
||||
|
||||
public static Map<UUID, Integer> dataUsed = new HashMap<>();
|
||||
public static Map<UUID, Integer> itemsUsed = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public boolean passes(Pair<Player,ItemStack> input) {
|
||||
Player player = input.left;
|
||||
UUID uuid = player.getUniqueId();
|
||||
ItemStack item = input.right;
|
||||
|
||||
return itemLimit(player,uuid,item) && dataLimit(player,uuid,item);
|
||||
}
|
||||
|
||||
private boolean itemLimit(Player player, UUID uuid, ItemStack item) {
|
||||
int currentUsed = itemsUsed.getOrDefault(uuid,0);
|
||||
ServerUtils.verbose("Current Player used items: " + currentUsed);
|
||||
currentUsed++;
|
||||
itemsUsed.put(uuid,currentUsed);
|
||||
return currentUsed <= Sentinel.getInstance().getDirector().io.nbtConfig.rateLimit.rateLimitItems;
|
||||
}
|
||||
|
||||
|
||||
private boolean dataLimit(Player player, UUID uuid, ItemStack item) {
|
||||
int itemData = 0;
|
||||
int currentData = dataUsed.getOrDefault(uuid,0);
|
||||
|
||||
ServerUtils.verbose("Current Player used data: " + currentData);
|
||||
try {
|
||||
NBTItem nbt = new NBTItem(item);
|
||||
itemData = nbt.toString().length();
|
||||
ServerUtils.verbose("Item data: " + itemData);
|
||||
currentData += itemData;
|
||||
} catch (Exception e) {
|
||||
Sentinel.getInstance().getLogger().warning("Could not determine size of item. Blocking.");
|
||||
Sentinel.getInstance().getLogger().warning(Arrays.toString(e.getStackTrace()));
|
||||
return false;
|
||||
}
|
||||
|
||||
dataUsed.put(uuid,currentData);
|
||||
|
||||
ServerUtils.verbose("New Player used data: " + currentData);
|
||||
|
||||
return currentData <= Sentinel.getInstance().getDirector().io.nbtConfig.rateLimit.rateLimitBytes;
|
||||
}
|
||||
|
||||
public static void decayData() {
|
||||
for (UUID uuid : dataUsed.keySet()) {
|
||||
int currentData = dataUsed.get(uuid);
|
||||
if (currentData > 0) {
|
||||
currentData -= Sentinel.getInstance().getDirector().io.nbtConfig.rateLimit.byteDecay;
|
||||
dataUsed.put(uuid, Math.max(0, currentData));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void decayItems() {
|
||||
for (UUID uuid : itemsUsed.keySet()) {
|
||||
int currentItems = itemsUsed.get(uuid);
|
||||
if (currentItems > 0) {
|
||||
currentItems -= Sentinel.getInstance().getDirector().io.nbtConfig.rateLimit.itemDecay;
|
||||
itemsUsed.put(uuid, Math.max(0, currentItems));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@ import me.trouper.sentinel.server.events.violations.entities.CommandMinecartPlac
|
||||
import me.trouper.sentinel.server.events.violations.entities.CommandMinecartUse;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.profanity.ProfanityFilter;
|
||||
import me.trouper.sentinel.server.functions.chatfilter.spam.SpamFilter;
|
||||
import me.trouper.sentinel.server.functions.itemchecks.RateLimitCheck;
|
||||
import me.trouper.sentinel.utils.Text;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
@@ -183,6 +184,8 @@ public final class Loader {
|
||||
Bukkit.getScheduler().runTaskTimer(Sentinel.getInstance(), SpamFilter::decayHeat,0, 20);
|
||||
Bukkit.getScheduler().runTaskTimer(Sentinel.getInstance(), ProfanityFilter::decayScore,0,1200);
|
||||
Bukkit.getScheduler().runTaskTimer(Sentinel.getInstance(), WandEvents::handleDisplay,0,1);
|
||||
Bukkit.getScheduler().runTaskTimer(Sentinel.getInstance(), RateLimitCheck::decayData,0,20*60);
|
||||
Bukkit.getScheduler().runTaskTimer(Sentinel.getInstance(), RateLimitCheck::decayItems,0,200);
|
||||
|
||||
if (Sentinel.getInstance().getDirector().io.mainConfig.backdoorDetection.enabled) Sentinel.getInstance().getDirector().backdoorDetection.init();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user