Tester made a file flooder... rate limits shall be implemented now!
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -12,6 +12,7 @@ softdepend:
|
|||||||
- ViaBackwards
|
- ViaBackwards
|
||||||
- ViaRewind
|
- ViaRewind
|
||||||
- Geyser-Spigot
|
- Geyser-Spigot
|
||||||
|
- NoChatReports
|
||||||
load: POSTWORLD
|
load: POSTWORLD
|
||||||
permissions:
|
permissions:
|
||||||
sentinel.admin:
|
sentinel.admin:
|
||||||
|
|||||||
Binary file not shown.
@@ -4,6 +4,7 @@ import io.github.itzispyder.pdk.utils.misc.config.JsonSerializable;
|
|||||||
import me.trouper.sentinel.Sentinel;
|
import me.trouper.sentinel.Sentinel;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class NBTConfig implements JsonSerializable<NBTConfig> {
|
public class NBTConfig implements JsonSerializable<NBTConfig> {
|
||||||
@Override
|
@Override
|
||||||
@@ -13,6 +14,16 @@ public class NBTConfig implements JsonSerializable<NBTConfig> {
|
|||||||
return file;
|
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 allowName = true;
|
||||||
public boolean allowLore = true;
|
public boolean allowLore = true;
|
||||||
public boolean allowAttributes = false;
|
public boolean allowAttributes = false;
|
||||||
@@ -21,6 +32,8 @@ public class NBTConfig implements JsonSerializable<NBTConfig> {
|
|||||||
public boolean allowCustomTools = false;
|
public boolean allowCustomTools = false;
|
||||||
public boolean allowBooks = false;
|
public boolean allowBooks = false;
|
||||||
public boolean allowRecursion = true;
|
public boolean allowRecursion = true;
|
||||||
|
public int maxCustomData = 64;
|
||||||
|
|
||||||
public int globalMaxEnchant = 5;
|
public int globalMaxEnchant = 5;
|
||||||
public int maxMending = 1;
|
public int maxMending = 1;
|
||||||
public int maxUnbreaking = 3;
|
public int maxUnbreaking = 3;
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
package me.trouper.sentinel.server.events.violations.players;
|
package me.trouper.sentinel.server.events.violations.players;
|
||||||
|
|
||||||
import io.github.itzispyder.pdk.plugin.gui.CustomGui;
|
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.Sentinel;
|
||||||
import me.trouper.sentinel.data.storage.NBTStorage;
|
import me.trouper.sentinel.data.storage.NBTStorage;
|
||||||
import me.trouper.sentinel.server.events.violations.AbstractViolation;
|
import me.trouper.sentinel.server.events.violations.AbstractViolation;
|
||||||
import me.trouper.sentinel.server.functions.helpers.ActionConfiguration;
|
import me.trouper.sentinel.server.functions.helpers.ActionConfiguration;
|
||||||
import me.trouper.sentinel.server.functions.itemchecks.ItemCheck;
|
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.Items;
|
||||||
import me.trouper.sentinel.server.gui.MainGUI;
|
import me.trouper.sentinel.server.gui.MainGUI;
|
||||||
import me.trouper.sentinel.server.gui.config.AntiNukeGUI;
|
import me.trouper.sentinel.server.gui.config.AntiNukeGUI;
|
||||||
@@ -28,18 +30,36 @@ public class CreativeHotbar extends AbstractViolation {
|
|||||||
private void onNBTPull(InventoryCreativeEvent e) {
|
private void onNBTPull(InventoryCreativeEvent e) {
|
||||||
//ServerUtils.verbose("NBT: Detected creative mode action");
|
//ServerUtils.verbose("NBT: Detected creative mode action");
|
||||||
if (!Sentinel.getInstance().getDirector().io.violationConfig.creativeHotbarAction.enabled) return;
|
if (!Sentinel.getInstance().getDirector().io.violationConfig.creativeHotbarAction.enabled) return;
|
||||||
ServerUtils.verbose("NBT: Enabled");
|
//ServerUtils.verbose("NBT: Enabled");
|
||||||
if (!(e.getWhoClicked() instanceof Player p)) return;
|
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!
|
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();
|
ItemStack i = e.getCursor();
|
||||||
if (PlayerUtils.isTrusted(p)) return;
|
if (PlayerUtils.isTrusted(p)) return;
|
||||||
ServerUtils.verbose("NBT: Not trusted");
|
//ServerUtils.verbose("NBT: Not trusted");
|
||||||
if (e.getCursor().getItemMeta() == null) return;
|
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;
|
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;
|
if (new ItemCheck().passes(i)) return;
|
||||||
ServerUtils.verbose("NBT: Item doesn't pass, performing action");
|
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 -> {}
|
default -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ public class ActionConfiguration {
|
|||||||
this.punishmentCommands = builder.punishmentCommands;
|
this.punishmentCommands = builder.punishmentCommands;
|
||||||
this.logToDiscord = builder.logToDiscord;
|
this.logToDiscord = builder.logToDiscord;
|
||||||
this.actionNode = builder.actionNode;
|
this.actionNode = builder.actionNode;
|
||||||
// Removed the actions being run here to prevent double execution
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Player getPlayer() {
|
public Player getPlayer() {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package me.trouper.sentinel.server.functions.itemchecks;
|
|||||||
|
|
||||||
import de.tr7zw.changeme.nbtapi.NBT;
|
import de.tr7zw.changeme.nbtapi.NBT;
|
||||||
import me.trouper.sentinel.Sentinel;
|
import me.trouper.sentinel.Sentinel;
|
||||||
|
import me.trouper.sentinel.data.config.NBTConfig;
|
||||||
import me.trouper.sentinel.utils.InventoryUtils;
|
import me.trouper.sentinel.utils.InventoryUtils;
|
||||||
import me.trouper.sentinel.utils.ServerUtils;
|
import me.trouper.sentinel.utils.ServerUtils;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
@@ -17,6 +18,7 @@ public class ItemCheck extends AbstractCheck<ItemStack> {
|
|||||||
@Override
|
@Override
|
||||||
public boolean passes(ItemStack item) {
|
public boolean passes(ItemStack item) {
|
||||||
ServerUtils.verbose("Checking item: " + item.getType().name());
|
ServerUtils.verbose("Checking item: " + item.getType().name());
|
||||||
|
NBTConfig config = Sentinel.getInstance().getDirector().io.nbtConfig;
|
||||||
|
|
||||||
// No metadata? Nothing to check.
|
// No metadata? Nothing to check.
|
||||||
if (item.getItemMeta() == null) {
|
if (item.getItemMeta() == null) {
|
||||||
@@ -38,11 +40,11 @@ public class ItemCheck extends AbstractCheck<ItemStack> {
|
|||||||
// NBT-based checks (e.g. custom consumables/tools).
|
// NBT-based checks (e.g. custom consumables/tools).
|
||||||
var nbt = NBT.itemStackToNBT(item);
|
var nbt = NBT.itemStackToNBT(item);
|
||||||
var components = nbt.getCompound("components");
|
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.");
|
ServerUtils.verbose("Item is consumable and not allowed.");
|
||||||
return false;
|
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.");
|
ServerUtils.verbose("Item is custom tool and not allowed.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -148,32 +150,32 @@ public class ItemCheck extends AbstractCheck<ItemStack> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Name, lore, potion, attribute and enchantment checks.
|
// 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.");
|
ServerUtils.verbose("Custom names not allowed.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!Sentinel.getInstance().getDirector().io.nbtConfig.allowLore && meta.hasLore()) {
|
if (!config.allowLore && meta.hasLore()) {
|
||||||
ServerUtils.verbose("Custom lore not allowed.");
|
ServerUtils.verbose("Custom lore not allowed.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!Sentinel.getInstance().getDirector().io.nbtConfig.allowPotions &&
|
if (!config.allowPotions &&
|
||||||
(item.getType().equals(Material.POTION) ||
|
(item.getType().equals(Material.POTION) ||
|
||||||
item.getType().equals(Material.SPLASH_POTION) ||
|
item.getType().equals(Material.SPLASH_POTION) ||
|
||||||
item.getType().equals(Material.LINGERING_POTION))) {
|
item.getType().equals(Material.LINGERING_POTION))) {
|
||||||
ServerUtils.verbose("Potions not allowed.");
|
ServerUtils.verbose("Potions not allowed.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!Sentinel.getInstance().getDirector().io.nbtConfig.allowAttributes && meta.hasAttributeModifiers()) {
|
if (!config.allowAttributes && meta.hasAttributeModifiers()) {
|
||||||
ServerUtils.verbose("Attribute modifiers not allowed.");
|
ServerUtils.verbose("Attribute modifiers not allowed.");
|
||||||
return false;
|
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.");
|
ServerUtils.verbose("Illegal enchantments found.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Recursion check for use-remainder items.
|
// Recursion check for use-remainder items.
|
||||||
if (meta.hasUseRemainder()) {
|
if (meta.hasUseRemainder()) {
|
||||||
if (!Sentinel.getInstance().getDirector().io.nbtConfig.allowRecursion) {
|
if (!config.allowRecursion) {
|
||||||
ServerUtils.verbose("Recursion not allowed.");
|
ServerUtils.verbose("Recursion not allowed.");
|
||||||
return false;
|
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.events.violations.entities.CommandMinecartUse;
|
||||||
import me.trouper.sentinel.server.functions.chatfilter.profanity.ProfanityFilter;
|
import me.trouper.sentinel.server.functions.chatfilter.profanity.ProfanityFilter;
|
||||||
import me.trouper.sentinel.server.functions.chatfilter.spam.SpamFilter;
|
import me.trouper.sentinel.server.functions.chatfilter.spam.SpamFilter;
|
||||||
|
import me.trouper.sentinel.server.functions.itemchecks.RateLimitCheck;
|
||||||
import me.trouper.sentinel.utils.Text;
|
import me.trouper.sentinel.utils.Text;
|
||||||
import org.bukkit.Bukkit;
|
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(), SpamFilter::decayHeat,0, 20);
|
||||||
Bukkit.getScheduler().runTaskTimer(Sentinel.getInstance(), ProfanityFilter::decayScore,0,1200);
|
Bukkit.getScheduler().runTaskTimer(Sentinel.getInstance(), ProfanityFilter::decayScore,0,1200);
|
||||||
Bukkit.getScheduler().runTaskTimer(Sentinel.getInstance(), WandEvents::handleDisplay,0,1);
|
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();
|
if (Sentinel.getInstance().getDirector().io.mainConfig.backdoorDetection.enabled) Sentinel.getInstance().getDirector().backdoorDetection.init();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user