From 8a783597241852700a14491c4cf990d791fa4934 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis <geoand@gmail.com> Date: Mon, 11 Apr 2022 12:54:32 +0300 Subject: [PATCH] Avoid XStream causing illegal access issues for internal JDK collections Relates to: #24492 --- .../junit/internal/CustomListConverter.java | 43 +++++++++++++++++++ .../junit/internal/CustomSetConverter.java | 40 +++++++++++++++++ .../test/junit/internal/XStreamDeepClone.java | 3 +- 3 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 test-framework/junit5/src/main/java/io/quarkus/test/junit/internal/CustomListConverter.java create mode 100644 test-framework/junit5/src/main/java/io/quarkus/test/junit/internal/CustomSetConverter.java diff --git a/test-framework/junit5/src/main/java/io/quarkus/test/junit/internal/CustomListConverter.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/internal/CustomListConverter.java new file mode 100644 index 00000000000000..f934c0fd661288 --- /dev/null +++ b/test-framework/junit5/src/main/java/io/quarkus/test/junit/internal/CustomListConverter.java @@ -0,0 +1,43 @@ +package io.quarkus.test.junit.internal; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import com.thoughtworks.xstream.converters.collections.CollectionConverter; +import com.thoughtworks.xstream.mapper.Mapper; + +/** + * A custom List converter that always uses ArrayList for unmarshalling. + * This is probably not semantically correct 100% of the time, but it's likely fine + * for all the cases where we are using marshalling / unmarshalling. + * + * The reason for doing this is to avoid XStream causing illegal access issues + * for internal JDK lists + */ +public class CustomListConverter extends CollectionConverter { + + // if we wanted to be 100% sure, we'd list all the List.of methods, but I think it's pretty safe to say + // that the JDK won't add custom implementations for the other classes + private final Set<String> SUPPORTED_CLASS_NAMES = Set.of( + List.of().getClass().getName(), + List.of(new Object()).getClass().getName(), + Arrays.asList(new Object()).getClass().getName(), + Collections.emptyList().getClass().getName()); + + public CustomListConverter(Mapper mapper) { + super(mapper); + } + + @Override + public boolean canConvert(Class type) { + return (type != null) && SUPPORTED_CLASS_NAMES.contains(type.getName()); + } + + @Override + protected Object createCollection(Class type) { + return new ArrayList<>(); + } +} diff --git a/test-framework/junit5/src/main/java/io/quarkus/test/junit/internal/CustomSetConverter.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/internal/CustomSetConverter.java new file mode 100644 index 00000000000000..5510ceefdc4638 --- /dev/null +++ b/test-framework/junit5/src/main/java/io/quarkus/test/junit/internal/CustomSetConverter.java @@ -0,0 +1,40 @@ +package io.quarkus.test.junit.internal; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import com.thoughtworks.xstream.converters.collections.CollectionConverter; +import com.thoughtworks.xstream.mapper.Mapper; + +/** + * A custom Set converter that always uses HashSet for unmarshalling. + * This is probably not semantically correct 100% of the time, but it's likely fine + * for all the cases where we are using marshalling / unmarshalling. + * + * The reason for doing this is to avoid XStream causing illegal access issues + * for internal JDK sets + */ +public class CustomSetConverter extends CollectionConverter { + + // if we wanted to be 100% sure, we'd list all the Set.of methods, but I think it's pretty safe to say + // that the JDK won't add custom implementations for the other classes + private final Set<String> SUPPORTED_CLASS_NAMES = Set.of( + Set.of().getClass().getName(), + Set.of(new Object()).getClass().getName(), + Collections.emptySet().getClass().getName()); + + public CustomSetConverter(Mapper mapper) { + super(mapper); + } + + @Override + public boolean canConvert(Class type) { + return (type != null) && SUPPORTED_CLASS_NAMES.contains(type.getName()); + } + + @Override + protected Object createCollection(Class type) { + return new HashSet<>(); + } +} diff --git a/test-framework/junit5/src/main/java/io/quarkus/test/junit/internal/XStreamDeepClone.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/internal/XStreamDeepClone.java index dc945e5bf7f7c4..a200e0a8412659 100644 --- a/test-framework/junit5/src/main/java/io/quarkus/test/junit/internal/XStreamDeepClone.java +++ b/test-framework/junit5/src/main/java/io/quarkus/test/junit/internal/XStreamDeepClone.java @@ -15,9 +15,10 @@ class XStreamDeepClone implements DeepClone { // avoid doing any work eagerly since the cloner is rarely used xStreamSupplier = () -> { XStream result = new XStream(); - XStream.setupDefaultSecurity(result); result.allowTypesByRegExp(new String[] { ".*" }); result.setClassLoader(classLoader); + result.registerConverter(new CustomListConverter(result.getMapper())); + result.registerConverter(new CustomSetConverter(result.getMapper())); return result; }; }