From 57082ccd6480c3d0867a4e4a6ef4317b357ba112 Mon Sep 17 00:00:00 2001 From: Ben Date: Sun, 11 Oct 2026 10:33:03 -0600 Subject: [PATCH 1/3] Expose terminal-only cleanup for external voting site display values --- .../bencodez/votingplugin/util/ServiceSiteValidator.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/VotingPlugin/src/main/java/com/bencodez/votingplugin/util/ServiceSiteValidator.java b/VotingPlugin/src/main/java/com/bencodez/votingplugin/util/ServiceSiteValidator.java index 44110fcdd4..b48cb49b9f 100644 --- a/VotingPlugin/src/main/java/com/bencodez/votingplugin/util/ServiceSiteValidator.java +++ b/VotingPlugin/src/main/java/com/bencodez/votingplugin/util/ServiceSiteValidator.java @@ -83,6 +83,15 @@ public static String inertForFormatting(String value) { return inert.append(FORMATTING_BOUNDARY).toString(); } + /** + * Restores the literal display value after all placeholder and formatting + * parsers have run. Do not call this on an intermediate template: it would + * allow untrusted identifiers to complete formatting tokens again. + */ + public static String literalAfterFormatting(String value) { + return value == null ? null : value.replace(FORMATTING_BOUNDARY, ""); + } + private static boolean isDisallowed(int codePoint) { if (codePoint == '[' || codePoint == ']' || codePoint == '\'' || codePoint == '"' || codePoint == '`' || codePoint == '\u00A7' From b536eb204b13de7e24733e3b9888a712279bb447 Mon Sep 17 00:00:00 2001 From: Ben Date: Sun, 11 Oct 2026 10:33:14 -0600 Subject: [PATCH 2/3] Hide inert name separators at NeoForge's final literal message sink --- .../votingplugin/neoforge/NeoForgeNativeRewardActions.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/VotingPlugin/src/main/java/com/bencodez/votingplugin/neoforge/NeoForgeNativeRewardActions.java b/VotingPlugin/src/main/java/com/bencodez/votingplugin/neoforge/NeoForgeNativeRewardActions.java index 9abafbcc25..aefe0e40db 100644 --- a/VotingPlugin/src/main/java/com/bencodez/votingplugin/neoforge/NeoForgeNativeRewardActions.java +++ b/VotingPlugin/src/main/java/com/bencodez/votingplugin/neoforge/NeoForgeNativeRewardActions.java @@ -105,7 +105,10 @@ private static Object commandCallback(Object proxy, Method method, Object[] argu private static void sendMessage(Object player, String message) throws ReflectiveOperationException { ClassLoader loader = player.getClass().getClassLoader(); Class componentType = Class.forName("net.minecraft.network.chat.Component", false, loader); - Object component = componentType.getMethod("literal", String.class).invoke(null, message); + // This is a native literal component: all replacement and validation has + // already finished, so discard guards only at the final display sink. + Object component = componentType.getMethod("literal", String.class) + .invoke(null, com.bencodez.votingplugin.util.ServiceSiteValidator.literalAfterFormatting(message)); Method send = findAssignableMethod(player.getClass(), "sendSystemMessage", componentType); invokeMessage(player, send, component); } From dec96b7721a4342b83a05567ae0e0e4cdf6603ba Mon Sep 17 00:00:00 2001 From: Ben Date: Sun, 11 Oct 2026 10:33:29 -0600 Subject: [PATCH 3/3] Test exact display labels after NeoForge's final literal formatting --- .../votingplugin/tests/ServiceSiteValidatorTest.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/VotingPlugin/src/test/java/com/bencodez/votingplugin/tests/ServiceSiteValidatorTest.java b/VotingPlugin/src/test/java/com/bencodez/votingplugin/tests/ServiceSiteValidatorTest.java index 0b5aa35ba7..be45e2d1eb 100644 --- a/VotingPlugin/src/test/java/com/bencodez/votingplugin/tests/ServiceSiteValidatorTest.java +++ b/VotingPlugin/src/test/java/com/bencodez/votingplugin/tests/ServiceSiteValidatorTest.java @@ -48,6 +48,16 @@ void formattingGuardPreventsTokensAcrossTemplateBoundaries() { assertEquals(value, guarded.replace("\u2060", "")); } + @Test + void finalLiteralMessageRemovesAllFormattingGuards() { + for (String site : new String[] { "Minecraft Menu", "R&D", "&aGreen", + "%player_name%", "https://example.com/vote?id=1&source=proxy" }) { + String guarded = ServiceSiteValidator.inertForFormatting(site); + assertEquals(site, ServiceSiteValidator.literalAfterFormatting(guarded)); + assertFalse(ServiceSiteValidator.literalAfterFormatting(guarded).contains("\u2060")); + } + } + @Test void rejectsMissingAndOversizedNames() { assertFalse(ServiceSiteValidator.isValid(null));