From 11d0aa1b396d8819a5a0d4933c5cfc72fe9ad102 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Wed, 11 Apr 2018 17:21:46 +0200 Subject: [PATCH] DATACMNS-1292 - Improved default setup of XMLBeam. We now explicitly disable entity expansion in the DocumentBuilderFactory used by XMLBeam. Introduced constructor in XmlBeamHttpMessageConverter to allow dedicated configuration of an XBProjector instance in case the defaults need tweaking and augmented the web configuration setup to automatically pick up a custom XmlBeamHttpMessageConverter bean instead of our own default if present. --- .../data/web/XmlBeamHttpMessageConverter.java | 53 ++++++++++++++++++- .../config/SpringDataWebConfiguration.java | 12 +++-- .../XmlBeamHttpMessageConverterUnitTests.java | 21 ++++++++ template.mf | 2 + 4 files changed, 83 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/springframework/data/web/XmlBeamHttpMessageConverter.java b/src/main/java/org/springframework/data/web/XmlBeamHttpMessageConverter.java index be0dc7992..5d3cb0d9a 100644 --- a/src/main/java/org/springframework/data/web/XmlBeamHttpMessageConverter.java +++ b/src/main/java/org/springframework/data/web/XmlBeamHttpMessageConverter.java @@ -18,6 +18,8 @@ package org.springframework.data.web; import java.io.IOException; import java.util.Map; +import javax.xml.parsers.DocumentBuilderFactory; + import org.springframework.core.ResolvableType; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.http.HttpInputMessage; @@ -27,8 +29,11 @@ import org.springframework.http.converter.AbstractHttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.http.converter.HttpMessageNotWritableException; +import org.springframework.util.Assert; import org.springframework.util.ConcurrentReferenceHashMap; +import org.xml.sax.SAXParseException; import org.xmlbeam.XBProjector; +import org.xmlbeam.config.DefaultXMLFactoriesConfig; /** * A read-only {@link HttpMessageConverter} to create XMLBeam-based projection instances for interfaces. @@ -47,9 +52,39 @@ public class XmlBeamHttpMessageConverter extends AbstractHttpMessageConverter clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { - return projectionFactory.io().stream(inputMessage.getBody()).read(clazz); + + try { + + return projectionFactory.io().stream(inputMessage.getBody()).read(clazz); + + } catch (RuntimeException o_O) { + + Throwable cause = o_O.getCause(); + + if (SAXParseException.class.isInstance(cause)) { + throw new HttpMessageNotReadableException("Cannot read input message!", cause); + } else { + throw o_O; + } + } } /* diff --git a/src/main/java/org/springframework/data/web/config/SpringDataWebConfiguration.java b/src/main/java/org/springframework/data/web/config/SpringDataWebConfiguration.java index ef4c6f7f3..955b251e3 100644 --- a/src/main/java/org/springframework/data/web/config/SpringDataWebConfiguration.java +++ b/src/main/java/org/springframework/data/web/config/SpringDataWebConfiguration.java @@ -53,8 +53,9 @@ import com.fasterxml.jackson.databind.ObjectMapper; @Configuration public class SpringDataWebConfiguration extends WebMvcConfigurerAdapter { - @Autowired private ApplicationContext context; - @Autowired @Qualifier("mvcConversionService") ObjectFactory conversionService; + private @Autowired ApplicationContext context; + private @Autowired @Qualifier("mvcConversionService") ObjectFactory conversionService; + private @Autowired(required = false) XmlBeamHttpMessageConverter xmlBeamHttpMessageConverter; /* * (non-Javadoc) @@ -133,7 +134,12 @@ public class SpringDataWebConfiguration extends WebMvcConfigurerAdapter { } if (ClassUtils.isPresent("org.xmlbeam.XBProjector", context.getClassLoader())) { - converters.add(0, new XmlBeamHttpMessageConverter()); + + XmlBeamHttpMessageConverter converter = xmlBeamHttpMessageConverter == null // + ? new XmlBeamHttpMessageConverter() // + : xmlBeamHttpMessageConverter; + + converters.add(0, converter); } } diff --git a/src/test/java/org/springframework/data/web/XmlBeamHttpMessageConverterUnitTests.java b/src/test/java/org/springframework/data/web/XmlBeamHttpMessageConverterUnitTests.java index 86edcd9ab..b58fca86b 100644 --- a/src/test/java/org/springframework/data/web/XmlBeamHttpMessageConverterUnitTests.java +++ b/src/test/java/org/springframework/data/web/XmlBeamHttpMessageConverterUnitTests.java @@ -22,13 +22,18 @@ import static org.mockito.Mockito.*; import java.io.ByteArrayInputStream; import java.io.IOException; +import org.hamcrest.Matchers; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import org.springframework.data.web.ProjectingJackson2HttpMessageConverterUnitTests.UnannotatedInterface; import org.springframework.http.HttpInputMessage; import org.springframework.http.MediaType; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.xml.sax.SAXParseException; import org.xmlbeam.annotation.XBRead; /** @@ -40,6 +45,8 @@ import org.xmlbeam.annotation.XBRead; @RunWith(MockitoJUnitRunner.class) public class XmlBeamHttpMessageConverterUnitTests { + public @Rule ExpectedException exception = ExpectedException.none(); + XmlBeamHttpMessageConverter converter = new XmlBeamHttpMessageConverter(); @Mock HttpInputMessage message; @@ -88,6 +95,20 @@ public class XmlBeamHttpMessageConverterUnitTests { assertThat(converter.canRead(Customer.class, MediaType.APPLICATION_XML), is(true)); } + @Test // DATACMNS-1292 + public void doesNotSupportEntityExpansion() throws Exception { + + preparePayload("\n" // + + "\n" // + + "]>&xxe;Matthews"); + + exception.expect(HttpMessageNotReadableException.class); + exception.expectCause(is(Matchers. instanceOf(SAXParseException.class))); + + converter.read(Customer.class, message); + } + private void preparePayload(String payload) throws IOException { when(message.getBody()).thenReturn(new ByteArrayInputStream(payload.getBytes())); } diff --git a/template.mf b/template.mf index f22cc38a9..ff381e18b 100644 --- a/template.mf +++ b/template.mf @@ -20,6 +20,7 @@ Import-Template: javax.servlet.*;version="[2.5.0, 4.0.0)";resolution:=optional, javax.xml.bind.*;version="0";resolution:=optional, javax.xml.transform.*;version="0";resolution:=optional, + javax.xml.parsers.*;version="0";resolution:=optional, net.minidev.json.*;version="${jsonpath:[=.=.=,+1.0.0)}";resolution:=optional, org.aopalliance.*;version="[1.0.0, 2.0.0)";resolution:=optional, org.joda.time.*;version="${jodatime:[=.=.=,+1.0.0)}";resolution:=optional, @@ -42,6 +43,7 @@ Import-Template: org.slf4j.*;version="${slf4j:[=.=.=,+1.0.0)}", org.threeten.bp.*;version="${threetenbp:[=.=.=,+1.0.0)}";resolution:=optional, org.w3c.dom.*;version="0", + org.xml.sax.*;version="0";resolution:=optional, org.xmlbeam.*;version="${xmlbeam:[=.=.=,+1.0.0)}";resolution:=optional, scala.*;version="${scala:[=.=.=,+1.0.0)}";resolution:=optional DynamicImport-Package: *