1 /* 2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"). 5 * You may not use this file except in compliance with the License. 6 * A copy of the License is located at 7 * 8 * http://aws.amazon.com/apache2.0 9 * 10 * or in the "license" file accompanying this file. This file is distributed 11 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 * express or implied. See the License for the specific language governing 13 * permissions and limitations under the License. 14 */ 15 16 package software.amazon.awssdk.protocol.asserts.marshalling; 17 18 import static org.junit.Assert.fail; 19 20 import java.io.StringWriter; 21 import java.io.Writer; 22 import javax.xml.XMLConstants; 23 import javax.xml.parsers.DocumentBuilder; 24 import javax.xml.parsers.DocumentBuilderFactory; 25 import javax.xml.parsers.ParserConfigurationException; 26 import javax.xml.transform.OutputKeys; 27 import javax.xml.transform.Transformer; 28 import javax.xml.transform.TransformerFactory; 29 import javax.xml.transform.dom.DOMSource; 30 import javax.xml.transform.stream.StreamResult; 31 import org.custommonkey.xmlunit.Diff; 32 import org.custommonkey.xmlunit.examples.RecursiveElementNameAndTextQualifier; 33 import org.w3c.dom.Document; 34 import software.amazon.awssdk.utils.StringInputStream; 35 36 public final class XmlAsserts { 37 38 private static final DocumentBuilder DOCUMENT_BUILDER = getDocumentBuilder(); 39 XmlAsserts()40 private XmlAsserts() { 41 } 42 getDocumentBuilder()43 private static DocumentBuilder getDocumentBuilder() { 44 DocumentBuilderFactory dbf = newSecureDocumentBuilderFactory(); 45 try { 46 return dbf.newDocumentBuilder(); 47 } catch (ParserConfigurationException e) { 48 throw new RuntimeException(e); 49 } 50 } 51 assertXmlEquals(String expectedXml, String actualXml)52 public static void assertXmlEquals(String expectedXml, String actualXml) { 53 try { 54 doAssertXmlEquals(expectedXml, actualXml); 55 } catch (Exception e) { 56 e.printStackTrace(); 57 fail(e.getMessage()); 58 } 59 } 60 doAssertXmlEquals(String expectedXml, String actualXml)61 private static void doAssertXmlEquals(String expectedXml, String actualXml) throws Exception { 62 Diff diff = new Diff(expectedXml, actualXml); 63 diff.overrideElementQualifier(new RecursiveElementNameAndTextQualifier()); 64 if (!diff.similar()) { 65 fail("\nExpected the following XML\n" + formatXml(expectedXml) + 66 "\nbut actual XML was\n\n" + 67 formatXml(actualXml)); 68 } 69 } 70 formatXml(String xmlDocumentString)71 private static String formatXml(String xmlDocumentString) throws Exception { 72 return formatXml(DOCUMENT_BUILDER.parse(new StringInputStream(xmlDocumentString))); 73 } 74 formatXml(Document xmlDocument)75 private static String formatXml(Document xmlDocument) throws Exception { 76 Transformer transformer = newSecureTransformerFactory().newTransformer(); 77 transformer.setOutputProperty(OutputKeys.INDENT, "yes"); 78 StreamResult result = new StreamResult(new StringWriter()); 79 DOMSource source = new DOMSource(xmlDocument); 80 transformer.transform(source, result); 81 try (Writer writer = result.getWriter()) { 82 return writer.toString(); 83 } 84 } 85 newSecureDocumentBuilderFactory()86 private static DocumentBuilderFactory newSecureDocumentBuilderFactory() { 87 DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); 88 docFactory.setXIncludeAware(false); 89 docFactory.setExpandEntityReferences(false); 90 trySetFeature(docFactory, XMLConstants.FEATURE_SECURE_PROCESSING, true); 91 trySetFeature(docFactory, "http://apache.org/xml/features/disallow-doctype-decl", true); 92 trySetFeature(docFactory, "http://xml.org/sax/features/external-general-entities", false); 93 trySetFeature(docFactory, "http://xml.org/sax/features/external-parameter-entities", false); 94 trySetAttribute(docFactory, "http://javax.xml.XMLConstants/property/accessExternalDTD", ""); 95 trySetAttribute(docFactory, "http://javax.xml.XMLConstants/property/accessExternalSchema", ""); 96 return docFactory; 97 } 98 newSecureTransformerFactory()99 private static TransformerFactory newSecureTransformerFactory() { 100 TransformerFactory transformerFactory = TransformerFactory.newInstance(); 101 trySetAttribute(transformerFactory, XMLConstants.ACCESS_EXTERNAL_DTD, ""); 102 trySetAttribute(transformerFactory, XMLConstants.ACCESS_EXTERNAL_STYLESHEET, ""); 103 return transformerFactory; 104 } 105 trySetFeature(DocumentBuilderFactory factory, String feature, boolean value)106 private static void trySetFeature(DocumentBuilderFactory factory, String feature, boolean value) { 107 try { 108 factory.setFeature(feature, value); 109 } catch (Exception e) { 110 throw new RuntimeException(e); 111 } 112 } 113 trySetAttribute(DocumentBuilderFactory factory, String feature, String value)114 private static void trySetAttribute(DocumentBuilderFactory factory, String feature, String value) { 115 try { 116 factory.setAttribute(feature, value); 117 } catch (Exception e) { 118 throw new RuntimeException(e); 119 } 120 } 121 trySetAttribute(TransformerFactory factory, String feature, Object value)122 private static void trySetAttribute(TransformerFactory factory, String feature, Object value) { 123 try { 124 factory.setAttribute(feature, value); 125 } catch (Exception e) { 126 throw new RuntimeException(e); 127 } 128 } 129 } 130