UPDATE Added additional tests, security config, and fix format issues (#7421)

This commit is contained in:
Juan Moreno 2019-08-05 19:35:34 -03:00 committed by KevinGilmore
parent a56d3b1e58
commit a02fcfbb06
7 changed files with 116 additions and 56 deletions

View File

@ -3,6 +3,7 @@ package com.baeldung.xml.attribute;
import org.dom4j.*; import org.dom4j.*;
import org.dom4j.io.DocumentSource; import org.dom4j.io.DocumentSource;
import org.dom4j.io.SAXReader; import org.dom4j.io.SAXReader;
import org.xml.sax.SAXException;
import javax.xml.XMLConstants; import javax.xml.XMLConstants;
import javax.xml.transform.OutputKeys; import javax.xml.transform.OutputKeys;
@ -17,9 +18,12 @@ import java.util.List;
public class Dom4jTransformer { public class Dom4jTransformer {
private final Document input; private final Document input;
public Dom4jTransformer(String resourcePath) throws DocumentException { public Dom4jTransformer(String resourcePath) throws DocumentException, SAXException {
// 1- Build the doc from the XML file // 1- Build the doc from the XML file
SAXReader xmlReader = new SAXReader(); SAXReader xmlReader = new SAXReader();
xmlReader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
xmlReader.setFeature("http://xml.org/sax/features/external-general-entities", false);
xmlReader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
this.input = xmlReader.read(resourcePath); this.input = xmlReader.read(resourcePath);
} }

View File

@ -32,7 +32,8 @@ public class JaxpTransformer {
// 1- Build the doc from the XML file // 1- Build the doc from the XML file
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
input = factory.newDocumentBuilder() factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
this.input = factory.newDocumentBuilder()
.parse(resourcePath); .parse(resourcePath);
} }
@ -40,24 +41,24 @@ public class JaxpTransformer {
// 2- Locate the node(s) with xpath // 2- Locate the node(s) with xpath
XPath xpath = XPathFactory.newInstance() XPath xpath = XPathFactory.newInstance()
.newXPath(); .newXPath();
NodeList nodes = (NodeList) xpath.evaluate(String.format("//*[contains(@%s, '%s')]", attribute, oldValue), input, XPathConstants.NODESET); NodeList nodes = (NodeList) xpath.evaluate(String.format("//*[contains(@%s, '%s')]", attribute, oldValue), this.input, XPathConstants.NODESET);
// 3- Make the change on the selected nodes // 3- Make the change on the selected nodes
for (int i = 0; i < nodes.getLength(); i++) { for (int i = 0; i < nodes.getLength(); i++) {
Element value = (Element) nodes.item(i); Element value = (Element) nodes.item(i);
value.setAttribute(attribute, newValue); value.setAttribute(attribute, newValue);
} }
//Stream api syntax // Stream api syntax
// IntStream // IntStream
// .range(0, nodes.getLength()) // .range(0, nodes.getLength())
// .mapToObj(i -> (Element) nodes.item(i)) // .mapToObj(i -> (Element) nodes.item(i))
// .forEach(value -> value.setAttribute(attribute, newValue)); // .forEach(value -> value.setAttribute(attribute, newValue));
// 4- Save the result to a new XML doc // 4- Save the result to a new XML doc
TransformerFactory factory = TransformerFactory.newInstance(); TransformerFactory factory = TransformerFactory.newInstance();
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
Transformer xformer = factory.newTransformer(); Transformer xformer = factory.newTransformer();
xformer.setOutputProperty(OutputKeys.INDENT, "yes"); xformer.setOutputProperty(OutputKeys.INDENT, "yes");
Writer output = new StringWriter(); Writer output = new StringWriter();
xformer.transform(new DOMSource(input), new StreamResult(output)); xformer.transform(new DOMSource(this.input), new StreamResult(output));
return output.toString(); return output.toString();
} }
} }

View File

@ -1,7 +1,19 @@
package com.baeldung.xml.attribute.jmh; package com.baeldung.xml.attribute.jmh;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.xpath.XPathExpressionException;
import org.dom4j.DocumentException; import org.dom4j.DocumentException;
import org.openjdk.jmh.annotations.*; import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.runner.Runner; import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException; import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options; import org.openjdk.jmh.runner.options.Options;
@ -12,30 +24,23 @@ import com.baeldung.xml.attribute.Dom4jTransformer;
import com.baeldung.xml.attribute.JaxpTransformer; import com.baeldung.xml.attribute.JaxpTransformer;
import com.baeldung.xml.attribute.JooxTransformer; import com.baeldung.xml.attribute.JooxTransformer;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.xpath.XPathExpressionException;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
@BenchmarkMode(Mode.AverageTime) @BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS) @OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Benchmark) @State(Scope.Benchmark)
public class AttributeBenchMark { public class AttributeBenchMark {
public static void main(String[] args) throws RunnerException { public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder() Options opt = new OptionsBuilder().include(AttributeBenchMark.class.getSimpleName())
.include(AttributeBenchMark.class.getSimpleName()) .forks(1)
.forks(1) .build();
.build();
new Runner(opt).run(); new Runner(opt).run();
} }
@Benchmark @Benchmark
public String dom4jBenchmark() throws DocumentException, TransformerException { public String dom4jBenchmark() throws DocumentException, TransformerException, SAXException {
String path = getClass() String path = this.getClass()
.getResource("/xml/attribute.xml") .getResource("/xml/attribute.xml")
.toString(); .toString();
Dom4jTransformer transformer = new Dom4jTransformer(path); Dom4jTransformer transformer = new Dom4jTransformer(path);
String attribute = "customer"; String attribute = "customer";
String oldValue = "true"; String oldValue = "true";
@ -46,9 +51,9 @@ public class AttributeBenchMark {
@Benchmark @Benchmark
public String jooxBenchmark() throws IOException, SAXException { public String jooxBenchmark() throws IOException, SAXException {
String path = getClass() String path = this.getClass()
.getResource("/xml/attribute.xml") .getResource("/xml/attribute.xml")
.toString(); .toString();
JooxTransformer transformer = new JooxTransformer(path); JooxTransformer transformer = new JooxTransformer(path);
String attribute = "customer"; String attribute = "customer";
String oldValue = "true"; String oldValue = "true";
@ -59,9 +64,9 @@ public class AttributeBenchMark {
@Benchmark @Benchmark
public String jaxpBenchmark() throws TransformerException, ParserConfigurationException, SAXException, IOException, XPathExpressionException { public String jaxpBenchmark() throws TransformerException, ParserConfigurationException, SAXException, IOException, XPathExpressionException {
String path = getClass() String path = this.getClass()
.getResource("/xml/attribute.xml") .getResource("/xml/attribute.xml")
.toString(); .toString();
JaxpTransformer transformer = new JaxpTransformer(path); JaxpTransformer transformer = new JaxpTransformer(path);
String attribute = "customer"; String attribute = "customer";
String oldValue = "true"; String oldValue = "true";

View File

@ -2,14 +2,19 @@ package com.baeldung.xml.attribute;
import org.dom4j.DocumentException; import org.dom4j.DocumentException;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.xml.sax.SAXException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactoryConfigurationError; import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.xpath.XPathExpressionException;
import java.io.IOException; import java.io.IOException;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Paths; import java.nio.file.Paths;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.xmlunit.assertj.XmlAssert.assertThat; import static org.xmlunit.assertj.XmlAssert.assertThat;
/** /**
@ -18,10 +23,9 @@ import static org.xmlunit.assertj.XmlAssert.assertThat;
public class Dom4jProcessorUnitTest { public class Dom4jProcessorUnitTest {
@Test @Test
public void givenXmlWithAttributes_whenModifyAttribute_thenGetXmlUpdated() throws TransformerFactoryConfigurationError, TransformerException, DocumentException { public void givenXmlWithAttributes_whenModifyAttribute_thenGetXmlUpdated() throws TransformerFactoryConfigurationError, TransformerException, DocumentException, SAXException {
String path = getClass() String path = getClass().getResource("/xml/attribute.xml")
.getResource("/xml/attribute.xml") .toString();
.toString();
Dom4jTransformer transformer = new Dom4jTransformer(path); Dom4jTransformer transformer = new Dom4jTransformer(path);
String attribute = "customer"; String attribute = "customer";
String oldValue = "true"; String oldValue = "true";
@ -33,23 +37,32 @@ public class Dom4jProcessorUnitTest {
} }
@Test @Test
public void givenTwoXml_whenModifyAttribute_thenGetSimilarXml() throws IOException, TransformerFactoryConfigurationError, TransformerException, URISyntaxException, DocumentException { public void givenTwoXml_whenModifyAttribute_thenGetSimilarXml() throws IOException, TransformerFactoryConfigurationError, TransformerException, URISyntaxException, DocumentException, SAXException {
String path = getClass() String path = getClass().getResource("/xml/attribute.xml")
.getResource("/xml/attribute.xml") .toString();
.toString();
Dom4jTransformer transformer = new Dom4jTransformer(path); Dom4jTransformer transformer = new Dom4jTransformer(path);
String attribute = "customer"; String attribute = "customer";
String oldValue = "true"; String oldValue = "true";
String newValue = "false"; String newValue = "false";
String expectedXml = new String(Files.readAllBytes((Paths.get(getClass() String expectedXml = new String(Files.readAllBytes((Paths.get(getClass().getResource("/xml/attribute_expected.xml")
.getResource("/xml/attribute_expected.xml") .toURI()))));
.toURI()))));
String result = transformer.modifyAttribute(attribute, oldValue, newValue); String result = transformer.modifyAttribute(attribute, oldValue, newValue);
assertThat(result) assertThat(result).and(expectedXml)
.and(expectedXml) .areSimilar();
.areSimilar(); }
@Test
public void givenXmlXee_whenInit_thenThrowException() throws IOException, SAXException, ParserConfigurationException, XPathExpressionException, TransformerFactoryConfigurationError, TransformerException {
String path = getClass().getResource("/xml/xee_attribute.xml")
.toString();
assertThatThrownBy(() -> {
new Dom4jTransformer(path);
}).isInstanceOf(DocumentException.class);
} }
} }

View File

@ -1,5 +1,6 @@
package com.baeldung.xml.attribute; package com.baeldung.xml.attribute;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.xmlunit.assertj.XmlAssert.assertThat; import static org.xmlunit.assertj.XmlAssert.assertThat;
import java.io.IOException; import java.io.IOException;
@ -11,6 +12,7 @@ import javax.xml.xpath.XPathExpressionException;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
/** /**
* Unit test for {@link JaxpTransformer}. * Unit test for {@link JaxpTransformer}.
@ -31,4 +33,16 @@ public class JaxpProcessorUnitTest {
assertThat(result).hasXPath("//*[contains(@customer, 'false')]"); assertThat(result).hasXPath("//*[contains(@customer, 'false')]");
} }
@Test
public void givenXmlXee_whenInit_thenThrowException() throws IOException, SAXException, ParserConfigurationException, XPathExpressionException, TransformerFactoryConfigurationError, TransformerException {
String path = getClass().getResource("/xml/xee_attribute.xml")
.toString();
assertThatThrownBy(() -> {
new JaxpTransformer(path);
}).isInstanceOf(SAXParseException.class);
}
} }

View File

@ -2,13 +2,19 @@ package com.baeldung.xml.attribute;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactoryConfigurationError; import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.xpath.XPathExpressionException;
import java.io.IOException; import java.io.IOException;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Paths; import java.nio.file.Paths;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.xmlunit.assertj.XmlAssert.assertThat; import static org.xmlunit.assertj.XmlAssert.assertThat;
/** /**
@ -18,9 +24,8 @@ public class JooxProcessorUnitTest {
@Test @Test
public void givenXmlWithAttributes_whenModifyAttribute_thenGetXmlUpdated() throws IOException, SAXException, TransformerFactoryConfigurationError { public void givenXmlWithAttributes_whenModifyAttribute_thenGetXmlUpdated() throws IOException, SAXException, TransformerFactoryConfigurationError {
String path = getClass() String path = getClass().getResource("/xml/attribute.xml")
.getResource("/xml/attribute.xml") .toString();
.toString();
JooxTransformer transformer = new JooxTransformer(path); JooxTransformer transformer = new JooxTransformer(path);
String attribute = "customer"; String attribute = "customer";
String oldValue = "true"; String oldValue = "true";
@ -33,22 +38,31 @@ public class JooxProcessorUnitTest {
@Test @Test
public void givenTwoXml_whenModifyAttribute_thenGetSimilarXml() throws IOException, TransformerFactoryConfigurationError, URISyntaxException, SAXException { public void givenTwoXml_whenModifyAttribute_thenGetSimilarXml() throws IOException, TransformerFactoryConfigurationError, URISyntaxException, SAXException {
String path = getClass() String path = getClass().getResource("/xml/attribute.xml")
.getResource("/xml/attribute.xml") .toString();
.toString();
JooxTransformer transformer = new JooxTransformer(path); JooxTransformer transformer = new JooxTransformer(path);
String attribute = "customer"; String attribute = "customer";
String oldValue = "true"; String oldValue = "true";
String newValue = "false"; String newValue = "false";
String expectedXml = new String(Files.readAllBytes((Paths.get(getClass() String expectedXml = new String(Files.readAllBytes((Paths.get(getClass().getResource("/xml/attribute_expected.xml")
.getResource("/xml/attribute_expected.xml") .toURI()))));
.toURI()))));
String result = transformer.modifyAttribute(attribute, oldValue, newValue); String result = transformer.modifyAttribute(attribute, oldValue, newValue);
assertThat(result) assertThat(result).and(expectedXml)
.and(expectedXml) .areSimilar();
.areSimilar(); }
@Test
public void givenXmlXee_whenInit_thenThrowException() throws IOException, SAXException, ParserConfigurationException, XPathExpressionException, TransformerFactoryConfigurationError, TransformerException {
String path = getClass().getResource("/xml/xee_attribute.xml")
.toString();
assertThatThrownBy(() -> {
new JooxTransformer(path);
}).isInstanceOf(SAXParseException.class);
} }
} }

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE notification [
<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
<notification id="5">
<bad_entry>&xxe;
</bad_entry>
<to customer="true">john@email.com</to>
<from>mary@email.com</from>
</notification>