diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/pom.xml
index 9d00f4969b7f..4989be6718ab 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/pom.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/pom.xml
@@ -205,6 +205,7 @@
src/test/resources/hello.txt
src/test/resources/bye.txt
src/test/resources/old-swap-file.swap
+ src/test/resources/xxe_template.xml
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/persistence/TemplateDeserializer.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/persistence/TemplateDeserializer.java
index fef07094421a..27e90931bb45 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/persistence/TemplateDeserializer.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/persistence/TemplateDeserializer.java
@@ -17,13 +17,14 @@
package org.apache.nifi.persistence;
import java.io.InputStream;
-
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.stream.StreamSource;
-
import org.apache.nifi.controller.serialization.FlowSerializationException;
import org.apache.nifi.web.api.dto.TemplateDTO;
@@ -32,10 +33,17 @@ public class TemplateDeserializer {
public static TemplateDTO deserialize(final InputStream inStream) {
try {
JAXBContext context = JAXBContext.newInstance(TemplateDTO.class);
+
+ // Manually constructing the XIF is necessary to prevent XXE attacks
+ XMLInputFactory xif = XMLInputFactory.newFactory();
+ xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
+ xif.setProperty(XMLInputFactory.SUPPORT_DTD, false);
+ XMLStreamReader xsr = xif.createXMLStreamReader(new StreamSource(inStream));
+
Unmarshaller unmarshaller = context.createUnmarshaller();
- JAXBElement templateElement = unmarshaller.unmarshal(new StreamSource(inStream), TemplateDTO.class);
+ JAXBElement templateElement = unmarshaller.unmarshal(xsr, TemplateDTO.class);
return templateElement.getValue();
- } catch (final JAXBException e) {
+ } catch (final JAXBException | XMLStreamException e) {
throw new FlowSerializationException(e);
}
}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/groovy/org/apache/nifi/persistence/TemplateDeserializerTest.groovy b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/groovy/org/apache/nifi/persistence/TemplateDeserializerTest.groovy
new file mode 100644
index 000000000000..c90868b3d95b
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/groovy/org/apache/nifi/persistence/TemplateDeserializerTest.groovy
@@ -0,0 +1,66 @@
+/*
+ * 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.persistence
+
+import org.apache.nifi.web.api.dto.TemplateDTO
+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
+
+@RunWith(JUnit4.class)
+class TemplateDeserializerTest extends GroovyTestCase {
+ private static final Logger logger = LoggerFactory.getLogger(TemplateDeserializerTest.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 testShouldHandleXXEInTemplateLoad() {
+ // Arrange
+ final String XXE_TEMPLATE_FILEPATH = "src/test/resources/xxe_template.xml"
+ InputStream templateStream = new File(XXE_TEMPLATE_FILEPATH).newInputStream()
+
+ // Act
+ def msg = shouldFail() {
+ TemplateDTO template = TemplateDeserializer.deserialize(templateStream)
+ logger.info("Deserialized template \"${template.name}\" -- ${template.description}")
+ }
+
+ // Assert
+ logger.expected(msg)
+ assert msg =~ "XMLStreamException: ParseError "
+ }
+}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/resources/xxe_template.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/resources/xxe_template.xml
new file mode 100644
index 000000000000..82674e0e1a0f
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/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
+
+ 21ae0bd6-5db6-3a47-0000-000000000000
+ ff49910d-06bb-37ee-0000-000000000000
+ PROCESSOR
+
+ 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
+