SAXBuilder.build() throws an undeclared unchecked exception on valid XML 1.1
SAXBuilder.build() is declared as
public Document build(Reader characterStream) throws JDOMException, IOException
so a caller catching JDOMException and IOException has handled every failure the
signature announces. On a valid XML 1.1 document it can still throw
org.jdom2.IllegalDataException, which extends IllegalArgumentException — unchecked,
undeclared, and past the caller's catch.
XML 1.1 permits , XML 1.0 does not, and JDOM does not fully implement XML 1.1:
the parser accepts the character under XML 1.1 rules and passes it to JDOM, whose verifier applies XML 1.0 rules and rejects it afterwards. (From inside a SAX callback, after build() has already begun.)
Affects the released 2.0.6.1.
Reproduce
Download and unzip Reproduce.zip
Poc.java, attached, parses the same content twice (once declared version="1.0", once version="1.1") inside the catch a caller would write from the signature:
curl -LO https://repo1.maven.org/maven2/org/jdom/jdom2/2.0.6.1/jdom2-2.0.6.1.jar
javac -cp jdom2-2.0.6.1.jar Poc.java
java -cp jdom2-2.0.6.1.jar:. Poc
static final String XML_10 = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<r>char:</r>";
static final String XML_11 = "<?xml version=\"1.1\" encoding=\"UTF-8\"?>\n<r>char:</r>";
static void parse(String label, String xml) throws JDOMException, IOException {
try {
Document doc = new SAXBuilder().build(new StringReader(xml));
System.out.println(label + ": parsed <" + doc.getRootElement().getName() + ">");
}
catch (JDOMException | IOException e) {
System.out.println(label + ": handled " + e.getClass().getName());
}
}
Output:
XML 1.0: handled org.jdom2.input.JDOMParseException
Exception in thread "main" org.jdom2.IllegalDataException: The data "char:" is not legal for a JDOM character content: 0x0005 is not a legal XML character.
at org.jdom2.Text.setText(Text.java:182)
at org.jdom2.Text.<init>(Text.java:112)
at org.jdom2.DefaultJDOMFactory.text(DefaultJDOMFactory.java:131)
at org.jdom2.input.sax.SAXHandler.flushCharacters(SAXHandler.java:840)
at org.jdom2.input.sax.SAXHandler.flushCharacters(SAXHandler.java:806)
at org.jdom2.input.sax.SAXHandler.endElement(SAXHandler.java:869)
at java.xml/com.sun.org.apache.xerces.internal.impl.XML11NSDocumentScannerImpl.scanEndElement(XML11NSDocumentScannerImpl.java:782)
...
at org.jdom2.input.sax.SAXBuilderEngine.build(SAXBuilderEngine.java:217)
at org.jdom2.input.sax.SAXBuilderEngine.build(SAXBuilderEngine.java:303)
at org.jdom2.input.SAXBuilder.build(SAXBuilder.java:1184)
at Poc.parse(Poc.java:16)
The XML 1.0 line is the same rejection reported the way the signature promises: the parser refuses the character itself and build() turns that into a JDOMParseException.
Only the XML 1.1 line escapes the catch.
Suggested direction
This is not a request to support XML 1.1. Rejecting content JDOM cannot represent is fine, but the rejection should reach the caller as the parse error it is, through JDOMException, like every other unusable input.
Found by the CISPA Fandango-Team while triaging OSS-Fuzz findings for jdom.
SAXBuilder.build()throws an undeclared unchecked exception on valid XML 1.1SAXBuilder.build()is declared asso a caller catching
JDOMExceptionandIOExceptionhas handled every failure thesignature announces. On a valid XML 1.1 document it can still throw
org.jdom2.IllegalDataException, which extendsIllegalArgumentException— unchecked,undeclared, and past the caller's
catch.XML 1.1 permits
, XML 1.0 does not, and JDOM does not fully implement XML 1.1:the parser accepts the character under XML 1.1 rules and passes it to JDOM, whose verifier applies XML 1.0 rules and rejects it afterwards. (From inside a SAX callback, after
build()has already begun.)Affects the released 2.0.6.1.
Reproduce
Download and unzip Reproduce.zip
Poc.java, attached, parses the same content twice (once declaredversion="1.0", onceversion="1.1") inside thecatcha caller would write from the signature:Output:
The XML 1.0 line is the same rejection reported the way the signature promises: the parser refuses the character itself and
build()turns that into aJDOMParseException.Only the XML 1.1 line escapes the
catch.Suggested direction
This is not a request to support XML 1.1. Rejecting content JDOM cannot represent is fine, but the rejection should reach the caller as the parse error it is, through
JDOMException, like every other unusable input.Found by the CISPA Fandango-Team while triaging OSS-Fuzz findings for jdom.