diff --git a/metafacture-xml/src/main/java/org/metafacture/xml/XmlDecoder.java b/metafacture-xml/src/main/java/org/metafacture/xml/XmlDecoder.java index 1712c277..2fc11380 100644 --- a/metafacture-xml/src/main/java/org/metafacture/xml/XmlDecoder.java +++ b/metafacture-xml/src/main/java/org/metafacture/xml/XmlDecoder.java @@ -41,25 +41,24 @@ * @author Christoph Böhme * */ -@Description("Reads an XML file and passes the XML events to a receiver.") +@Description("Reads an XML file and passes the XML events to a receiver. Set `totalEntitySizeLimit=\"0\"` to allow unlimited XML entities.") @In(Reader.class) @Out(XmlReceiver.class) @FluxCommand("decode-xml") public final class XmlDecoder extends DefaultObjectPipe { private static final String SAX_PROPERTY_LEXICAL_HANDLER = "http://xml.org/sax/properties/lexical-handler"; - + private static final String TOTAL_ENTITY_SIZE_LIMIT = "http://www.oracle.com/xml/jaxp/properties/totalEntitySizeLimit"; private final XMLReader saxReader; /** - * Constructs an XmlDecoder by obtaining a new instance of an + * Creates an instance of {@link XmlDecoder} by obtaining a new instance of an * {@link org.xml.sax.XMLReader}. */ public XmlDecoder() { try { final SAXParserFactory parserFactory = SAXParserFactory.newInstance(); parserFactory.setNamespaceAware(true); - saxReader = parserFactory.newSAXParser().getXMLReader(); } catch (final ParserConfigurationException | SAXException e) { @@ -67,15 +66,29 @@ public XmlDecoder() { } } + /** + * Sets the total entity size limit for the XML parser. + * See java-api-xml-processing-jaxp-security-guide.html + * + * Defaults to "50,000,000". Set to "0" to allow unlimited entities. + * + * @param totalEntitySizeLimit the size of the allowed entities. Set to "0" if entities should be unlimited. + */ + public void setTotalEntitySizeLimit(final String totalEntitySizeLimit) { + try { + saxReader.setProperty(TOTAL_ENTITY_SIZE_LIMIT, totalEntitySizeLimit); + } + catch (final SAXException e) { + throw new MetafactureException(e); + } + } + @Override public void process(final Reader reader) { try { saxReader.parse(new InputSource(reader)); } - catch (final IOException e) { - throw new MetafactureException(e); - } - catch (final SAXException e) { + catch (final IOException | SAXException e) { throw new MetafactureException(e); } } @@ -89,10 +102,7 @@ protected void onSetReceiver() { try { saxReader.setProperty(SAX_PROPERTY_LEXICAL_HANDLER, getReceiver()); } - catch (final SAXNotRecognizedException e) { - throw new MetafactureException(e); - } - catch (final SAXNotSupportedException e) { + catch (final SAXNotRecognizedException | SAXNotSupportedException e) { throw new MetafactureException(e); } } diff --git a/metafacture-xml/src/test/java/org/metafacture/xml/XmlDecoderTest.java b/metafacture-xml/src/test/java/org/metafacture/xml/XmlDecoderTest.java new file mode 100644 index 00000000..7414aece --- /dev/null +++ b/metafacture-xml/src/test/java/org/metafacture/xml/XmlDecoderTest.java @@ -0,0 +1,69 @@ +/* + * Copyright 2024 Pascal Christoph (hbz) + * + * Licensed under the Apache License, Version 2.0 the "License"; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.metafacture.xml; + +import org.junit.Before; +import org.junit.Test; +import org.metafacture.framework.MetafactureException; + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; + +/** + * Tests for class {@link XmlDecoder}. + * + * @author Pascal Christoph (dr0i) + */ +public final class XmlDecoderTest { + + private final String TEST_XML_WITH_TWO_ENTITIES = ">>"; + private XmlDecoder xmlDecoder; + private final Reader reader = new StringReader(TEST_XML_WITH_TWO_ENTITIES); + + @Before + public void initSystemUnderTest() { + xmlDecoder = new XmlDecoder(); + } + + @Test + public void issue554_default() { + process(xmlDecoder); + } + + @Test(expected = MetafactureException.class) + public void issue554_shouldFail() { + xmlDecoder.setTotalEntitySizeLimit("1"); + process(xmlDecoder); + } + + @Test + public void issue554_unlimitedEntities() { + xmlDecoder.setTotalEntitySizeLimit("0"); + process(xmlDecoder); + } + + private void process(XmlDecoder xmlDecoder) { + try { + xmlDecoder.process(reader); + reader.close(); + } + catch (IOException e) { + throw new RuntimeException(e); + } + } +}