From e745ff4721601dbe8075e05aec4c3ddc07ddfdf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20M=C3=BCller?= Date: Mon, 27 Oct 2025 17:10:44 +0100 Subject: [PATCH] added AntiFormattedBook to detect and sanitize illegal book formatting --- .../antiFormattedBook/AntiFormattedBook.java | 66 +++++++++++++++++++ .../antiFormattedBook/BookEditListener.java | 36 ++++++++++ 2 files changed, 102 insertions(+) create mode 100644 common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/security/antiFormattedBook/AntiFormattedBook.java create mode 100644 common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/security/antiFormattedBook/BookEditListener.java diff --git a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/security/antiFormattedBook/AntiFormattedBook.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/security/antiFormattedBook/AntiFormattedBook.java new file mode 100644 index 0000000..6af6d80 --- /dev/null +++ b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/security/antiFormattedBook/AntiFormattedBook.java @@ -0,0 +1,66 @@ +package eu.mhsl.craftattack.spawn.common.appliances.security.antiFormattedBook; + +import eu.mhsl.craftattack.spawn.core.appliance.Appliance; +import net.kyori.adventure.text.*; +import net.kyori.adventure.text.format.Style; +import net.kyori.adventure.text.format.TextColor; +import net.kyori.adventure.text.format.TextDecoration; +import org.bukkit.event.Listener; +import org.bukkit.inventory.meta.BookMeta; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Objects; + +public class AntiFormattedBook extends Appliance { + private static final char SECTION = '\u00A7'; + + public boolean containsFormatting(BookMeta meta) { + if (this.hasFormattingDeep(meta.title())) return true; + if (this.hasFormattingDeep(meta.author())) return true; + + for (Component c : meta.pages()) { + if (this.hasFormattingDeep(c)) return true; + } + return false; + } + + private boolean hasFormattingDeep(@Nullable Component component) { + if(component == null) return false; + if (this.hastFormatting(component)) return true; + + if (component instanceof TextComponent tc && tc.content().indexOf(SECTION) >= 0) return true; + + if (component instanceof NBTComponent nbt) { + if (nbt.separator() != null && this.hasFormattingDeep(nbt.separator())) return true; + } + + for (Component child : component.children()) { + if (this.hasFormattingDeep(child)) return true; + } + return false; + } + + private boolean hastFormatting(Component component) { + Style style = component.style(); + + TextColor color = style.color(); + if (color != null) return true; + if (style.font() != null) return true; + if (style.insertion() != null && !Objects.requireNonNull(style.insertion()).isEmpty()) return true; + + for (var decoration : style.decorations().entrySet()) { + if (decoration.getValue() == TextDecoration.State.TRUE) return true; + } + + return style.hoverEvent() != null || style.clickEvent() != null; + } + + @Override + protected @NotNull List listeners() { + return List.of( + new BookEditListener() + ); + } +} diff --git a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/security/antiFormattedBook/BookEditListener.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/security/antiFormattedBook/BookEditListener.java new file mode 100644 index 0000000..1fcf3c9 --- /dev/null +++ b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/security/antiFormattedBook/BookEditListener.java @@ -0,0 +1,36 @@ +package eu.mhsl.craftattack.spawn.common.appliances.security.antiFormattedBook; + +import eu.mhsl.craftattack.spawn.common.appliances.tooling.acInform.AcInform; +import eu.mhsl.craftattack.spawn.core.Main; +import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener; +import net.kyori.adventure.text.Component; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.player.PlayerEditBookEvent; +import org.bukkit.inventory.meta.BookMeta; + +import java.util.List; + +class BookEditListener extends ApplianceListener { + @EventHandler + public void onBookEdit(PlayerEditBookEvent event) { + Player player = event.getPlayer(); + BookMeta meta = event.getNewBookMeta(); + + if (this.getAppliance().containsFormatting(meta)) { + Main.instance().getAppliance(AcInform.class).notifyAdmins( + "internal", + player.getName(), + "illegalBookFormatting", + 1f + ); + + BookMeta sanitized = meta.clone(); + sanitized.title(null); + sanitized.author(null); + //noinspection ResultOfMethodCallIgnored + sanitized.pages(List.of(Component.empty())); + event.setNewBookMeta(sanitized); + } + } +}