Skip to content

Commit c3e2a29

Browse files
authored
[AJmycukR] vuln-fix: Securing XML parser against XXE (CVE-2023-23926)
Fixing a XML External Entity (XXE) vulnerability, that was impacting the apoc.import.graphml procedure.
1 parent 1b60237 commit c3e2a29

File tree

2 files changed

+43
-0
lines changed

2 files changed

+43
-0
lines changed

core/src/main/java/apoc/export/graphml/XmlGraphMLReader.java

+13
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,8 @@ public long parseXML(Reader input) throws XMLStreamException {
206206
XMLInputFactory inputFactory = XMLInputFactory.newInstance();
207207
inputFactory.setProperty("javax.xml.stream.isCoalescing", true);
208208
inputFactory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, true);
209+
inputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
210+
inputFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
209211
XMLEventReader reader = inputFactory.createXMLEventReader(input);
210212
Entity last = null;
211213
Map<String, Key> nodeKeys = new HashMap<>();
@@ -218,10 +220,17 @@ public long parseXML(Reader input) throws XMLStreamException {
218220
XMLEvent event;
219221
try {
220222
event = (XMLEvent) reader.next();
223+
if ( event.getEventType() == XMLStreamConstants.DTD)
224+
{
225+
generateXmlDoctypeException();
226+
}
221227
} catch (Exception e) {
222228
// in case of unicode invalid chars we skip the event, or we exit in case of EOF
223229
if (e.getMessage().contains("Unexpected EOF")) {
224230
break;
231+
} else if (e.getMessage().contains("DOCTYPE"))
232+
{
233+
throw e;
225234
}
226235
continue;
227236
}
@@ -416,4 +425,8 @@ private String getAttribute(StartElement element, QName qname) {
416425
Attribute attribute = element.getAttributeByName(qname);
417426
return attribute != null ? attribute.getValue() : null;
418427
}
428+
429+
private RuntimeException generateXmlDoctypeException() {
430+
throw new RuntimeException("XML documents with a DOCTYPE are not allowed.");
431+
}
419432
}

core/src/test/java/apoc/export/graphml/ExportGraphMLTest.java

+30
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,12 @@
4949
import static apoc.util.BinaryTestUtil.fileToBinary;
5050
import static apoc.util.MapUtil.map;
5151
import static apoc.util.TestUtil.isRunningInCI;
52+
import static apoc.util.TestUtil.testResult;
5253
import static org.junit.Assert.assertEquals;
5354
import static org.junit.Assert.assertFalse;
5455
import static org.junit.Assert.assertNotNull;
5556
import static org.junit.Assert.assertNull;
57+
import static org.junit.Assert.assertThrows;
5658
import static org.junit.Assert.assertTrue;
5759
import static org.junit.Assume.assumeFalse;
5860
import static org.neo4j.configuration.GraphDatabaseSettings.TransactionStateMemoryAllocation.OFF_HEAP;
@@ -714,6 +716,34 @@ public void testExportGraphmlQueryWithStringCaptionCamelCase() {
714716
assertXMLEquals(output, EXPECTED_TYPES_PATH_CAMEL_CASE);
715717
}
716718

719+
@Test
720+
public void testImportGraphmlPreventXXEVulnerabilityThrowsQueryExecutionException() {
721+
QueryExecutionException e = assertThrows(QueryExecutionException.class,
722+
() -> testResult(db, "CALL apoc.import.graphml('" + TestUtil.getUrlFileName("xml/xxe.xml") + "', {})", (r) -> {
723+
r.next();
724+
r.close();
725+
})
726+
);
727+
728+
Throwable except = ExceptionUtils.getRootCause(e);
729+
assertTrue(except instanceof RuntimeException);
730+
assertEquals(except.getMessage(), "XML documents with a DOCTYPE are not allowed.");
731+
}
732+
733+
@Test
734+
public void testImportGraphmlPreventBillionLaughVulnerabilityThrowsQueryExecutionException() {
735+
QueryExecutionException e = assertThrows(QueryExecutionException.class,
736+
() -> testResult(db, "CALL apoc.import.graphml('" + TestUtil.getUrlFileName("xml/billion_laughs.xml") + "', {})", (r) -> {
737+
r.next();
738+
r.close();
739+
})
740+
);
741+
742+
Throwable except = ExceptionUtils.getRootCause(e);
743+
assertTrue(except instanceof RuntimeException);
744+
assertEquals(except.getMessage(), "XML documents with a DOCTYPE are not allowed.");
745+
}
746+
717747
private void assertResults(File output, Map<String, Object> r, final String source) {
718748
assertCommons(r);
719749
assertEquals(output.getAbsolutePath(), r.get("file"));

0 commit comments

Comments
 (0)