This file was deleted.

This file was deleted.

5 changes: 5 additions & 0 deletions nifi-commons/nifi-single-user-utils/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
</parent>
<artifactId>nifi-single-user-utils</artifactId>
<dependencies>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-xml-processing</artifactId>
<version>1.17.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>at.favre.lib</groupId>
<artifactId>bcrypt</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,20 @@
package org.apache.nifi.authentication.single.user.writer;

import org.apache.nifi.authentication.single.user.SingleUserCredentials;
import org.apache.nifi.xml.processing.stream.StandardXMLEventReaderProvider;
import org.apache.nifi.xml.processing.stream.XMLEventReaderProvider;

import javax.xml.stream.XMLEventFactory;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.Characters;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import javax.xml.transform.stream.StreamSource;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
Expand Down Expand Up @@ -199,10 +201,8 @@ private XMLEventWriter getProvidersWriter(final OutputStream outputStream) throw
return outputFactory.createXMLEventWriter(outputStream);
}

private XMLEventReader getProvidersReader(final InputStream inputStream) throws XMLStreamException {
final XMLInputFactory inputFactory = XMLInputFactory.newFactory();
inputFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
inputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
return inputFactory.createXMLEventReader(inputStream);
private XMLEventReader getProvidersReader(final InputStream inputStream) {
final XMLEventReaderProvider readerProvider = new StandardXMLEventReaderProvider();
return readerProvider.getEventReader(new StreamSource(inputStream));
}
}
5 changes: 5 additions & 0 deletions nifi-commons/nifi-site-to-site-client/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@
<artifactId>nifi-security-utils</artifactId>
<version>1.16.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-xml-processing</artifactId>
<version>1.17.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,12 @@
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import org.apache.nifi.security.xml.XmlUtils;
import javax.xml.transform.stream.StreamSource;

import org.apache.nifi.xml.processing.ProcessingException;
import org.apache.nifi.xml.processing.stream.StandardXMLStreamReaderProvider;
import org.apache.nifi.xml.processing.stream.XMLStreamReaderProvider;

@XmlRootElement
public class ClusterNodeInformation {
Expand Down Expand Up @@ -65,9 +68,10 @@ public void marshal(final OutputStream os) throws JAXBException {
public static ClusterNodeInformation unmarshal(final InputStream is) throws JAXBException {
try {
final Unmarshaller unmarshaller = JAXB_CONTEXT.createUnmarshaller();
final XMLStreamReader xsr = XmlUtils.createSafeReader(is);
final XMLStreamReaderProvider provider = new StandardXMLStreamReaderProvider();
final XMLStreamReader xsr = provider.getStreamReader(new StreamSource(is));
return (ClusterNodeInformation) unmarshaller.unmarshal(xsr);
} catch (XMLStreamException e) {
} catch (final ProcessingException e) {
throw new JAXBException("Error unmarshalling the cluster node information", e);
}
}
Expand Down
55 changes: 55 additions & 0 deletions nifi-commons/nifi-xml-processing/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?xml version="1.0"?>
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-commons</artifactId>
<version>1.17.0-SNAPSHOT</version>
</parent>
<artifactId>nifi-xml-processing</artifactId>
<version>1.17.0-SNAPSHOT</version>
<packaging>jar</packaging>
<build>
<plugins>
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<version>4.6.0.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
<configuration>
<effort>Max</effort>
<threshold>low</threshold>
<failOnError>true</failOnError>
<plugins>
<plugin>
<groupId>com.h3xstream.findsecbugs</groupId>
<artifactId>findsecbugs-plugin</artifactId>
<version>1.12.0</version>
</plugin>
</plugins>
</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* 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.xml.processing;

/**
* General Exception for XML processing problems
*/
public class ProcessingException extends RuntimeException {
/**
* Processing Exception with message and cause for tracing
*
* @param message Error Message
* @param cause Throwable cause for tracing
*/
public ProcessingException(final String message, final Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* 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.xml.processing;

import javax.xml.XMLConstants;

/**
* XML Processing Features
*/
public enum ProcessingFeature {
/** Secure Processing */
SECURE_PROCESSING(XMLConstants.FEATURE_SECURE_PROCESSING, true),

/** SAX Namespaces */
SAX_NAMESPACES("http://xml.org/sax/features/namespaces", true),

/** SAX Namespace Prefixes */
SAX_NAMESPACE_PREFIXES("http://xml.org/sax/features/namespace-prefixes", true),

/** Disallow Document Type Declaration */
DISALLOW_DOCTYPE_DECL("http://apache.org/xml/features/disallow-doctype-decl", true);

private final String feature;

private final boolean enabled;

ProcessingFeature(final String feature, final boolean enabled) {
this.feature = feature;
this.enabled = enabled;
}

public String getFeature() {
return feature;
}

public boolean isEnabled() {
return enabled;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* 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.xml.processing.parsers;

import org.w3c.dom.Document;

import java.io.InputStream;

/**
* Provider for instances of Document Object Model
*/
public interface DocumentProvider {
/**
* Create new Document
*
* @return Document
*/
Document newDocument();

/**
* Parse InputStream to Document
*
* @param inputStream InputStream to be parsed
* @return Document parsed from stream
*/
Document parse(InputStream inputStream);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
* 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.xml.processing.parsers;

import org.apache.nifi.xml.processing.ProcessingException;
import org.apache.nifi.xml.processing.ProcessingFeature;
import org.w3c.dom.Document;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;

import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.validation.Schema;
import java.io.IOException;
import java.io.InputStream;
import java.util.Objects;

/**
* Standard implementation of Document Provider with secure processing enabled
*/
public class StandardDocumentProvider implements DocumentProvider {
private boolean namespaceAware;

private Schema schema;

private ErrorHandler errorHandler;

/**
* Set Error Handler
*
* @param errorHandler Error Handler
*/
public void setErrorHandler(final ErrorHandler errorHandler) {
this.errorHandler = errorHandler;
}

/**
* Set Namespace Aware status on DocumentBuilderFactory
*
* @param namespaceAware Namespace Awareness
*/
public void setNamespaceAware(final boolean namespaceAware) {
this.namespaceAware = namespaceAware;
}

/**
* Set Namespace Aware status on DocumentBuilderFactory
*
* @param schema Schema for validation or null to disable validation
*/
public void setSchema(final Schema schema) {
this.schema = schema;
}

@Override
public Document newDocument() {
final DocumentBuilderFactory documentBuilderFactory = getDocumentBuilderFactory();

try {
documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, ProcessingFeature.SECURE_PROCESSING.isEnabled());
documentBuilderFactory.setFeature(ProcessingFeature.DISALLOW_DOCTYPE_DECL.getFeature(), ProcessingFeature.DISALLOW_DOCTYPE_DECL.isEnabled());

final DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
documentBuilder.setErrorHandler(errorHandler);

return documentBuilder.newDocument();
} catch (final ParserConfigurationException e) {
throw new ProcessingException("Configuration failed", e);
}
}

/**
* Build and return DocumentBuilder
*
* @return DocumentBuilder configured using provided properties
*/
@Override
public Document parse(final InputStream inputStream) {
Objects.requireNonNull(inputStream, "InputStream required");
final DocumentBuilderFactory documentBuilderFactory = getDocumentBuilderFactory();

try {
documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, ProcessingFeature.SECURE_PROCESSING.isEnabled());
documentBuilderFactory.setFeature(ProcessingFeature.DISALLOW_DOCTYPE_DECL.getFeature(), isDisallowDocumentTypeDeclaration());

final DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
documentBuilder.setErrorHandler(errorHandler);

return documentBuilder.parse(inputStream);
} catch (final ParserConfigurationException|SAXException|IOException e) {
throw new ProcessingException("Parsing failed", e);
}
}

protected boolean isDisallowDocumentTypeDeclaration() {
return ProcessingFeature.DISALLOW_DOCTYPE_DECL.isEnabled();
}

private DocumentBuilderFactory getDocumentBuilderFactory() {
final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();

documentBuilderFactory.setSchema(schema);
documentBuilderFactory.setNamespaceAware(namespaceAware);

documentBuilderFactory.setXIncludeAware(false);
documentBuilderFactory.setExpandEntityReferences(false);

return documentBuilderFactory;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* 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.xml.processing.sax;

import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;

/**
* SAX Input Source Parser
*/
public interface InputSourceParser {
/**
* Parse Input Source using Content Handler
*
* @param inputSource Input Source to be parsed
* @param contentHandler Content Handler used during parsing
*/
void parse(InputSource inputSource, ContentHandler contentHandler);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* 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.xml.processing.sax;

import org.apache.nifi.xml.processing.ProcessingException;
import org.apache.nifi.xml.processing.ProcessingFeature;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

import javax.xml.XMLConstants;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.IOException;
import java.util.Objects;

/**
* Standard implementation of Input Source Parser with secure processing enabled
*/
public class StandardInputSourceParser implements InputSourceParser {
private boolean namespaceAware;

/**
* Set Namespace Aware status on SAXParserFactory
*
* @param namespaceAware Namespace Aware status
*/
public void setNamespaceAware(final boolean namespaceAware) {
this.namespaceAware = namespaceAware;
}

/**
* Parse Input Source using Content Handler
*
* @param inputSource Input Source to be parsed
* @param contentHandler Content Handler used during parsing
*/
@Override
public void parse(final InputSource inputSource, final ContentHandler contentHandler) {
Objects.requireNonNull(inputSource, "InputSource required");
Objects.requireNonNull(contentHandler, "ContentHandler required");

try {
parseInputSource(inputSource, contentHandler);
} catch (final ParserConfigurationException|SAXException e) {
throw new ProcessingException("Parsing failed", e);
}
}

private void parseInputSource(final InputSource inputSource, final ContentHandler contentHandler) throws ParserConfigurationException, SAXException {
final SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
saxParserFactory.setNamespaceAware(namespaceAware);
saxParserFactory.setXIncludeAware(false);

saxParserFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, ProcessingFeature.SECURE_PROCESSING.isEnabled());
saxParserFactory.setFeature(ProcessingFeature.DISALLOW_DOCTYPE_DECL.getFeature(), ProcessingFeature.DISALLOW_DOCTYPE_DECL.isEnabled());

if (namespaceAware) {
saxParserFactory.setFeature(ProcessingFeature.SAX_NAMESPACES.getFeature(), ProcessingFeature.SAX_NAMESPACES.isEnabled());
saxParserFactory.setFeature(ProcessingFeature.SAX_NAMESPACE_PREFIXES.getFeature(), ProcessingFeature.SAX_NAMESPACE_PREFIXES.isEnabled());
}

final SAXParser parser = saxParserFactory.newSAXParser();

final XMLReader reader = parser.getXMLReader();
reader.setContentHandler(contentHandler);

try {
reader.parse(inputSource);
} catch (final IOException e) {
throw new ProcessingException("Parsing failed", e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* 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.xml.processing.stream;

import org.apache.nifi.xml.processing.ProcessingException;

import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.transform.stream.StreamSource;
import java.util.Objects;

/**
* Standard implementation of XMLStreamReader provider with secure processing enabled
*/
public class StandardXMLEventReaderProvider implements XMLEventReaderProvider {
/**
* Get XML Event Reader
*
* @param streamSource Stream Source for Reader
* @return Configured XML Event Reader
*/
@Override
public XMLEventReader getEventReader(final StreamSource streamSource) {
Objects.requireNonNull(streamSource, "StreamSource required");

final XMLInputFactory inputFactory = XMLInputFactory.newFactory();
inputFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
inputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false);

try {
return inputFactory.createXMLEventReader(streamSource);
} catch (final XMLStreamException e) {
throw new ProcessingException("Reader creation failed", e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* 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.xml.processing.stream;

import org.apache.nifi.xml.processing.ProcessingException;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.stream.StreamSource;
import java.util.Objects;

/**
* Standard implementation of XMLStreamReader provider with secure processing enabled
*/
public class StandardXMLStreamReaderProvider implements XMLStreamReaderProvider {
/**
* Get XML Stream Reader with external entities disabled
*
* @param streamSource Stream Source for Reader
* @return Configured XML Stream Reader
*/
@Override
public XMLStreamReader getStreamReader(final StreamSource streamSource) {
Objects.requireNonNull(streamSource, "StreamSource required");

final XMLInputFactory inputFactory = XMLInputFactory.newFactory();
inputFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
inputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false);

try {
return inputFactory.createXMLStreamReader(streamSource);
} catch (final XMLStreamException e) {
throw new ProcessingException("Reader creation failed", e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* 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.xml.processing.stream;

import javax.xml.stream.XMLEventReader;
import javax.xml.transform.stream.StreamSource;

/**
* Provider for instances of XMLEventReader
*/
public interface XMLEventReaderProvider {
/**
* Get XML Event Reader
*
* @param streamSource Stream Source for Reader
* @return Configured XML Event Reader
*/
XMLEventReader getEventReader(StreamSource streamSource);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* 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.xml.processing.stream;

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

/**
* Provider for instances of XMLStreamReader
*/
public interface XMLStreamReaderProvider {
/**
* Get XML Stream Reader
*
* @param streamSource Stream Source for Reader
* @return Configured XML Stream Reader
*/
XMLStreamReader getStreamReader(StreamSource streamSource);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* 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.xml.processing.validation;

import javax.xml.transform.Source;
import javax.xml.validation.Schema;

/**
* XML Schema Validator
*/
public interface SchemaValidator {
/**
* Validate Source using Schema
*
* @param schema Schema source for Validator
* @param source Source to be validated
*/
void validate(Schema schema, Source source);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* 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.xml.processing.validation;

import org.apache.nifi.xml.processing.ProcessingException;
import org.apache.nifi.xml.processing.ProcessingFeature;
import org.xml.sax.SAXException;

import javax.xml.XMLConstants;
import javax.xml.transform.Source;
import javax.xml.validation.Schema;
import javax.xml.validation.Validator;
import java.io.IOException;
import java.util.Objects;

/**
* Standard implementation of XML Schema Validator with secure processing enabled
*/
public class StandardSchemaValidator implements SchemaValidator {
/**
* Validate Source using Schema
*
* @param schema Schema source for Validator
* @param source Source to be validated
*/
@Override
public void validate(final Schema schema, final Source source) {
Objects.requireNonNull(schema, "Schema required");
Objects.requireNonNull(source, "Source required");

final Validator validator = schema.newValidator();

try {
validator.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, ProcessingFeature.SECURE_PROCESSING.isEnabled());
} catch (final SAXException e) {
throw new ProcessingException("Validator configuration failed", e);
}

try {
validator.validate(source);
} catch (final SAXException|IOException e) {
throw new ProcessingException("Validation failed", e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* 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.xml.processing;

import java.io.InputStream;

public class ResourceProvider {
private static final String STANDARD_DOCUMENT_DOCTYPE_ENTITY = "/standard-document-doctype-entity.xml";

private static final String STANDARD_DOCUMENT_DOCTYPE = "/standard-document-doctype.xml";

private static final String STANDARD_DOCUMENT = "/standard-document.xml";

private static final String STANDARD_NAMESPACE_DOCUMENT = "/standard-namespace-document.xml";

private static final String STANDARD_NAMESPACE_DOCUMENT_DOCTYPE_ENTITY = "/standard-namespace-document-doctype-entity.xml";

private static final String STANDARD_SCHEMA = "/standard-schema.xsd";

public static InputStream getStandardDocument() {
return getResource(STANDARD_DOCUMENT);
}

public static InputStream getStandardDocumentDocTypeEntity() {
return getResource(STANDARD_DOCUMENT_DOCTYPE_ENTITY);
}

public static InputStream getStandardDocumentDocType() {
return getResource(STANDARD_DOCUMENT_DOCTYPE);
}

public static InputStream getStandardNamespaceDocument() {
return getResource(STANDARD_NAMESPACE_DOCUMENT);
}

public static InputStream getStandardNamespaceDocumentDocTypeEntity() {
return getResource(STANDARD_NAMESPACE_DOCUMENT_DOCTYPE_ENTITY);
}

public static InputStream getStandardSchema() {
return getResource(STANDARD_SCHEMA);
}

private static InputStream getResource(final String path) {
final InputStream resource = ResourceProvider.class.getResourceAsStream(path);
if (resource == null) {
throw new IllegalStateException(String.format("Resource [%s] not found", path));
}
return resource;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* 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.xml.processing.parsers;

import org.apache.nifi.xml.processing.ProcessingException;
import org.apache.nifi.xml.processing.ResourceProvider;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.w3c.dom.Document;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXParseException;

import javax.xml.validation.Schema;
import javax.xml.validation.ValidatorHandler;

import java.io.IOException;
import java.io.InputStream;

import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class StandardDocumentProviderTest {
@Mock
Schema schema;

@Mock
ValidatorHandler validatorHandler;

@Mock
ErrorHandler errorHandler;

@Test
void testNewDocument() {
final StandardDocumentProvider provider = new StandardDocumentProvider();

final Document document = provider.newDocument();

assertNotNull(document);
}

@Test
void testParseStandard() throws IOException {
final StandardDocumentProvider provider = new StandardDocumentProvider();

try (final InputStream inputStream = ResourceProvider.getStandardDocument()) {
final Document document = provider.parse(inputStream);
assertNotNull(document);
}
}

@Test
void testParseDocumentTypeDeclarationException() throws IOException {
final StandardDocumentProvider provider = new StandardDocumentProvider();

try (final InputStream inputStream = ResourceProvider.getStandardDocumentDocType()) {
assertParsingException(inputStream, provider);
}
}

@Test
void testParseExternalEntityException() throws IOException {
final StandardDocumentProvider provider = new StandardDocumentProvider();

assertParsingException(provider);
}

@Test
void testParseNamespaceAwareSchemaConfiguredExternalEntityException() throws IOException {
when(schema.newValidatorHandler()).thenReturn(validatorHandler);

final StandardDocumentProvider provider = new StandardDocumentProvider();
provider.setNamespaceAware(true);
provider.setSchema(schema);
provider.setErrorHandler(errorHandler);

assertParsingException(provider);
}

private void assertParsingException(final StandardDocumentProvider provider) throws IOException {
try (final InputStream inputStream = ResourceProvider.getStandardDocumentDocTypeEntity()) {
assertParsingException(inputStream, provider);
}
}

private void assertParsingException(final InputStream inputStream, final StandardDocumentProvider provider) {
final ProcessingException processingException = assertThrows(ProcessingException.class, () -> provider.parse(inputStream));
assertInstanceOf(SAXParseException.class, processingException.getCause());
}
}
Original file line number Diff line number Diff line change
@@ -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.xml.processing.sax;

import org.apache.nifi.xml.processing.ProcessingException;
import org.apache.nifi.xml.processing.ResourceProvider;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;

import java.io.IOException;
import java.io.InputStream;

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

@ExtendWith(MockitoExtension.class)
public class StandardInputSourceParserTest {
@Mock
ContentHandler contentHandler;

@Test
void testParseStandard() throws IOException {
final StandardInputSourceParser parser = new StandardInputSourceParser();

try (final InputStream inputStream = ResourceProvider.getStandardDocument()) {
parser.parse(new InputSource(inputStream), contentHandler);
}
}

@Test
void testParseDocumentTypeDeclarationException() throws IOException {
final StandardInputSourceParser parser = new StandardInputSourceParser();

try (final InputStream inputStream = ResourceProvider.getStandardDocumentDocType()) {
assertParsingException(inputStream, parser);
}
}

@Test
void testParseExternalEntityException() throws IOException {
final StandardInputSourceParser parser = new StandardInputSourceParser();

assertParsingException(parser);
}

@Test
void testParseNamespaceAwareExternalEntityException() throws IOException {
final StandardInputSourceParser parser = new StandardInputSourceParser();

parser.setNamespaceAware(true);

assertParsingException(parser);
}

private void assertParsingException(final StandardInputSourceParser parser) throws IOException {
try (final InputStream inputStream = ResourceProvider.getStandardDocumentDocTypeEntity()) {
assertParsingException(inputStream, parser);
}
}

private void assertParsingException(final InputStream inputStream, final StandardInputSourceParser parser) {
final ProcessingException processingException = assertThrows(ProcessingException.class, () -> parser.parse(new InputSource(inputStream), new DefaultHandler()));
assertInstanceOf(SAXParseException.class, processingException.getCause());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* 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.xml.processing.stream;

import org.apache.nifi.xml.processing.ResourceProvider;
import org.junit.jupiter.api.Test;

import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLStreamException;
import javax.xml.transform.stream.StreamSource;
import java.io.IOException;
import java.io.InputStream;

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

public class StandardXMLEventReaderProviderTest {
@Test
void testGetEventReaderStandard() throws IOException, XMLStreamException {
final StandardXMLEventReaderProvider provider = new StandardXMLEventReaderProvider();

try (final InputStream inputStream = ResourceProvider.getStandardDocument()) {
final XMLEventReader reader = provider.getEventReader(new StreamSource(inputStream));
processReader(reader);
}
}

@Test
void testGetEventReaderStandardDocumentTypeDeclaration() throws IOException {
final StandardXMLEventReaderProvider provider = new StandardXMLEventReaderProvider();

try (final InputStream inputStream = ResourceProvider.getStandardDocumentDocType()) {
final XMLEventReader reader = provider.getEventReader(new StreamSource(inputStream));
assertDoesNotThrow(() -> processReader(reader));
}
}

@Test
void testGetEventReaderStandardExternalEntityException() throws IOException {
final StandardXMLEventReaderProvider provider = new StandardXMLEventReaderProvider();

try (final InputStream inputStream = ResourceProvider.getStandardDocumentDocTypeEntity()) {
final XMLEventReader reader = provider.getEventReader(new StreamSource(inputStream));
assertThrows(XMLStreamException.class, () -> processReader(reader));
}
}

private void processReader(final XMLEventReader reader) throws XMLStreamException {
while (reader.hasNext()) {
reader.nextEvent();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* 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.xml.processing.stream;

import org.apache.nifi.xml.processing.ResourceProvider;
import org.junit.jupiter.api.Test;

import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.stream.StreamSource;
import java.io.IOException;
import java.io.InputStream;

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

public class StandardXMLStreamReaderProviderTest {
@Test
void testGetStreamReaderStandard() throws IOException, XMLStreamException {
final StandardXMLStreamReaderProvider provider = new StandardXMLStreamReaderProvider();

try (final InputStream inputStream = ResourceProvider.getStandardDocument()) {
final XMLStreamReader reader = provider.getStreamReader(new StreamSource(inputStream));
processReader(reader);
}
}

@Test
void testGetStreamReaderStandardDocumentTypeDeclaration() throws IOException {
final StandardXMLStreamReaderProvider provider = new StandardXMLStreamReaderProvider();

try (final InputStream inputStream = ResourceProvider.getStandardDocumentDocType()) {
final XMLStreamReader reader = provider.getStreamReader(new StreamSource(inputStream));
assertDoesNotThrow(() -> processReader(reader));
}
}

@Test
void testGetStreamReaderStandardExternalEntityException() throws IOException {
final StandardXMLStreamReaderProvider provider = new StandardXMLStreamReaderProvider();

try (final InputStream inputStream = ResourceProvider.getStandardDocumentDocTypeEntity()) {
final XMLStreamReader reader = provider.getStreamReader(new StreamSource(inputStream));
assertThrows(XMLStreamException.class, () -> processReader(reader));
}
}

private void processReader(final XMLStreamReader reader) throws XMLStreamException {
while (reader.hasNext()) {
reader.next();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* 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.xml.processing.validation;

import org.apache.nifi.xml.processing.ProcessingException;
import org.apache.nifi.xml.processing.ResourceProvider;
import org.junit.jupiter.api.Test;
import org.xml.sax.SAXException;

import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;

import java.io.IOException;
import java.io.InputStream;

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

public class StandardSchemaValidatorTest {
private static final String SCHEMA_LANGUAGE = "http://www.w3.org/2001/XMLSchema";

@Test
void testValidate() throws SAXException, IOException {
final SchemaFactory schemaFactory = SchemaFactory.newInstance(SCHEMA_LANGUAGE);
final Schema schema;
try (final InputStream inputStream = ResourceProvider.getStandardSchema()) {
schema = schemaFactory.newSchema(new StreamSource(inputStream));
}

final StandardSchemaValidator validator = new StandardSchemaValidator();

try (final InputStream inputStream = ResourceProvider.getStandardNamespaceDocument()) {
validator.validate(schema, new StreamSource(inputStream));
}
}

@Test
void testValidateExternalEntityException() throws SAXException, IOException {
final SchemaFactory schemaFactory = SchemaFactory.newInstance(SCHEMA_LANGUAGE);
final Schema schema;
try (final InputStream inputStream = ResourceProvider.getStandardSchema()) {
schema = schemaFactory.newSchema(new StreamSource(inputStream));
}

final StandardSchemaValidator validator = new StandardSchemaValidator();

try (final InputStream inputStream = ResourceProvider.getStandardNamespaceDocumentDocTypeEntity()) {
final ProcessingException exception = assertThrows(ProcessingException.class, () -> validator.validate(schema, new StreamSource(inputStream)));
final Throwable cause = exception.getCause();
assertInstanceOf(SAXException.class, cause);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><!DOCTYPE standard [<!ENTITY entity SYSTEM 'file:///file-not-found'> %entity;]>
<!--
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.
-->
<standard>&entity;</standard>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><!DOCTYPE standard>
<!--
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.
-->
<standard />
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!--
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.
-->
<standard />
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><!DOCTYPE standard [<!ENTITY entity SYSTEM 'file:///file-not-found'> %entity;]>
<!--
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.
-->
<standard xmlns="urn:standard">&entity;</standard>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!--
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.
-->
<standard xmlns="urn:standard" />
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
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.
-->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:standard">
<xs:element name="standard" />
</xs:schema>
1 change: 1 addition & 0 deletions nifi-commons/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,6 @@
<module>nifi-vault-utils</module>
<module>nifi-web-utils</module>
<module>nifi-write-ahead-log</module>
<module>nifi-xml-processing</module>
</modules>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
Expand All @@ -36,6 +37,7 @@
import javax.xml.stream.XMLStreamWriter;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
Expand Down Expand Up @@ -410,7 +412,7 @@ public void forciblyInheritFingerprint(final String fingerprint) throws Authoriz

final PoliciesUsersAndGroups policiesUsersAndGroups = parsePoliciesUsersAndGroups(fingerprint);
if (isInheritable(policiesUsersAndGroups)) {
logger.debug("Inheriting Polciies, Users & Groups");
logger.debug("Inheriting Policies, Users & Groups");
inheritPoliciesUsersAndGroups(policiesUsersAndGroups);
} else {
logger.info("Cannot directly inherit Policies, Users & Groups. Will backup existing Policies, Users & Groups, and then replace with proposed configuration");
Expand All @@ -427,8 +429,7 @@ private PoliciesUsersAndGroups parsePoliciesUsersAndGroups(final String fingerpr

final byte[] fingerprintBytes = fingerprint.getBytes(StandardCharsets.UTF_8);
try (final ByteArrayInputStream in = new ByteArrayInputStream(fingerprintBytes)) {
final DocumentBuilder docBuilder = createSafeDocumentBuilder();
final Document document = docBuilder.parse(in);
final Document document = parseFingerprint(in);
final Element rootElement = document.getDocumentElement();

// parse all the users and add them to the current authorizer
Expand All @@ -451,14 +452,14 @@ private PoliciesUsersAndGroups parsePoliciesUsersAndGroups(final String fingerpr
Node policyNode = policyNodes.item(i);
accessPolicies.add(parsePolicy((Element) policyNode));
}
} catch (SAXException | ParserConfigurationException | IOException e) {
} catch (final IOException e) {
throw new AuthorizationAccessException("Unable to parse fingerprint", e);
}

return new PoliciesUsersAndGroups(accessPolicies, users, groups);
}

public static DocumentBuilder createSafeDocumentBuilder() throws ParserConfigurationException {
private Document parseFingerprint(final InputStream inputStream) throws IOException {
final DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
docFactory.setSchema(null);
docFactory.setNamespaceAware(true);
Expand All @@ -471,7 +472,13 @@ public static DocumentBuilder createSafeDocumentBuilder() throws ParserConfigura
docFactory.setXIncludeAware(false);
docFactory.setExpandEntityReferences(false);

return docFactory.newDocumentBuilder();
try {
docFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
final DocumentBuilder documentBuilder = docFactory.newDocumentBuilder();
return documentBuilder.parse(inputStream);
} catch (final ParserConfigurationException|SAXException e) {
throw new IOException("Fingerprint parsing failed", e);
}
}

private User parseUser(final Element element) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@
<version>1.16.1-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-xml-processing</artifactId>
<version>1.17.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@
import org.apache.nifi.processors.evtx.parser.MalformedChunkException;
import org.apache.nifi.processors.evtx.parser.Record;
import org.apache.nifi.processors.evtx.parser.bxml.RootNode;
import org.apache.nifi.security.xml.XmlUtils;
import org.apache.nifi.util.MockFlowFile;
import org.apache.nifi.util.TestRunner;
import org.apache.nifi.util.TestRunners;
import org.apache.nifi.xml.processing.parsers.StandardDocumentProvider;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
Expand All @@ -43,11 +43,8 @@
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.XMLStreamException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
Expand All @@ -59,8 +56,8 @@
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
Expand Down Expand Up @@ -159,7 +156,7 @@ public void testGetSupportedPropertyDescriptors() {
}

@Test
public void testProcessFileGranularity() throws IOException, MalformedChunkException, XMLStreamException {
public void testProcessFileGranularity() throws IOException, MalformedChunkException {
String basename = "basename";
int chunkNum = 5;
int offset = 10001;
Expand Down Expand Up @@ -203,7 +200,7 @@ public void testProcessFileGranularity() throws IOException, MalformedChunkExcep
}

@Test
public void testProcessChunkGranularity() throws IOException, MalformedChunkException, XMLStreamException {
public void testProcessChunkGranularity() throws IOException, MalformedChunkException {
String basename = "basename";
int chunkNum = 5;
int offset = 10001;
Expand Down Expand Up @@ -266,7 +263,7 @@ public void testProcessChunkGranularity() throws IOException, MalformedChunkExce
}

@Test
public void testProcess1RecordGranularity() throws IOException, MalformedChunkException, XMLStreamException {
public void testProcess1RecordGranularity() throws IOException, MalformedChunkException {
String basename = "basename";
int chunkNum = 5;
int offset = 10001;
Expand Down Expand Up @@ -340,7 +337,7 @@ public void testProcess1RecordGranularity() throws IOException, MalformedChunkEx
}

@Test
public void fileGranularityLifecycleTest() throws IOException, ParserConfigurationException, SAXException {
public void fileGranularityLifecycleTest() throws IOException {
String baseName = "testFileName";
String name = baseName + ".evtx";
TestRunner testRunner = TestRunners.newTestRunner(ParseEvtx.class);
Expand Down Expand Up @@ -374,7 +371,7 @@ public void fileGranularityLifecycleTest() throws IOException, ParserConfigurati
}

@Test
public void chunkGranularityLifecycleTest() throws IOException, ParserConfigurationException, SAXException {
public void chunkGranularityLifecycleTest() throws IOException {
String baseName = "testFileName";
String name = baseName + ".evtx";
TestRunner testRunner = TestRunners.newTestRunner(ParseEvtx.class);
Expand Down Expand Up @@ -406,7 +403,7 @@ public void chunkGranularityLifecycleTest() throws IOException, ParserConfigurat
}

@Test
public void recordGranularityLifecycleTest() throws IOException, ParserConfigurationException, SAXException {
public void recordGranularityLifecycleTest() throws IOException {
String baseName = "testFileName";
String name = baseName + ".evtx";
TestRunner testRunner = TestRunners.newTestRunner(ParseEvtx.class);
Expand Down Expand Up @@ -471,12 +468,13 @@ private void testValidEvents(String granularity, String filename, int expectedCo
testRunner.assertTransferCount(ParseEvtx.REL_SUCCESS, expectedCount);
}

private int validateFlowFiles(List<MockFlowFile> successFlowFiles) throws SAXException, IOException, ParserConfigurationException {
private int validateFlowFiles(List<MockFlowFile> successFlowFiles) {
assertTrue(successFlowFiles.size() > 0);
int totalSize = 0;
for (MockFlowFile successFlowFile : successFlowFiles) {
// Verify valid XML output
Document document = XmlUtils.createSafeDocumentBuilder(false).parse(new ByteArrayInputStream(successFlowFile.toByteArray()));
final StandardDocumentProvider documentProvider = new StandardDocumentProvider();
Document document = documentProvider.parse(successFlowFile.getContentStream());
Element documentElement = document.getDocumentElement();
assertEquals(XmlRootNodeHandler.EVENTS, documentElement.getTagName());
NodeList eventNodes = documentElement.getChildNodes();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@
import org.apache.nifi.bundle.Bundle;
import org.apache.nifi.nar.ExtensionManager;
import org.apache.nifi.properties.SensitivePropertyProviderFactoryAware;
import org.apache.nifi.security.xml.XmlUtils;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.util.file.classloader.ClassLoaderUtils;
import org.apache.nifi.xml.processing.ProcessingException;
import org.apache.nifi.xml.processing.stream.StandardXMLStreamReaderProvider;
import org.apache.nifi.xml.processing.stream.XMLStreamReaderProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
Expand All @@ -40,7 +42,6 @@
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
Expand Down Expand Up @@ -211,12 +212,13 @@ private Authorizers loadAuthorizersConfiguration() throws Exception {
final Schema schema = schemaFactory.newSchema(Authorizers.class.getResource(AUTHORIZERS_XSD));

// attempt to unmarshal
final XMLStreamReader xsr = XmlUtils.createSafeReader(new StreamSource(authorizersConfigurationFile));
final XMLStreamReaderProvider provider = new StandardXMLStreamReaderProvider();
final XMLStreamReader xsr = provider.getStreamReader(new StreamSource(authorizersConfigurationFile));
final Unmarshaller unmarshaller = JAXB_CONTEXT.createUnmarshaller();
unmarshaller.setSchema(schema);
final JAXBElement<Authorizers> element = unmarshaller.unmarshal(xsr, Authorizers.class);
return element.getValue();
} catch (XMLStreamException | SAXException | JAXBException e) {
} catch (final ProcessingException | SAXException | JAXBException e) {
throw new Exception("Unable to load the authorizer configuration file at: " + authorizersConfigurationFile.getAbsolutePath(), e);
}
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
<artifactId>nifi-utils</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-xml-processing</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,35 +16,35 @@
*/
package org.apache.nifi.documentation.html;

import java.io.IOException;
import java.io.StringReader;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;

import org.apache.nifi.xml.processing.ProcessingException;
import org.apache.nifi.xml.processing.parsers.DocumentProvider;
import org.apache.nifi.xml.processing.parsers.StandardDocumentProvider;
import org.junit.Assert;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

/**
* A helper class to validate xml documents.
*
*
*/
public class XmlValidator {
private static final String DOCTYPE = "<!DOCTYPE html>";

private static final String EMPTY = "";

/**
* Asserts a failure if the provided XML is not valid. <strong>This method does
* not use the "safe" {@link DocumentBuilderFactory} from
* {@code XmlUtils#createSafeDocumentBuilder(Schema, boolean)} because it checks
* generated documentation which contains a doctype. </strong>
* Asserts a failure if the provided XHTML is not valid
*
* @param xml the XML to validate
*/
public static void assertXmlValid(String xml) {
final String html = xml.replace(DOCTYPE, EMPTY);
try {
final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
dbf.newDocumentBuilder().parse(new InputSource(new StringReader(xml)));
} catch (SAXException | IOException | ParserConfigurationException e) {
final DocumentProvider provider = new StandardDocumentProvider();
provider.parse(new ByteArrayInputStream(html.getBytes(StandardCharsets.UTF_8)));
} catch (final ProcessingException e) {
Assert.fail(e.getMessage());
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,11 @@
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-security-utils</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-xml-processing</artifactId>
<version>1.17.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-properties</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,16 @@
import org.apache.nifi.authorization.util.IdentityMapping;
import org.apache.nifi.authorization.util.IdentityMappingUtil;
import org.apache.nifi.components.PropertyValue;
import org.apache.nifi.security.xml.XmlUtils;
import org.apache.nifi.user.generated.Users;
import org.apache.nifi.util.FlowInfo;
import org.apache.nifi.util.FlowParser;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.util.file.FileUtils;
import org.apache.nifi.web.api.dto.PortDTO;
import org.apache.nifi.xml.processing.ProcessingException;
import org.apache.nifi.xml.processing.parsers.StandardDocumentProvider;
import org.apache.nifi.xml.processing.stream.StandardXMLStreamReaderProvider;
import org.apache.nifi.xml.processing.stream.XMLStreamReaderProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
Expand All @@ -51,8 +54,6 @@
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
Expand Down Expand Up @@ -501,8 +502,8 @@ private List<AccessPolicy> parsePolicies(final String fingerprint) {

final byte[] fingerprintBytes = fingerprint.getBytes(StandardCharsets.UTF_8);
try (final ByteArrayInputStream in = new ByteArrayInputStream(fingerprintBytes)) {
final DocumentBuilder docBuilder = XmlUtils.createSafeDocumentBuilder(false);
final Document document = docBuilder.parse(in);
final StandardDocumentProvider documentProvider = new StandardDocumentProvider();
final Document document = documentProvider.parse(in);
final Element rootElement = document.getDocumentElement();

// parse all the policies and add them to the current access policy provider
Expand All @@ -511,7 +512,7 @@ private List<AccessPolicy> parsePolicies(final String fingerprint) {
Node policyNode = policyNodes.item(i);
policies.add(parsePolicy((Element) policyNode));
}
} catch (SAXException | ParserConfigurationException | IOException e) {
} catch (final ProcessingException | IOException e) {
throw new AuthorizationAccessException("Unable to parse fingerprint", e);
}

Expand Down Expand Up @@ -631,13 +632,14 @@ private void saveAuthorizations(final Authorizations authorizations, final File

private Authorizations unmarshallAuthorizations() throws JAXBException {
try {
final XMLStreamReader xsr = XmlUtils.createSafeReader(new StreamSource(authorizationsFile));
final XMLStreamReaderProvider provider = new StandardXMLStreamReaderProvider();
final XMLStreamReader xsr = provider.getStreamReader(new StreamSource(authorizationsFile));
final Unmarshaller unmarshaller = JAXB_AUTHORIZATIONS_CONTEXT.createUnmarshaller();
unmarshaller.setSchema(authorizationsSchema);

final JAXBElement<Authorizations> element = unmarshaller.unmarshal(xsr, Authorizations.class);
return element.getValue();
} catch (XMLStreamException e) {
} catch (final ProcessingException e) {
logger.error("Encountered an error reading authorizations file: ", e);
throw new JAXBException("Error reading authorizations file", e);
}
Expand Down Expand Up @@ -757,8 +759,9 @@ private void convertLegacyAuthorizedUsers(final Authorizations authorizations) t

final XMLStreamReader xsr;
try {
xsr = XmlUtils.createSafeReader(new StreamSource(authorizedUsersFile));
} catch (XMLStreamException e) {
final XMLStreamReaderProvider provider = new StandardXMLStreamReaderProvider();
xsr = provider.getStreamReader(new StreamSource(authorizedUsersFile));
} catch (final ProcessingException e) {
logger.error("Encountered an error reading authorized users file: ", e);
throw new JAXBException("Error reading authorized users file", e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,25 +28,25 @@
import org.apache.nifi.authorization.util.IdentityMapping;
import org.apache.nifi.authorization.util.IdentityMappingUtil;
import org.apache.nifi.components.PropertyValue;
import org.apache.nifi.security.xml.XmlUtils;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.util.file.FileUtils;
import org.apache.nifi.xml.processing.ProcessingException;
import org.apache.nifi.xml.processing.parsers.StandardDocumentProvider;
import org.apache.nifi.xml.processing.stream.StandardXMLStreamReaderProvider;
import org.apache.nifi.xml.processing.stream.XMLStreamReaderProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
Expand Down Expand Up @@ -598,8 +598,8 @@ private UsersAndGroups parseUsersAndGroups(final String fingerprint) {

final byte[] fingerprintBytes = fingerprint.getBytes(StandardCharsets.UTF_8);
try (final ByteArrayInputStream in = new ByteArrayInputStream(fingerprintBytes)) {
final DocumentBuilder docBuilder = XmlUtils.createSafeDocumentBuilder(false);
final Document document = docBuilder.parse(in);
final StandardDocumentProvider documentProvider = new StandardDocumentProvider();
final Document document = documentProvider.parse(in);
final Element rootElement = document.getDocumentElement();

// parse all the users and add them to the current user group provider
Expand All @@ -615,7 +615,7 @@ private UsersAndGroups parseUsersAndGroups(final String fingerprint) {
Node groupNode = groupNodes.item(i);
groups.add(parseGroup((Element) groupNode));
}
} catch (SAXException | ParserConfigurationException | IOException e) {
} catch (final ProcessingException | IOException e) {
throw new AuthorizationAccessException("Unable to parse fingerprint", e);
}

Expand Down Expand Up @@ -724,11 +724,12 @@ private Tenants unmarshallTenants() throws JAXBException {
final Unmarshaller unmarshaller = JAXB_TENANTS_CONTEXT.createUnmarshaller();
unmarshaller.setSchema(tenantsSchema);

final XMLStreamReaderProvider provider = new StandardXMLStreamReaderProvider();
try {
final XMLStreamReader xsr = XmlUtils.createSafeReader(new StreamSource(tenantsFile));
final XMLStreamReader xsr = provider.getStreamReader(new StreamSource(tenantsFile));
final JAXBElement<Tenants> element = unmarshaller.unmarshal(xsr, Tenants.class);
return element.getValue();
} catch (XMLStreamException e) {
} catch (final ProcessingException e) {
throw new JAXBException("Error unmarshalling tenants", e);
}
}
Expand All @@ -752,10 +753,11 @@ private void convertLegacyAuthorizedUsers(final Tenants tenants) throws Authoriz
throw new AuthorizerCreationException("Legacy Authorized Users File '" + legacyAuthorizedUsersFile + "' does not exists");
}

XMLStreamReader xsr;
final XMLStreamReaderProvider provider = new StandardXMLStreamReaderProvider();
final XMLStreamReader xsr;
try {
xsr = XmlUtils.createSafeReader(new StreamSource(authorizedUsersFile));
} catch (XMLStreamException e) {
xsr = provider.getStreamReader(new StreamSource(authorizedUsersFile));
} catch (final ProcessingException e) {
throw new AuthorizerCreationException("Error converting the legacy authorizers file", e);
}

Expand Down

This file was deleted.

Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@
<artifactId>nifi-security-utils</artifactId>
<version>1.16.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-xml-processing</artifactId>
<version>1.17.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,13 @@
import org.apache.nifi.authorization.exception.AuthorizerDestructionException;
import org.apache.nifi.authorization.exception.UninheritableAuthorizationsException;
import org.apache.nifi.components.PropertyValue;
import org.apache.nifi.security.xml.XmlUtils;
import org.apache.nifi.xml.processing.ProcessingException;
import org.apache.nifi.xml.processing.parsers.StandardDocumentProvider;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
Expand Down Expand Up @@ -239,12 +237,13 @@ public void checkInheritability(String proposedFingerprint) throws Authorization
}
}

private final FingerprintHolder parseFingerprint(final String fingerprint) throws AuthorizationAccessException {
private FingerprintHolder parseFingerprint(final String fingerprint) throws AuthorizationAccessException {
final byte[] fingerprintBytes = fingerprint.getBytes(StandardCharsets.UTF_8);

try (final ByteArrayInputStream in = new ByteArrayInputStream(fingerprintBytes)) {
final DocumentBuilder docBuilder = XmlUtils.createSafeDocumentBuilder(true);
final Document document = docBuilder.parse(in);
final StandardDocumentProvider documentProvider = new StandardDocumentProvider();
documentProvider.setNamespaceAware(true);
final Document document = documentProvider.parse(in);
final Element rootElement = document.getDocumentElement();

final NodeList accessPolicyProviderList = rootElement.getElementsByTagName(ACCESS_POLICY_PROVIDER_ELEMENT);
Expand All @@ -260,7 +259,7 @@ private final FingerprintHolder parseFingerprint(final String fingerprint) throw
final Node accessPolicyProvider = accessPolicyProviderList.item(0);
final Node userGroupProvider = userGroupProviderList.item(0);
return new FingerprintHolder(accessPolicyProvider.getTextContent(), userGroupProvider.getTextContent());
} catch (SAXException | ParserConfigurationException | IOException e) {
} catch (final ProcessingException | IOException e) {
throw new AuthorizationAccessException("Unable to parse fingerprint", e);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,16 @@
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-framework-core-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-security-utils</artifactId>
<version>1.17.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-xml-processing</artifactId>
<version>1.17.0-SNAPSHOT</version>
</dependency>

<!-- spring dependencies -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,17 @@
package org.apache.nifi.cluster.protocol;

import org.apache.nifi.cluster.coordination.node.NodeConnectionStatus;
import org.apache.nifi.security.xml.XmlUtils;
import org.apache.nifi.xml.processing.ProcessingException;
import org.apache.nifi.xml.processing.stream.StandardXMLStreamReaderProvider;
import org.apache.nifi.xml.processing.stream.XMLStreamReaderProvider;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.stream.StreamSource;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
Expand Down Expand Up @@ -122,9 +124,10 @@ public static void marshal(final HeartbeatPayload payload, final OutputStream os
public static HeartbeatPayload unmarshal(final InputStream is) throws ProtocolException {
try {
final Unmarshaller unmarshaller = JAXB_CONTEXT.createUnmarshaller();
final XMLStreamReader xsr = XmlUtils.createSafeReader(is);
final XMLStreamReaderProvider provider = new StandardXMLStreamReaderProvider();
final XMLStreamReader xsr = provider.getStreamReader(new StreamSource(is));
return (HeartbeatPayload) unmarshaller.unmarshal(xsr);
} catch (final JAXBException | XMLStreamException e) {
} catch (final JAXBException | ProcessingException e) {
throw new ProtocolException(e);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,17 @@
import org.apache.nifi.cluster.protocol.jaxb.message.DataFlowAdapter;
import org.apache.nifi.controller.flow.VersionedDataflow;
import org.apache.nifi.controller.serialization.FlowSerializationException;
import org.apache.nifi.security.xml.XmlUtils;
import org.apache.nifi.xml.processing.ProcessingException;
import org.apache.nifi.xml.processing.parsers.StandardDocumentProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;

import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.ParserConfigurationException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.Serializable;
import java.net.URL;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
Expand All @@ -48,7 +44,6 @@
*/
@XmlJavaTypeAdapter(DataFlowAdapter.class)
public class StandardDataFlow implements Serializable, DataFlow {
private static final URL FLOW_XSD_RESOURCE = StandardDataFlow.class.getClassLoader().getResource("/FlowConfiguration.xsd");
private static final Logger logger = LoggerFactory.getLogger(StandardDataFlow.class);

private static final long serialVersionUID = 1L;
Expand Down Expand Up @@ -135,21 +130,19 @@ private static Document parseFlowBytes(final byte[] flow) throws FlowSerializati
return null;
}

// create document by parsing proposed flow bytes
try {
// create validating document builder
final DocumentBuilder docBuilder = XmlUtils.createSafeDocumentBuilder(true);
docBuilder.setErrorHandler(new DefaultHandler() {
final StandardDocumentProvider documentProvider = new StandardDocumentProvider();
documentProvider.setNamespaceAware(true);
documentProvider.setErrorHandler(new DefaultHandler() {
@Override
public void error(final SAXParseException e) {
logger.warn("Schema validation error parsing Flow Configuration at line {}, col {}: {}", e.getLineNumber(), e.getColumnNumber(), e.getMessage());
}
});

// parse flow
return docBuilder.parse(new ByteArrayInputStream(flow));
} catch (final SAXException | ParserConfigurationException | IOException ex) {
throw new FlowSerializationException(ex);
return documentProvider.parse(new ByteArrayInputStream(flow));
} catch (final ProcessingException e) {
throw new FlowSerializationException("Flow parsing failed", e);
}
}

Expand All @@ -163,8 +156,7 @@ private VersionedDataflow parseVersionedDataflow(final byte[] flow) {
objectMapper.setAnnotationIntrospector(new JaxbAnnotationIntrospector(objectMapper.getTypeFactory()));
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

final VersionedDataflow versionedDataflow = objectMapper.readValue(flow, VersionedDataflow.class);
return versionedDataflow;
return objectMapper.readValue(flow, VersionedDataflow.class);
} catch (final Exception e) {
throw new FlowSerializationException("Could not parse flow as a VersionedDataflow", e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,16 @@
import org.apache.nifi.cluster.protocol.ProtocolContext;
import org.apache.nifi.cluster.protocol.ProtocolMessageMarshaller;
import org.apache.nifi.cluster.protocol.ProtocolMessageUnmarshaller;
import org.apache.nifi.security.xml.XmlUtils;
import org.apache.nifi.xml.processing.ProcessingException;
import org.apache.nifi.xml.processing.stream.StandardXMLStreamReaderProvider;
import org.apache.nifi.xml.processing.stream.XMLStreamReaderProvider;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.stream.StreamSource;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
Expand Down Expand Up @@ -137,10 +139,11 @@ public T unmarshal(final InputStream is) throws IOException {
final Unmarshaller unmarshaller = jaxbCtx.createUnmarshaller();
final byte[] msg = new byte[totalBytesRead];
buffer.get(msg);
final XMLStreamReader xsr = XmlUtils.createSafeReader(new ByteArrayInputStream(msg));
final XMLStreamReaderProvider provider = new StandardXMLStreamReaderProvider();
final XMLStreamReader xsr = provider.getStreamReader(new StreamSource(new ByteArrayInputStream(msg)));
return (T) unmarshaller.unmarshal(xsr);

} catch (final JAXBException | XMLStreamException e) {
} catch (final JAXBException | ProcessingException e) {
throw new IOException("Failed unmarshalling protocol message due to: " + e, e);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,16 @@
<artifactId>nifi-property-encryptor</artifactId>
<version>1.16.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-security-utils</artifactId>
<version>1.17.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-xml-processing</artifactId>
<version>1.17.0-SNAPSHOT</version>
</dependency>

<dependency>
<groupId>org.apache.nifi.registry</groupId>
Expand Down
Loading