Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AVRO-3985: Add trusted packages support in SpecificData #2934

Merged
merged 4 commits into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, Class> CLASS_CACHE = new ConcurrentHashMap<>();

static Class getClassProp(Schema schema, String prop) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,25 @@
import org.apache.avro.io.ResolvingDecoder;
import org.apache.avro.util.ClassUtils;
import java.io.IOException;
import java.util.ArrayList;
Fokko marked this conversation as resolved.
Show resolved Hide resolved
import java.util.Arrays;
import java.util.List;

/**
* {@link org.apache.avro.io.DatumReader DatumReader} for generated Java
* classes.
*/
public class SpecificDatumReader<T> extends GenericDatumReader<T> {

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<String> trustedPackages = new ArrayList<>();

public SpecificDatumReader() {
this(null, null, SpecificData.get());
}
Expand All @@ -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}. */
Expand Down Expand Up @@ -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.");
martin-g marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

public final List<String> getTrustedPackages() {
return trustedPackages;
}

@Override
protected Object readRecord(Object old, Schema expected, ResolvingDecoder in) throws IOException {
SpecificData data = getSpecificData();
Expand Down
Loading