From 9c931f669c09f3c907214638f296073e7cce3c3b Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 2 Sep 2021 22:20:52 +0200 Subject: [PATCH] Convenient configuration of type permissions for XStream 1.4.18 Closes gh-27343 (cherry picked from commit 837301fdb3a92b1e40535a404dbdbf57b01c3816) --- .../oxm/xstream/XStreamMarshaller.java | 33 ++++++++++++++++--- .../oxm/xstream/XStreamMarshallerTests.java | 12 ++++--- .../oxm/xstream/XStreamUnmarshallerTests.java | 28 ++++++++++------ 3 files changed, 55 insertions(+), 18 deletions(-) diff --git a/spring-oxm/src/main/java/org/springframework/oxm/xstream/XStreamMarshaller.java b/spring-oxm/src/main/java/org/springframework/oxm/xstream/XStreamMarshaller.java index 881529d211..f7b9b0e510 100644 --- a/spring-oxm/src/main/java/org/springframework/oxm/xstream/XStreamMarshaller.java +++ b/spring-oxm/src/main/java/org/springframework/oxm/xstream/XStreamMarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -64,6 +64,8 @@ import com.thoughtworks.xstream.io.xml.XppDriver; import com.thoughtworks.xstream.mapper.CannotResolveClassException; import com.thoughtworks.xstream.mapper.Mapper; import com.thoughtworks.xstream.mapper.MapperWrapper; +import com.thoughtworks.xstream.security.ForbiddenClassException; +import com.thoughtworks.xstream.security.TypePermission; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; @@ -106,7 +108,7 @@ import org.springframework.util.xml.StaxUtils; * Therefore, it has limited namespace support. As such, it is rather unsuitable for * usage within Web Services. * - *

This marshaller requires XStream 1.4.5 or higher, as of Spring 4.3. + *

This marshaller requires XStream 1.4.7 or higher, as of Spring 5.2.17. * Note that {@link XStream} construction has been reworked in 4.0, with the * stream driver and the class loader getting passed into XStream itself now. * @@ -146,6 +148,9 @@ public class XStreamMarshaller extends AbstractMarshaller implements BeanClassLo @Nullable private ConverterMatcher[] converters; + @Nullable + private TypePermission[] typePermissions; + @Nullable private MarshallingStrategy marshallingStrategy; @@ -268,6 +273,20 @@ public class XStreamMarshaller extends AbstractMarshaller implements BeanClassLo this.converters = converters; } + /** + * Set XStream type permissions such as + * {@link com.thoughtworks.xstream.security.AnyTypePermission}, + * {@link com.thoughtworks.xstream.security.ExplicitTypePermission} etc, + * as an alternative to overriding the {@link #customizeXStream} method. + *

Note: As of XStream 1.4.18, the default type permissions are + * restricted to well-known core JDK types. For any custom types, + * explicit type permissions need to be registered. + * @since 5.2.17 + */ + public void setTypePermissions(TypePermission... typePermissions) { + this.typePermissions = typePermissions; + } + /** * Set a custom XStream {@link MarshallingStrategy} to use. * @since 4.0 @@ -407,7 +426,7 @@ public class XStreamMarshaller extends AbstractMarshaller implements BeanClassLo @Override public void afterPropertiesSet() { - // no-op due to use of SingletonSupplier for the XStream field. + // no-op due to use of SingletonSupplier for the XStream field } /** @@ -479,6 +498,12 @@ public class XStreamMarshaller extends AbstractMarshaller implements BeanClassLo } } + if (this.typePermissions != null) { + for (TypePermission permission : this.typePermissions) { + xstream.addPermission(permission); + } + } + if (this.marshallingStrategy != null) { xstream.setMarshallingStrategy(this.marshallingStrategy); } @@ -844,7 +869,7 @@ public class XStreamMarshaller extends AbstractMarshaller implements BeanClassLo */ protected XmlMappingException convertXStreamException(Exception ex, boolean marshalling) { if (ex instanceof StreamException || ex instanceof CannotResolveClassException || - ex instanceof ConversionException) { + ex instanceof ForbiddenClassException || ex instanceof ConversionException) { if (marshalling) { return new MarshallingFailureException("XStream marshalling exception", ex); } diff --git a/spring-oxm/src/test/java/org/springframework/oxm/xstream/XStreamMarshallerTests.java b/spring-oxm/src/test/java/org/springframework/oxm/xstream/XStreamMarshallerTests.java index 5f05936d75..304d3fcc77 100644 --- a/spring-oxm/src/test/java/org/springframework/oxm/xstream/XStreamMarshallerTests.java +++ b/spring-oxm/src/test/java/org/springframework/oxm/xstream/XStreamMarshallerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,6 +43,7 @@ import com.thoughtworks.xstream.io.HierarchicalStreamWriter; import com.thoughtworks.xstream.io.json.JettisonMappedXmlDriver; import com.thoughtworks.xstream.io.json.JsonHierarchicalStreamDriver; import com.thoughtworks.xstream.io.json.JsonWriter; +import com.thoughtworks.xstream.security.AnyTypePermission; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.InOrder; @@ -67,18 +68,21 @@ import static org.mockito.Mockito.mock; /** * @author Arjen Poutsma * @author Sam Brannen + * @author Juergen Hoeller */ class XStreamMarshallerTests { private static final String EXPECTED_STRING = "42"; - private final XStreamMarshaller marshaller = new XStreamMarshaller(); - private final Flight flight = new Flight(); + private XStreamMarshaller marshaller; + @BeforeEach void createMarshaller() { + marshaller = new XStreamMarshaller(); + marshaller.setTypePermissions(AnyTypePermission.ANY); marshaller.setAliases(Collections.singletonMap("flight", Flight.class.getName())); flight.setFlightNumber(42L); } @@ -143,7 +147,7 @@ class XStreamMarshallerTests { ByteArrayOutputStream os = new ByteArrayOutputStream(); StreamResult result = new StreamResult(os); marshaller.marshal(flight, result); - String s = new String(os.toByteArray(), "UTF-8"); + String s = os.toString("UTF-8"); assertThat(XmlContent.of(s)).isSimilarTo(EXPECTED_STRING); } diff --git a/spring-oxm/src/test/java/org/springframework/oxm/xstream/XStreamUnmarshallerTests.java b/spring-oxm/src/test/java/org/springframework/oxm/xstream/XStreamUnmarshallerTests.java index 1c864545aa..7c87eda225 100644 --- a/spring-oxm/src/test/java/org/springframework/oxm/xstream/XStreamUnmarshallerTests.java +++ b/spring-oxm/src/test/java/org/springframework/oxm/xstream/XStreamUnmarshallerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ package org.springframework.oxm.xstream; import java.io.ByteArrayInputStream; import java.io.StringReader; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; @@ -29,6 +30,7 @@ import javax.xml.transform.Source; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamSource; +import com.thoughtworks.xstream.security.AnyTypePermission; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.w3c.dom.Document; @@ -40,6 +42,7 @@ import static org.assertj.core.api.Assertions.assertThat; /** * @author Arjen Poutsma + * @author Juergen Hoeller */ public class XStreamUnmarshallerTests { @@ -47,21 +50,16 @@ public class XStreamUnmarshallerTests { private XStreamMarshaller unmarshaller; + @BeforeEach - public void createUnmarshaller() throws Exception { + public void createUnmarshaller() { unmarshaller = new XStreamMarshaller(); + unmarshaller.setTypePermissions(AnyTypePermission.ANY); Map> aliases = new HashMap<>(); aliases.put("flight", Flight.class); unmarshaller.setAliases(aliases); } - private void testFlight(Object o) { - boolean condition = o instanceof Flight; - assertThat(condition).as("Unmarshalled object is not Flights").isTrue(); - Flight flight = (Flight) o; - assertThat(flight).as("Flight is null").isNotNull(); - assertThat(flight.getFlightNumber()).as("Number is invalid").isEqualTo(42L); - } @Test public void unmarshalDomSource() throws Exception { @@ -83,7 +81,7 @@ public class XStreamUnmarshallerTests { @Test public void unmarshalStreamSourceInputStream() throws Exception { - StreamSource source = new StreamSource(new ByteArrayInputStream(INPUT_STRING.getBytes("UTF-8"))); + StreamSource source = new StreamSource(new ByteArrayInputStream(INPUT_STRING.getBytes(StandardCharsets.UTF_8))); Object flights = unmarshaller.unmarshal(source); testFlight(flights); } @@ -94,5 +92,15 @@ public class XStreamUnmarshallerTests { Object flights = unmarshaller.unmarshal(source); testFlight(flights); } + + + private void testFlight(Object o) { + boolean condition = o instanceof Flight; + assertThat(condition).as("Unmarshalled object is not Flights").isTrue(); + Flight flight = (Flight) o; + assertThat(flight).as("Flight is null").isNotNull(); + assertThat(flight.getFlightNumber()).as("Number is invalid").isEqualTo(42L); + } + }