diff --git a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/xml/XmlUtils.java b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/xml/XmlUtils.java
index 99c90a6d53..e384c5b30a 100644
--- a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/xml/XmlUtils.java
+++ b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/xml/XmlUtils.java
@@ -16,11 +16,17 @@
*/
package org.apache.nifi.security.xml;
+import java.io.InputStream;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.stream.StreamSource;
-import java.io.InputStream;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
public class XmlUtils {
@@ -41,4 +47,23 @@ public class XmlUtils {
xif.setProperty(XMLInputFactory.SUPPORT_DTD, false);
return xif.createXMLStreamReader(source);
}
+
+ public static XMLReader createSafeSaxReader(SAXParserFactory saxParserFactory, ContentHandler contentHandler) throws SAXException, ParserConfigurationException {
+ if (saxParserFactory == null) {
+ throw new IllegalArgumentException("The provided SAX parser factory cannot be null");
+ }
+
+ if (contentHandler == null) {
+ throw new IllegalArgumentException("The provided SAX content handler cannot be null");
+ }
+
+ saxParserFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
+ saxParserFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
+
+ SAXParser saxParser = saxParserFactory.newSAXParser();
+ XMLReader xmlReader = saxParser.getXMLReader();
+ xmlReader.setContentHandler(contentHandler);
+
+ return xmlReader;
+ }
}
diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/pom.xml b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/pom.xml
index 3cdd7879ea..0fffbb8522 100644
--- a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/pom.xml
+++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/pom.xml
@@ -515,6 +515,8 @@
src/main/java/org/apache/nifi/security/util/crypto/bcrypt/BCrypt.java
+ src/test/resources/xxe_template.xml
+ src/test/resources/xxe_from_report.xml
diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/SplitXml.java b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/SplitXml.java
index 502f7f3506..de513c8ba4 100644
--- a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/SplitXml.java
+++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/SplitXml.java
@@ -16,6 +16,12 @@
*/
package org.apache.nifi.processors.standard;
+import static org.apache.nifi.flowfile.attributes.FragmentAttributes.FRAGMENT_COUNT;
+import static org.apache.nifi.flowfile.attributes.FragmentAttributes.FRAGMENT_ID;
+import static org.apache.nifi.flowfile.attributes.FragmentAttributes.FRAGMENT_INDEX;
+import static org.apache.nifi.flowfile.attributes.FragmentAttributes.SEGMENT_ORIGINAL_FILENAME;
+import static org.apache.nifi.flowfile.attributes.FragmentAttributes.copyAttributesToOriginal;
+
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
@@ -28,11 +34,8 @@ import java.util.TreeMap;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
-
import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
-
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.nifi.annotation.behavior.EventDriven;
import org.apache.nifi.annotation.behavior.InputRequirement;
@@ -54,7 +57,7 @@ import org.apache.nifi.processor.ProcessorInitializationContext;
import org.apache.nifi.processor.Relationship;
import org.apache.nifi.processor.util.StandardValidators;
import org.apache.nifi.processors.standard.util.XmlElementNotifier;
-import org.apache.nifi.stream.io.BufferedInputStream;
+import org.apache.nifi.security.xml.XmlUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.Attributes;
@@ -64,12 +67,6 @@ import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
-import static org.apache.nifi.flowfile.attributes.FragmentAttributes.FRAGMENT_COUNT;
-import static org.apache.nifi.flowfile.attributes.FragmentAttributes.FRAGMENT_ID;
-import static org.apache.nifi.flowfile.attributes.FragmentAttributes.FRAGMENT_INDEX;
-import static org.apache.nifi.flowfile.attributes.FragmentAttributes.SEGMENT_ORIGINAL_FILENAME;
-import static org.apache.nifi.flowfile.attributes.FragmentAttributes.copyAttributesToOriginal;
-
@EventDriven
@SideEffectFree
@SupportsBatching
@@ -175,12 +172,9 @@ public class SplitXml extends AbstractProcessor {
final AtomicBoolean failed = new AtomicBoolean(false);
session.read(original, rawIn -> {
- try (final InputStream in = new BufferedInputStream(rawIn)) {
- SAXParser saxParser = null;
+ try (final InputStream in = new java.io.BufferedInputStream(rawIn)) {
try {
- saxParser = saxParserFactory.newSAXParser();
- final XMLReader reader = saxParser.getXMLReader();
- reader.setContentHandler(parser);
+ final XMLReader reader = XmlUtils.createSafeSaxReader(saxParserFactory, parser);
reader.parse(new InputSource(in));
} catch (final ParserConfigurationException | SAXException e) {
logger.error("Unable to parse {} due to {}", new Object[]{original, e});
diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/groovy/org/apache/nifi/processors/standard/SplitXmlTest.groovy b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/groovy/org/apache/nifi/processors/standard/SplitXmlTest.groovy
new file mode 100644
index 0000000000..f04dca685e
--- /dev/null
+++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/groovy/org/apache/nifi/processors/standard/SplitXmlTest.groovy
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.nifi.processors.standard
+
+import org.apache.nifi.util.TestRunner
+import org.apache.nifi.util.TestRunners
+import org.junit.After
+import org.junit.Before
+import org.junit.BeforeClass
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+
+import java.nio.file.Paths
+
+
+@RunWith(JUnit4.class)
+class SplitXmlTest extends GroovyTestCase {
+ private static final Logger logger = LoggerFactory.getLogger(SplitXmlTest.class)
+
+ @BeforeClass
+ static void setUpOnce() throws Exception {
+ logger.metaClass.methodMissing = { String name, args ->
+ logger.info("[${name?.toUpperCase()}] ${(args as List).join(" ")}")
+ }
+ }
+
+ @Before
+ void setUp() throws Exception {
+
+ }
+
+ @After
+ void tearDown() throws Exception {
+
+ }
+
+ @Test
+ void testShouldHandleXXEInTemplate() {
+ // Arrange
+ final String XXE_TEMPLATE_FILEPATH = "src/test/resources/xxe_template.xml"
+ final TestRunner runner = TestRunners.newTestRunner(new SplitXml())
+ runner.setProperty(SplitXml.SPLIT_DEPTH, "3")
+ runner.enqueue(Paths.get(XXE_TEMPLATE_FILEPATH))
+
+ // Act
+ runner.run()
+ logger.info("SplitXML processor ran")
+
+ // Assert
+ runner.assertAllFlowFilesTransferred(SplitXml.REL_FAILURE)
+ }
+
+ @Test
+ void testShouldHandleRemoteCallXXE() {
+ // Arrange
+ final String XXE_TEMPLATE_FILEPATH = "src/test/resources/xxe_from_report.xml"
+ final TestRunner runner = TestRunners.newTestRunner(new SplitXml())
+ runner.setProperty(SplitXml.SPLIT_DEPTH, "3")
+ runner.enqueue(Paths.get(XXE_TEMPLATE_FILEPATH))
+
+ // Act
+ runner.run()
+ logger.info("SplitXML processor ran")
+
+ // Assert
+ runner.assertAllFlowFilesTransferred(SplitXml.REL_FAILURE)
+ }
+}
diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/xxe_from_report.xml b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/xxe_from_report.xml
new file mode 100644
index 0000000000..42b22a0f3f
--- /dev/null
+++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/xxe_from_report.xml
@@ -0,0 +1,2 @@
+
+1
\ No newline at end of file
diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/xxe_template.xml b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/xxe_template.xml
new file mode 100644
index 0000000000..82674e0e1a
--- /dev/null
+++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/xxe_template.xml
@@ -0,0 +1,230 @@
+]>
+
+ &xxe;
+ A simple template which generates flowfiles and logs them.
+ 3a204982-015e-1000-eaa2-19d352ec8394
+
+
+ 0fbe8be5-306c-3b6c-0000-000000000000
+ 21ae0bd6-5db6-3a47-0000-000000000000
+ 1 GB
+ 10000
+
+ 21ae0bd6-5db6-3a47-0000-000000000000
+ fd90023d-a235-30f6-0000-000000000000
+ PROCESSOR
+
+ 0 sec
+ 1
+
+ success
+
+ 0
+
+
+ fd90023d-a235-30f6-0000-000000000000
+ 21ae0bd6-5db6-3a47-0000-000000000000
+
+ 0.0
+ 318.3128613789876
+
+
+ nifi-standard-nar
+ org.apache.nifi
+ 1.4.0-SNAPSHOT
+
+
+ WARN
+
+ 1
+
+
+ Log Level
+
+ Log Level
+
+
+
+ Log Payload
+
+ Log Payload
+
+
+
+ Attributes to Log
+
+ Attributes to Log
+
+
+
+ attributes-to-log-regex
+
+ attributes-to-log-regex
+
+
+
+ Attributes to Ignore
+
+ Attributes to Ignore
+
+
+
+ attributes-to-ignore-regex
+
+ attributes-to-ignore-regex
+
+
+
+ Log prefix
+
+ Log prefix
+
+
+
+ character-set
+
+ character-set
+
+
+
+ ALL
+ false
+ 30 sec
+
+
+ Log Level
+ info
+
+
+ Log Payload
+ true
+
+
+ Attributes to Log
+
+
+ attributes-to-log-regex
+ .*
+
+
+ Attributes to Ignore
+
+
+ attributes-to-ignore-regex
+
+
+ Log prefix
+
+
+ character-set
+ UTF-8
+
+
+ 0
+ 0 sec
+ TIMER_DRIVEN
+ 1 sec
+
+ LogAttribute
+
+ true
+ success
+
+ STOPPED
+
+ org.apache.nifi.processors.standard.LogAttribute
+
+
+ ff49910d-06bb-37ee-0000-000000000000
+ 21ae0bd6-5db6-3a47-0000-000000000000
+
+ 1.1368683772161603E-13
+ 0.0
+
+
+ nifi-standard-nar
+ org.apache.nifi
+ 1.4.0-SNAPSHOT
+
+
+ WARN
+
+ 1
+
+
+ File Size
+
+ File Size
+
+
+
+ Batch Size
+
+ Batch Size
+
+
+
+ Data Format
+
+ Data Format
+
+
+
+ Unique FlowFiles
+
+ Unique FlowFiles
+
+
+
+ generate-ff-custom-text
+
+ generate-ff-custom-text
+
+
+
+ ALL
+ false
+ 30 sec
+
+
+ File Size
+ 0B
+
+
+ Batch Size
+ 1
+
+
+ Data Format
+ Text
+
+
+ Unique FlowFiles
+ false
+
+
+ generate-ff-custom-text
+ This is a plaintext message.
+
+
+ 0
+ 1 sec
+ TIMER_DRIVEN
+ 1 sec
+
+ GenerateFlowFile
+
+ false
+ success
+
+ STOPPED
+
+ org.apache.nifi.processors.standard.GenerateFlowFile
+
+
+ 09/05/2017 14:51:01 PDT
+