refined SignEditListener to filter illegal characters more robustly by normalizing input and introducing specific character whitelists

This commit is contained in:
2025-12-22 21:41:34 +01:00
parent 71f2da8e99
commit 89c1c4335b

View File

@@ -6,18 +6,73 @@ import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.block.SignChangeEvent; import org.bukkit.event.block.SignChangeEvent;
import java.text.Normalizer;
import java.util.Set;
class SignEditListener extends ApplianceListener<AntiIllegalSignCharacters> { class SignEditListener extends ApplianceListener<AntiIllegalSignCharacters> {
private static final Set<Integer> ALLOWED_CHARS = Set.of(
(int)' ', (int)'.', (int)',', (int)';', (int)':', (int)'!', (int)'?',
(int)'"', (int)'\'',
(int)'(', (int)')', (int)'[', (int)']', (int)'{', (int)'}',
(int)'-', (int)'_', (int)'+', (int)'=', (int)'/', (int)'\\',
(int)'@', (int)'#', (int)'$', (int)'%', (int)'&', (int)'*',
(int)'<', (int)'>', (int)'|',
(int)'~', (int)'`', (int)'^'
);
private static final Set<Integer> ALLOWED_EXTRA = Set.of(
(int)'Ä', (int)'Ö', (int)'Ü', (int)'ä', (int)'ö', (int)'ü', (int)'ß',
(int)'€', (int)'°', (int)'µ'
);
@EventHandler @EventHandler
public void onSignEdit(SignChangeEvent event) { public void onSignEdit(SignChangeEvent event) {
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
Component line = event.line(i); Component line = event.line(i);
if(line == null) continue; if (line == null) continue;
String lineStr = PlainTextComponentSerializer.plainText().serialize(line); String plainString = PlainTextComponentSerializer.plainText().serialize(line);
plainString = Normalizer.normalize(plainString, Normalizer.Form.NFC);
if (!lineStr.matches("^[ -~]*$")) { String cleaned = filterAllowed(plainString);
String cleaned = lineStr.replaceAll("[^ -~]", "");
event.line(i, Component.text(cleaned)); event.line(i, Component.text(cleaned));
} }
} }
private static String filterAllowed(String s) {
StringBuilder out = new StringBuilder(s.length());
for (int off = 0; off < s.length(); ) {
int cp = s.codePointAt(off);
off += Character.charCount(cp);
if (isForbidden(cp)) continue;
if (Character.isLetterOrDigit(cp)) {
out.appendCodePoint(cp);
continue;
}
if (ALLOWED_CHARS.contains(cp) || ALLOWED_EXTRA.contains(cp)) {
out.appendCodePoint(cp);
}
}
return out.toString();
}
private static boolean isForbidden(int cp) {
// Surrogates / invalid
if (cp >= 0xD800 && cp <= 0xDFFF) return true;
// Private Use Area (Mod/Pack-Icons/Placeholder)
if (cp >= 0xE000 && cp <= 0xF8FF) return true;
// Zero-width and control characters
if (cp == 0x200B || cp == 0x200C || cp == 0x200D || cp == 0xFEFF) return true;
// BiDi-Steuerzeichen
if (cp >= 0x202A && cp <= 0x202E) return true;
if (cp >= 0x2066 && cp <= 0x2069) return true;
return cp == '§';
} }
} }