Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/main/java/tools/jackson/dataformat/xml/XmlFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,8 @@ protected FromXmlParser _createParser(ObjectReadContext readCtxt, IOContext ioCt
sr = _xmlInputFactory.createXMLStreamReader(in);
} catch (XMLStreamException e) {
return StaxUtil.throwAsReadException(e, null);
} catch (ArrayIndexOutOfBoundsException e) {
return _reportBadXmlReaderCreation(e);
}
return _fromXmlParser(readCtxt, ioCtxt, _initializeXmlReader(sr));
}
Expand All @@ -497,6 +499,8 @@ protected FromXmlParser _createParser(ObjectReadContext readCtxt, IOContext ioCt
sr = _xmlInputFactory.createXMLStreamReader(r);
} catch (XMLStreamException e) {
return StaxUtil.throwAsReadException(e, null);
} catch (ArrayIndexOutOfBoundsException e) {
return _reportBadXmlReaderCreation(e);
}
return _fromXmlParser(readCtxt, ioCtxt, _initializeXmlReader(sr));
}
Expand All @@ -519,6 +523,8 @@ protected FromXmlParser _createParser(ObjectReadContext readCtxt, IOContext ioCt
}
} catch (XMLStreamException e) {
return StaxUtil.throwAsReadException(e, null);
} catch (ArrayIndexOutOfBoundsException e) {
return _reportBadXmlReaderCreation(e);
}
return _fromXmlParser(readCtxt, ioCtxt, _initializeXmlReader(sr));
}
Expand All @@ -538,10 +544,25 @@ protected FromXmlParser _createParser(ObjectReadContext readCtxt, IOContext ioCt
}
} catch (XMLStreamException e) {
return StaxUtil.throwAsReadException(e, null);
} catch (ArrayIndexOutOfBoundsException e) {
return _reportBadXmlReaderCreation(e);
}
return _fromXmlParser(readCtxt, ioCtxt, _initializeXmlReader(sr));
}

// Some non-Woodstox Stax implementations (for example SJSXP, [dataformat-xml#618])
// parse the XML declaration eagerly while creating the stream reader, and can fail
// with unchecked exceptions on malformed input instead of `XMLStreamException`.
// Translate to the standard read-exception type so callers see a `JacksonException`
// here too, matching how `_initializeXmlReader` already handles the sibling case.
private <T> T _reportBadXmlReaderCreation(ArrayIndexOutOfBoundsException e) {
throw new StreamReadException(null,
"Internal processing error by `XMLInputFactory` of type "
+_xmlInputFactory.getClass().getName()
+" when creating `XMLStreamReader` (consider using Woodstox instead): "
+e.getMessage(), e);
}

/**
* Overridable method to allow using custom FromXmlParser sub-classes.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package tools.jackson.dataformat.xml.stream;

import java.io.*;

import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.Source;

import org.junit.jupiter.api.Test;

import com.ctc.wstx.stax.WstxInputFactory;

import tools.jackson.core.exc.StreamReadException;

import tools.jackson.dataformat.xml.XmlFactory;
import tools.jackson.dataformat.xml.XmlMapper;
import tools.jackson.dataformat.xml.XmlTestUtil;

import static org.junit.jupiter.api.Assertions.assertThrows;

// [dataformat-xml#618]: some non-Woodstox Stax impls (notably JDK-bundled SJSXP)
// parse the XML declaration while creating the stream reader and fail with unchecked
// exceptions on malformed input. XmlFactory already translates the reader.next() and
// _initializeXmlReader cases; this checks the createXMLStreamReader boundary too, so
// callers see a StreamReadException rather than a leaked ArrayIndexOutOfBoundsException.
public class BrokenReaderFactoryTest extends XmlTestUtil
{
// Stands in for a non-Woodstox impl whose reader creation throws non-XMLStreamException
static class BrokenInputFactory extends WstxInputFactory {
@Override
public XMLStreamReader createXMLStreamReader(InputStream in) {
throw new ArrayIndexOutOfBoundsException("Index 5 out of bounds for length 4");
}

@Override
public XMLStreamReader createXMLStreamReader(Reader r) {
throw new ArrayIndexOutOfBoundsException("Index 5 out of bounds for length 4");
}

@Override
public XMLStreamReader createXMLStreamReader(Source s) {
throw new ArrayIndexOutOfBoundsException("Index 5 out of bounds for length 4");
}
}

private final XmlMapper MAPPER = new XmlMapper(new XmlFactory(new BrokenInputFactory()));

private static final String DOC = "<root>value</root>";

@Test
public void testByteArrayInput() throws Exception {
StreamReadException e = assertThrows(StreamReadException.class,
() -> MAPPER.readTree(utf8Bytes(DOC)));
verifyException(e, "Internal processing error by `XMLInputFactory`");
}

@Test
public void testInputStreamInput() throws Exception {
StreamReadException e = assertThrows(StreamReadException.class,
() -> MAPPER.readTree(new ByteArrayInputStream(utf8Bytes(DOC))));
verifyException(e, "Internal processing error by `XMLInputFactory`");
}

@Test
public void testReaderInput() throws Exception {
StreamReadException e = assertThrows(StreamReadException.class,
() -> MAPPER.readTree(new StringReader(DOC)));
verifyException(e, "Internal processing error by `XMLInputFactory`");
}

@Test
public void testStringInput() throws Exception {
StreamReadException e = assertThrows(StreamReadException.class,
() -> MAPPER.readTree(DOC));
verifyException(e, "Internal processing error by `XMLInputFactory`");
}
}
Loading