From 77a81a3d0b9287ca9d3a456d19af13b555826a6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?JB=20Onofr=C3=A9?= Date: Sat, 1 Jun 2024 09:22:32 +0200 Subject: [PATCH 1/4] AVRO-3985: Add trusted packages support in SpecificData --- .../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..10603b17c60 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 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 && trustedPackages.get(0).equals("*")); + } + + 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 ClassNotFoundException("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 List getTrustedPackages() { + return trustedPackages; + } + @Override protected Object readRecord(Object old, Schema expected, ResolvingDecoder in) throws IOException { SpecificData data = getSpecificData(); From 27b7afb663cd50a1ec533b31ee2493c10e4bf867 Mon Sep 17 00:00:00 2001 From: Fokko Driesprong Date: Wed, 19 Jun 2024 18:21:41 +0200 Subject: [PATCH 2/4] Apply suggestions from code review Co-authored-by: Martin Grigorov --- .../java/org/apache/avro/specific/SpecificDatumReader.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 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 10603b17c60..28ea56e5606 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,6 +24,7 @@ import org.apache.avro.io.ResolvingDecoder; import org.apache.avro.util.ClassUtils; import java.io.IOException; +import java.lang.SecurityException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -41,7 +42,7 @@ public class SpecificDatumReader extends GenericDatumReader { "java.lang,java.math,java.io,java.net,org.apache.avro.reflect").split(","); } - private List trustedPackages = new ArrayList<>(); + private final List trustedPackages = new ArrayList<>(); public SpecificDatumReader() { this(null, null, SpecificData.get()); @@ -124,7 +125,7 @@ private Class getPropAsClass(Schema schema, String prop) { } private boolean trustAllPackages() { - return (trustedPackages.size() == 1 && trustedPackages.get(0).equals("*")); + return (trustedPackages.size() == 1 && "*".equals(trustedPackages.get(0))); } private void checkSecurity(Class clazz) throws ClassNotFoundException { @@ -148,7 +149,7 @@ private void checkSecurity(Class clazz) throws ClassNotFoundException { } } - public List getTrustedPackages() { + public final List getTrustedPackages() { return trustedPackages; } From 68df6e1b7946b46ab06691b66c45e771fa225247 Mon Sep 17 00:00:00 2001 From: Fokko Driesprong Date: Wed, 19 Jun 2024 18:23:50 +0200 Subject: [PATCH 3/4] Move to SecurityException --- .../main/java/org/apache/avro/specific/SpecificDatumReader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 28ea56e5606..54a107b4c1c 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 @@ -143,7 +143,7 @@ private void checkSecurity(Class clazz) throws ClassNotFoundException { } } if (!found) { - throw new ClassNotFoundException("Forbidden " + clazz + 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."); } } From 77f25ed681acaf8bf2e674bdf22fd928768cda7c Mon Sep 17 00:00:00 2001 From: Fokko Driesprong Date: Wed, 19 Jun 2024 18:30:32 +0200 Subject: [PATCH 4/4] Remove redundant import --- .../main/java/org/apache/avro/specific/SpecificDatumReader.java | 1 - 1 file changed, 1 deletion(-) 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 54a107b4c1c..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,7 +24,6 @@ import org.apache.avro.io.ResolvingDecoder; import org.apache.avro.util.ClassUtils; import java.io.IOException; -import java.lang.SecurityException; import java.util.ArrayList; import java.util.Arrays; import java.util.List;