From 84bed9e479933b0b512e021ffceddba7aa945dca Mon Sep 17 00:00:00 2001 From: fourbites Date: Fri, 20 Mar 2026 20:53:26 +0100 Subject: [PATCH] Class check cleanup --- .../com/threerings/io/ObjectInputStream.java | 60 +++++++++++-------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/core/src/main/java/com/threerings/io/ObjectInputStream.java b/core/src/main/java/com/threerings/io/ObjectInputStream.java index 6b0a5326c..c12330d5c 100644 --- a/core/src/main/java/com/threerings/io/ObjectInputStream.java +++ b/core/src/main/java/com/threerings/io/ObjectInputStream.java @@ -246,32 +246,8 @@ public class ObjectInputStream extends DataInputStream protected ClassMapping createClassMapping (short code, String cname) throws IOException, ClassNotFoundException { - // validate the class name against the whitelist before loading if (_allowedPrefixes != null) { - // strip array encoding to get the component class name - // e.g. "[Lcom.threerings.Foo;" -> "com.threerings.Foo" - String checkName = cname; - while (checkName.startsWith("[")) { - checkName = checkName.substring(1); - } - if (checkName.startsWith("L") && checkName.endsWith(";")) { - checkName = checkName.substring(1, checkName.length() - 1); - } - - // primitive array descriptors (I, Z, B, S, C, J, F, D) are always safe - boolean allowed = (checkName.length() == 1); - for (String prefix : _allowedPrefixes) { - if (checkName.startsWith(prefix)) { - allowed = true; - break; - } - } - if (!allowed) { - log.warning("Blocked deserialization of non-whitelisted class", - "code", code, "class", cname); - throw new IOException( - "Deserialization of class not allowed: " + cname); - } + validateClassPrefix(code, cname); } // resolve the class and streamer @@ -293,6 +269,40 @@ public class ObjectInputStream extends DataInputStream return new ClassMapping(code, sclass, streamer); } + /** + * Validates that a class name is allowed by the configured prefix whitelist. Array prefixes + * ({@code [}) are stripped first. Primitive type descriptors ({@code I}, {@code Z}, etc.) are + * always allowed. Object type descriptors ({@code Lcom.threerings.Foo;}) are checked against + * the whitelist. + */ + protected void validateClassPrefix (short code, String cname) + throws IOException + { + // strip array prefixes to get the component type descriptor + int start = 0; + while (start < cname.length() && cname.charAt(start) == '[') { + start++; + } + String desc = cname.substring(start); + + // primitive descriptors (I, Z, B, S, C, J, F, D) are always safe + if (!desc.startsWith("L") || !desc.endsWith(";")) { + return; + } + + // extract the class name from "Lcom.threerings.Foo;" + String className = desc.substring(1, desc.length() - 1); + for (String prefix : _allowedPrefixes) { + if (className.startsWith(prefix)) { + return; + } + } + + log.warning("Blocked deserialization of non-whitelisted class", + "code", code, "class", cname); + throw new IOException("Deserialization of class not allowed: " + cname); + } + /** * Reads an object from the input stream that was previously written with {@link * ObjectOutputStream#writeBareObject(Object)}.