From 971ff57496add7aa0d2a3b1785dea699d287606d Mon Sep 17 00:00:00 2001 From: Intelli Date: Wed, 22 Apr 2026 18:31:19 -0600 Subject: [PATCH] Added byte data sanitization to resolve NoSuchElementException (fixes #890) --- .../database/statement/EntityStatement.java | 2 +- .../net/coreprotect/utility/ItemUtils.java | 135 ++++++++++++++++-- 2 files changed, 121 insertions(+), 16 deletions(-) diff --git a/src/main/java/net/coreprotect/database/statement/EntityStatement.java b/src/main/java/net/coreprotect/database/statement/EntityStatement.java index 355e8d57..5fc0f03b 100644 --- a/src/main/java/net/coreprotect/database/statement/EntityStatement.java +++ b/src/main/java/net/coreprotect/database/statement/EntityStatement.java @@ -66,7 +66,7 @@ public class EntityStatement { resultSet.close(); } catch (Exception e) { // only display exception on development branch - if (!ConfigHandler.EDITION_BRANCH.contains("-dev")) { + if (ConfigHandler.EDITION_BRANCH.contains("-dev")) { e.printStackTrace(); } } diff --git a/src/main/java/net/coreprotect/utility/ItemUtils.java b/src/main/java/net/coreprotect/utility/ItemUtils.java index 7a9dc2f9..ab474d5c 100644 --- a/src/main/java/net/coreprotect/utility/ItemUtils.java +++ b/src/main/java/net/coreprotect/utility/ItemUtils.java @@ -1,13 +1,16 @@ package net.coreprotect.utility; import java.io.ByteArrayOutputStream; +import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.logging.Logger; import org.bukkit.Location; import org.bukkit.Material; @@ -31,6 +34,9 @@ import net.coreprotect.utility.serialize.ItemMetaHandler; public class ItemUtils { + private static final Object UNSERIALIZABLE_VALUE = new Object(); + private static final Logger LOGGER = Logger.getLogger("CoreProtect"); + private ItemUtils() { throw new IllegalStateException("Utility class"); } @@ -336,27 +342,126 @@ public class ItemUtils { } public static byte[] convertByteData(Object data) { - byte[] result = null; if (data == null) { - return result; + return null; } try { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - BukkitObjectOutputStream oos = new BukkitObjectOutputStream(bos); - oos.writeObject(data); - oos.flush(); - oos.close(); - bos.close(); - result = bos.toByteArray(); + return serializeByteData(data); } - catch (Exception e) { // only display exception on development branch - if (!ConfigHandler.EDITION_BRANCH.contains("-dev")) { - e.printStackTrace(); + catch (Exception initialException) { + Object sanitizedData = sanitizeByteData(data); + if (sanitizedData != UNSERIALIZABLE_VALUE) { + try { + byte[] result = serializeByteData(sanitizedData); + if (ConfigHandler.EDITION_BRANCH.contains("-dev")) { + LOGGER.warning("CoreProtect sanitized unsupported metadata during byte serialization; some nested values were omitted."); + } + return result; + } + catch (Exception sanitizedException) { + if (ConfigHandler.EDITION_BRANCH.contains("-dev")) { + sanitizedException.printStackTrace(); + } + return null; + } + } + + if (ConfigHandler.EDITION_BRANCH.contains("-dev")) { + initialException.printStackTrace(); } } - return result; + return null; + } + + private static byte[] serializeByteData(Object data) throws Exception { + try (ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); BukkitObjectOutputStream objectStream = new BukkitObjectOutputStream(byteStream)) { + objectStream.writeObject(data); + objectStream.flush(); + return byteStream.toByteArray(); + } + } + + private static boolean canSerializeByteData(Object data) { + try { + serializeByteData(data); + return true; + } + catch (Exception e) { + return false; + } + } + + private static Object sanitizeByteData(Object data) { + if (data == null || canSerializeByteData(data)) { + return data; + } + + if (data instanceof Map) { + Map sourceMap = (Map) data; + Map sanitizedMap = new LinkedHashMap<>(); + for (Map.Entry entry : sourceMap.entrySet()) { + Object sanitizedKey = sanitizeByteData(entry.getKey()); + Object sanitizedValue = sanitizeByteData(entry.getValue()); + if (sanitizedKey == UNSERIALIZABLE_VALUE || sanitizedValue == UNSERIALIZABLE_VALUE) { + continue; + } + + sanitizedMap.put(sanitizedKey, sanitizedValue); + } + + if (sanitizedMap.isEmpty() && !sourceMap.isEmpty()) { + return UNSERIALIZABLE_VALUE; + } + + return canSerializeByteData(sanitizedMap) ? sanitizedMap : UNSERIALIZABLE_VALUE; + } + + if (data instanceof List) { + List sourceList = (List) data; + List sanitizedList = new ArrayList<>(); + for (Object value : sourceList) { + Object sanitizedValue = sanitizeByteData(value); + if (sanitizedValue == UNSERIALIZABLE_VALUE) { + continue; + } + + sanitizedList.add(sanitizedValue); + } + + if (sanitizedList.isEmpty() && !sourceList.isEmpty()) { + return UNSERIALIZABLE_VALUE; + } + + return canSerializeByteData(sanitizedList) ? sanitizedList : UNSERIALIZABLE_VALUE; + } + + if (data.getClass().isArray()) { + int length = Array.getLength(data); + List sanitizedValues = new ArrayList<>(); + for (int index = 0; index < length; index++) { + Object sanitizedValue = sanitizeByteData(Array.get(data, index)); + if (sanitizedValue == UNSERIALIZABLE_VALUE) { + continue; + } + + sanitizedValues.add(sanitizedValue); + } + + if (sanitizedValues.isEmpty() && length > 0) { + return UNSERIALIZABLE_VALUE; + } + + Object sanitizedArray = Array.newInstance(data.getClass().getComponentType(), sanitizedValues.size()); + for (int index = 0; index < sanitizedValues.size(); index++) { + Array.set(sanitizedArray, index, sanitizedValues.get(index)); + } + + return canSerializeByteData(sanitizedArray) ? sanitizedArray : UNSERIALIZABLE_VALUE; + } + + return UNSERIALIZABLE_VALUE; } public static ItemMeta deserializeItemMeta(Class itemMetaClass, Map args) { @@ -365,7 +470,7 @@ public class ItemUtils { return (ItemMeta) org.bukkit.configuration.serialization.ConfigurationSerialization.deserializeObject(args, delegate.value()); } catch (Exception e) { // only display exception on development branch - if (!ConfigHandler.EDITION_BRANCH.contains("-dev")) { + if (ConfigHandler.EDITION_BRANCH.contains("-dev")) { e.printStackTrace(); } } @@ -456,4 +561,4 @@ public class ItemUtils { return result; } -} \ No newline at end of file +}