From f2609c6185277777da5d7da204897302bad6a39d Mon Sep 17 00:00:00 2001 From: Oscar WvH-K Date: Sun, 10 Aug 2025 15:53:10 +0200 Subject: [PATCH] java-[key-]class allowed packages must be packages --- .../avro/specific/SpecificDatumReader.java | 34 +++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/lang/java/avro/src/main/java/org/apache/avro/specific/SpecificDatumReader.java b/lang/java/avro/src/main/java/org/apache/avro/specific/SpecificDatumReader.java index 48d4420e750..a6ba6550f4e 100644 --- a/lang/java/avro/src/main/java/org/apache/avro/specific/SpecificDatumReader.java +++ b/lang/java/avro/src/main/java/org/apache/avro/specific/SpecificDatumReader.java @@ -30,6 +30,7 @@ import java.util.List; import java.util.HashSet; import java.util.Set; +import java.util.stream.Stream; /** * {@link org.apache.avro.io.DatumReader DatumReader} for generated Java @@ -38,7 +39,7 @@ public class SpecificDatumReader extends GenericDatumReader { /** - * @deprecated prefer to use SERIALIZABLE_CLASSES instead. + * @deprecated prefer to use {@link #SERIALIZABLE_CLASSES} instead. */ @Deprecated public static final String[] SERIALIZABLE_PACKAGES; @@ -47,12 +48,29 @@ public class SpecificDatumReader extends GenericDatumReader { static { // no serializable classes by default - String serializableClassesProp = System.getProperty("org.apache.avro.SERIALIZABLE_CLASSES"); - SERIALIZABLE_CLASSES = (serializableClassesProp == null) ? new String[0] : serializableClassesProp.split(","); + SERIALIZABLE_CLASSES = streamPropertyEntries(System.getProperty("org.apache.avro.SERIALIZABLE_CLASSES")) + .toArray(String[]::new); // no serializable packages by default - String serializablePackagesProp = System.getProperty("org.apache.avro.SERIALIZABLE_PACKAGES"); - SERIALIZABLE_PACKAGES = (serializablePackagesProp == null) ? new String[0] : serializablePackagesProp.split(","); + SERIALIZABLE_PACKAGES = streamPropertyEntries(System.getProperty("org.apache.avro.SERIALIZABLE_PACKAGES")) + // Add a '.' suffix to ensure we'll be matching package names instead of + // arbitrary prefixes, except for the wildcard "*", which allows all + // packages (this is only safe in fully controlled environments!). + .map(entry -> "*".equals(entry) ? entry : entry + ".").toArray(String[]::new); + } + + /** + * Parse a comma separated list into non-empty entries. Leading and trailing + * whitespace is stripped. + * + * @param commaSeparatedEntries the comma separated list of entries + * @return a stream of the entries + */ + private static Stream streamPropertyEntries(String commaSeparatedEntries) { + if (commaSeparatedEntries == null) { + return Stream.empty(); + } + return Stream.of(commaSeparatedEntries.split(",")).map(String::strip).filter(s -> !s.isEmpty()); } // The primitive "class names" based on Class.isPrimitive() @@ -167,8 +185,10 @@ private void checkSecurity(String className) throws ClassNotFoundException { } } - throw new SecurityException("Forbidden " + className - + "! This class is not trusted to be included in Avro schema using java-class. Please set org.apache.avro.SERIALIZABLE_CLASSES system property with the class you trust or org.apache.avro.SERIALIZABLE_PACKAGES system property with the packages you trust."); + throw new SecurityException("Forbidden " + className + "! This class is not trusted to be included in Avro " + + "schemas using java-class. Please set the system property org.apache.avro.SERIALIZABLE_CLASSES to the comma " + + "separated list of classes you trust. You can also set the system property " + + "org.apache.avro.SERIALIZABLE_PACKAGES to the comma separated list of the packages you trust."); } /**