From f6b3bd7e50e6e09fedddb98c61558c022ba31285 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?JB=20Onofr=C3=A9?= Date: Mon, 24 Jun 2024 22:36:51 +0200 Subject: [PATCH] AVRO-3985: Add trusted packages support in SpecificData (#2934) * AVRO-3985: Add trusted packages support in SpecificData * Apply suggestions from code review Co-authored-by: Martin Grigorov * Move to SecurityException * Remove redundant import --------- Co-authored-by: Fokko Driesprong Co-authored-by: Martin Grigorov --- .../org/apache/avro/reflect/ReflectData.java | 10 ---- .../avro/specific/SpecificDatumReader.java | 47 ++++++++++++++++++- 2 files changed, 46 insertions(+), 11 deletions(-) diff --git a/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectData.java b/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectData.java index 24173b9b273..0c0b10478a5 100644 --- a/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectData.java +++ b/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectData.java @@ -429,16 +429,6 @@ private FieldAccessor getFieldAccessor(Class c, String fieldName) { return null; } - /** @deprecated Replaced by {@link SpecificData#CLASS_PROP} */ - @Deprecated - static final String CLASS_PROP = "java-class"; - /** @deprecated Replaced by {@link SpecificData#KEY_CLASS_PROP} */ - @Deprecated - static final String KEY_CLASS_PROP = "java-key-class"; - /** @deprecated Replaced by {@link SpecificData#ELEMENT_PROP} */ - @Deprecated - static final String ELEMENT_PROP = "java-element-class"; - private static final Map CLASS_CACHE = new ConcurrentHashMap<>(); static Class getClassProp(Schema schema, String prop) { 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 d924c8e04b7..8950f165991 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 @@ -24,12 +24,25 @@ import org.apache.avro.io.ResolvingDecoder; import org.apache.avro.util.ClassUtils; import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; /** * {@link org.apache.avro.io.DatumReader DatumReader} for generated Java * classes. */ public class SpecificDatumReader extends GenericDatumReader { + + public static final String[] SERIALIZABLE_PACKAGES; + + static { + SERIALIZABLE_PACKAGES = System.getProperty("org.apache.avro.SERIALIZABLE_PACKAGES", + "java.lang,java.math,java.io,java.net,org.apache.avro.reflect").split(","); + } + + private final List trustedPackages = new ArrayList<>(); + public SpecificDatumReader() { this(null, null, SpecificData.get()); } @@ -55,6 +68,7 @@ public SpecificDatumReader(Schema writer, Schema reader) { */ public SpecificDatumReader(Schema writer, Schema reader, SpecificData data) { super(writer, reader, data); + trustedPackages.addAll(Arrays.asList(SERIALIZABLE_PACKAGES)); } /** Construct given a {@link SpecificData}. */ @@ -101,12 +115,43 @@ private Class getPropAsClass(Schema schema, String prop) { if (name == null) return null; try { - return ClassUtils.forName(getData().getClassLoader(), name); + Class clazz = ClassUtils.forName(getData().getClassLoader(), name); + checkSecurity(clazz); + return clazz; } catch (ClassNotFoundException e) { throw new AvroRuntimeException(e); } } + private boolean trustAllPackages() { + return (trustedPackages.size() == 1 && "*".equals(trustedPackages.get(0))); + } + + private void checkSecurity(Class clazz) throws ClassNotFoundException { + if (trustAllPackages() || clazz.isPrimitive()) { + return; + } + + boolean found = false; + Package thePackage = clazz.getPackage(); + if (thePackage != null) { + for (String trustedPackage : getTrustedPackages()) { + if (thePackage.getName().equals(trustedPackage) || thePackage.getName().startsWith(trustedPackage + ".")) { + found = true; + break; + } + } + if (!found) { + throw new SecurityException("Forbidden " + clazz + + "! This class is not trusted to be included in Avro schema using java-class. Please set org.apache.avro.SERIALIZABLE_PACKAGES system property with the packages you trust."); + } + } + } + + public final List getTrustedPackages() { + return trustedPackages; + } + @Override protected Object readRecord(Object old, Schema expected, ResolvingDecoder in) throws IOException { SpecificData data = getSpecificData();