From cba8f225d91cc8a0bf4648b439c9caceefeed4fe Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 21 Apr 2026 20:34:29 +0200 Subject: [PATCH] Consistently ignore exceptions for Xerces-specific properties Closes gh-36682 (cherry picked from commit af7a5716e800db463f0e6913eb729f1bae050eb9) --- .../oxm/jaxb/Jaxb2Marshaller.java | 36 ++++++---- .../oxm/support/AbstractMarshaller.java | 54 ++++++++++---- .../Jaxb2RootElementHttpMessageConverter.java | 44 +++++++----- .../xml/SourceHttpMessageConverter.java | 70 +++++++++++++------ 4 files changed, 140 insertions(+), 64 deletions(-) diff --git a/spring-oxm/src/main/java/org/springframework/oxm/jaxb/Jaxb2Marshaller.java b/spring-oxm/src/main/java/org/springframework/oxm/jaxb/Jaxb2Marshaller.java index 2e05585caff..d6eb564e42d 100644 --- a/spring-oxm/src/main/java/org/springframework/oxm/jaxb/Jaxb2Marshaller.java +++ b/spring-oxm/src/main/java/org/springframework/oxm/jaxb/Jaxb2Marshaller.java @@ -913,17 +913,29 @@ public class Jaxb2Marshaller implements MimeMarshaller, MimeUnmarshaller, Generi // By default, Spring will prevent the processing of external entities. // This is a mitigation against XXE attacks. if (xmlReader == null) { - SAXParserFactory saxParserFactory = this.sourceParserFactory; - if (saxParserFactory == null) { - saxParserFactory = SAXParserFactory.newInstance(); - saxParserFactory.setNamespaceAware(true); - saxParserFactory.setFeature( - "http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd()); - saxParserFactory.setFeature( - "http://xml.org/sax/features/external-general-entities", isProcessExternalEntities()); - this.sourceParserFactory = saxParserFactory; + SAXParserFactory factory = this.sourceParserFactory; + if (factory == null) { + factory = SAXParserFactory.newInstance(); + factory.setNamespaceAware(true); + try { + factory.setFeature( + "http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd()); + } + catch (Exception ex) { + // Xerces properties not recognized/supported - ignore + } + try { + factory.setFeature( + "http://xml.org/sax/features/external-general-entities", isProcessExternalEntities()); + factory.setFeature( + "http://xml.org/sax/features/external-parameter-entities", isProcessExternalEntities()); + } + catch (Exception ex) { + // SAX properties not recognized/supported - ignore + } + this.sourceParserFactory = factory; } - SAXParser saxParser = saxParserFactory.newSAXParser(); + SAXParser saxParser = factory.newSAXParser(); xmlReader = saxParser.getXMLReader(); } if (!isProcessExternalEntities()) { @@ -931,8 +943,8 @@ public class Jaxb2Marshaller implements MimeMarshaller, MimeUnmarshaller, Generi } return new SAXSource(xmlReader, inputSource); } - catch (SAXException | ParserConfigurationException ex) { - logger.info("Processing of external entities could not be disabled", ex); + catch (Exception ex) { + logger.warn("Processing of external entities could not be disabled", ex); return source; } } diff --git a/spring-oxm/src/main/java/org/springframework/oxm/support/AbstractMarshaller.java b/spring-oxm/src/main/java/org/springframework/oxm/support/AbstractMarshaller.java index 7dcb6352ff5..fc200b210c7 100644 --- a/spring-oxm/src/main/java/org/springframework/oxm/support/AbstractMarshaller.java +++ b/spring-oxm/src/main/java/org/springframework/oxm/support/AbstractMarshaller.java @@ -75,6 +75,7 @@ public abstract class AbstractMarshaller implements Marshaller, Unmarshaller { private static final EntityResolver NO_OP_ENTITY_RESOLVER = (publicId, systemId) -> new InputSource(new StringReader("")); + /** Logger available to subclasses. */ protected final Log logger = LogFactory.getLog(getClass()); @@ -167,8 +168,22 @@ public abstract class AbstractMarshaller implements Marshaller, Unmarshaller { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setValidating(false); factory.setNamespaceAware(true); - factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd()); - factory.setFeature("http://xml.org/sax/features/external-general-entities", isProcessExternalEntities()); + try { + factory.setFeature( + "http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd()); + } + catch (Exception ex) { + // Xerces properties not recognized/supported - ignore + } + try { + factory.setFeature( + "http://xml.org/sax/features/external-general-entities", isProcessExternalEntities()); + factory.setFeature( + "http://xml.org/sax/features/external-parameter-entities", isProcessExternalEntities()); + } + catch (Exception ex) { + // SAX properties not recognized/supported - ignore + } return factory; } @@ -197,17 +212,29 @@ public abstract class AbstractMarshaller implements Marshaller, Unmarshaller { * @throws ParserConfigurationException if thrown by JAXP methods */ protected XMLReader createXmlReader() throws SAXException, ParserConfigurationException { - SAXParserFactory parserFactory = this.saxParserFactory; - if (parserFactory == null) { - parserFactory = SAXParserFactory.newInstance(); - parserFactory.setNamespaceAware(true); - parserFactory.setFeature( - "http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd()); - parserFactory.setFeature( - "http://xml.org/sax/features/external-general-entities", isProcessExternalEntities()); - this.saxParserFactory = parserFactory; + SAXParserFactory factory = this.saxParserFactory; + if (factory == null) { + factory = SAXParserFactory.newInstance(); + factory.setNamespaceAware(true); + try { + factory.setFeature( + "http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd()); + } + catch (Exception ex) { + // Xerces properties not recognized/supported - ignore + } + try { + factory.setFeature( + "http://xml.org/sax/features/external-general-entities", isProcessExternalEntities()); + factory.setFeature( + "http://xml.org/sax/features/external-parameter-entities", isProcessExternalEntities()); + } + catch (Exception ex) { + // SAX properties not recognized/supported - ignore + } + this.saxParserFactory = factory; } - SAXParser saxParser = parserFactory.newSAXParser(); + SAXParser saxParser = factory.newSAXParser(); XMLReader xmlReader = saxParser.getXMLReader(); if (!isProcessExternalEntities()) { xmlReader.setEntityResolver(NO_OP_ENTITY_RESOLVER); @@ -459,8 +486,7 @@ public abstract class AbstractMarshaller implements Marshaller, Unmarshaller { catch (NullPointerException ex) { if (!isSupportDtd()) { throw new UnmarshallingFailureException("NPE while unmarshalling. " + - "This can happen on JDK 1.6 due to the presence of DTD " + - "declarations, which are disabled."); + "This can happen due to the presence of DTD declarations, which are disabled."); } throw ex; } diff --git a/spring-web/src/main/java/org/springframework/http/converter/xml/Jaxb2RootElementHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/xml/Jaxb2RootElementHttpMessageConverter.java index 1c04f4d27c0..345818bd40e 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/xml/Jaxb2RootElementHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/xml/Jaxb2RootElementHttpMessageConverter.java @@ -19,7 +19,6 @@ package org.springframework.http.converter.xml; import java.io.StringReader; import java.nio.charset.Charset; -import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import javax.xml.transform.Result; @@ -38,7 +37,6 @@ import jakarta.xml.bind.annotation.XmlRootElement; import jakarta.xml.bind.annotation.XmlType; import org.xml.sax.EntityResolver; import org.xml.sax.InputSource; -import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.springframework.core.annotation.AnnotationUtils; @@ -68,6 +66,10 @@ import org.springframework.util.ClassUtils; */ public class Jaxb2RootElementHttpMessageConverter extends AbstractJaxb2HttpMessageConverter { + private static final EntityResolver NO_OP_ENTITY_RESOLVER = + (publicId, systemId) -> new InputSource(new StringReader("")); + + private boolean supportDtd = false; private boolean processExternalEntities = false; @@ -177,24 +179,36 @@ public class Jaxb2RootElementHttpMessageConverter extends AbstractJaxb2HttpMessa try { // By default, Spring will prevent the processing of external entities. // This is a mitigation against XXE attacks. - SAXParserFactory saxParserFactory = this.sourceParserFactory; - if (saxParserFactory == null) { - saxParserFactory = SAXParserFactory.newInstance(); - saxParserFactory.setNamespaceAware(true); - saxParserFactory.setFeature( - "http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd()); - saxParserFactory.setFeature( - "http://xml.org/sax/features/external-general-entities", isProcessExternalEntities()); - this.sourceParserFactory = saxParserFactory; + SAXParserFactory factory = this.sourceParserFactory; + if (factory == null) { + factory = SAXParserFactory.newInstance(); + factory.setNamespaceAware(true); + try { + factory.setFeature( + "http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd()); + } + catch (Exception ex) { + // Xerces properties not recognized/supported - ignore + } + try { + factory.setFeature( + "http://xml.org/sax/features/external-general-entities", isProcessExternalEntities()); + factory.setFeature( + "http://xml.org/sax/features/external-parameter-entities", isProcessExternalEntities()); + } + catch (Exception ex) { + // SAX properties not recognized/supported - ignore + } + this.sourceParserFactory = factory; } - SAXParser saxParser = saxParserFactory.newSAXParser(); + SAXParser saxParser = factory.newSAXParser(); XMLReader xmlReader = saxParser.getXMLReader(); if (!isProcessExternalEntities()) { xmlReader.setEntityResolver(NO_OP_ENTITY_RESOLVER); } return new SAXSource(xmlReader, inputSource); } - catch (SAXException | ParserConfigurationException ex) { + catch (Exception ex) { logger.warn("Processing of external entities could not be disabled", ex); return source; } @@ -240,8 +254,4 @@ public class Jaxb2RootElementHttpMessageConverter extends AbstractJaxb2HttpMessa return true; } - - private static final EntityResolver NO_OP_ENTITY_RESOLVER = - (publicId, systemId) -> new InputSource(new StringReader("")); - } diff --git a/spring-web/src/main/java/org/springframework/http/converter/xml/SourceHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/xml/SourceHttpMessageConverter.java index 5467ddce458..28de49386f1 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/xml/SourceHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/xml/SourceHttpMessageConverter.java @@ -179,17 +179,29 @@ public class SourceHttpMessageConverter extends AbstractHttpMe try { // By default, Spring will prevent the processing of external entities. // This is a mitigation against XXE attacks. - DocumentBuilderFactory builderFactory = this.documentBuilderFactory; - if (builderFactory == null) { - builderFactory = DocumentBuilderFactory.newInstance(); - builderFactory.setNamespaceAware(true); - builderFactory.setFeature( - "http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd()); - builderFactory.setFeature( - "http://xml.org/sax/features/external-general-entities", isProcessExternalEntities()); - this.documentBuilderFactory = builderFactory; + DocumentBuilderFactory factory = this.documentBuilderFactory; + if (factory == null) { + factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + try { + factory.setFeature( + "http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd()); + } + catch (Exception ex) { + // Xerces properties not recognized/supported - ignore + } + try { + factory.setFeature( + "http://xml.org/sax/features/external-general-entities", isProcessExternalEntities()); + factory.setFeature( + "http://xml.org/sax/features/external-parameter-entities", isProcessExternalEntities()); + } + catch (Exception ex) { + // SAX properties not recognized/supported - ignore + } + this.documentBuilderFactory = factory; } - DocumentBuilder builder = builderFactory.newDocumentBuilder(); + DocumentBuilder builder = factory.newDocumentBuilder(); if (!isProcessExternalEntities()) { builder.setEntityResolver(NO_OP_ENTITY_RESOLVER); } @@ -215,17 +227,29 @@ public class SourceHttpMessageConverter extends AbstractHttpMe private SAXSource readSAXSource(InputStream body, HttpInputMessage inputMessage) throws IOException { try { - SAXParserFactory parserFactory = this.saxParserFactory; - if (parserFactory == null) { - parserFactory = SAXParserFactory.newInstance(); - parserFactory.setNamespaceAware(true); - parserFactory.setFeature( - "http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd()); - parserFactory.setFeature( - "http://xml.org/sax/features/external-general-entities", isProcessExternalEntities()); - this.saxParserFactory = parserFactory; + SAXParserFactory factory = this.saxParserFactory; + if (factory == null) { + factory = SAXParserFactory.newInstance(); + factory.setNamespaceAware(true); + try { + factory.setFeature( + "http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd()); + } + catch (Exception ex) { + // Xerces properties not recognized/supported - ignore + } + try { + factory.setFeature( + "http://xml.org/sax/features/external-general-entities", isProcessExternalEntities()); + factory.setFeature( + "http://xml.org/sax/features/external-parameter-entities", isProcessExternalEntities()); + } + catch (Exception ex) { + // SAX properties not recognized/supported - ignore + } + this.saxParserFactory = factory; } - SAXParser saxParser = parserFactory.newSAXParser(); + SAXParser saxParser = factory.newSAXParser(); XMLReader xmlReader = saxParser.getXMLReader(); if (!isProcessExternalEntities()) { xmlReader.setEntityResolver(NO_OP_ENTITY_RESOLVER); @@ -233,7 +257,11 @@ public class SourceHttpMessageConverter extends AbstractHttpMe byte[] bytes = StreamUtils.copyToByteArray(body); return new SAXSource(xmlReader, new InputSource(new ByteArrayInputStream(bytes))); } - catch (SAXException | ParserConfigurationException ex) { + catch (ParserConfigurationException ex) { + throw new HttpMessageNotReadableException( + "Could not set feature: " + ex.getMessage(), ex, inputMessage); + } + catch (SAXException ex) { throw new HttpMessageNotReadableException( "Could not parse document: " + ex.getMessage(), ex, inputMessage); }