PageRenderTime 47ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/src/test/java/SaxParserBehaviourTest.java

https://bitbucket.org/jwalton/xml-parser-sanity-check
Java | 228 lines | 165 code | 50 blank | 13 comment | 0 complexity | cc45c696f2d158c417c589789bccc760 MD5 | raw file
  1. import java.io.ByteArrayInputStream;
  2. import java.io.IOException;
  3. import java.io.StringReader;
  4. import java.io.UnsupportedEncodingException;
  5. import javax.xml.XMLConstants;
  6. import javax.xml.parsers.ParserConfigurationException;
  7. import javax.xml.parsers.SAXParser;
  8. import javax.xml.parsers.SAXParserFactory;
  9. import com.atlassian.security.xml.UntrustedXmlParserFactory;
  10. import org.junit.Test;
  11. import org.w3c.dom.Document;
  12. import org.xml.sax.EntityResolver;
  13. import org.xml.sax.InputSource;
  14. import org.xml.sax.SAXException;
  15. import org.xml.sax.SAXNotRecognizedException;
  16. import org.xml.sax.SAXParseException;
  17. import org.xml.sax.XMLReader;
  18. import org.xml.sax.helpers.DefaultHandler;
  19. import static org.junit.Assert.assertEquals;
  20. import static org.junit.Assert.assertFalse;
  21. /**
  22. * There are two ways to use SAX parsers, through {@link SAXParser} and through {@link XMLReader}.
  23. * In both cases, you must ensure the entity resolver is set to an implementation that throws IOException
  24. * for all resolution attempts.
  25. */
  26. public class SaxParserBehaviourTest
  27. {
  28. static SAXParser createSaxParser() throws ParserConfigurationException, SAXException
  29. {
  30. SAXParserFactory spf = SAXParserFactory.newInstance();
  31. spf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
  32. // This will turn any attempt to use a DTD into failure
  33. // spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
  34. // Only necessary for bundled non-JDK Xerces
  35. spf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
  36. // Note that it's the EntityResolver in DefaultHandler that looks up entities.
  37. // You must throw an IOException in the resolver to prevent it hitting the specified path
  38. return spf.newSAXParser();
  39. }
  40. private static InputSource EMPTY_INPUT_SOURCE = new InputSource(new ByteArrayInputStream(new byte[0]));
  41. private static EntityResolver emptyEntityResolver = new EntityResolver()
  42. {
  43. public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException
  44. {
  45. return EMPTY_INPUT_SOURCE;
  46. }
  47. };
  48. static XMLReader createXmlReader() throws ParserConfigurationException, SAXException
  49. {
  50. return UntrustedXmlParserFactory.newXmlReader();
  51. // XMLReader xr = createSaxParser().getXMLReader();
  52. // xr.setEntityResolver(emptyEntityResolver);
  53. // return xr;
  54. }
  55. static XMLReader createNamespaceAwareXmlReader() throws ParserConfigurationException, SAXException
  56. {
  57. return UntrustedXmlParserFactory.newNamespaceAwareXmlReader();
  58. }
  59. @Test(expected = SAXNotRecognizedException.class)
  60. public void askingForUnknownAttributesFails() throws Exception
  61. {
  62. SAXParserFactory spf = SAXParserFactory.newInstance();
  63. spf.setFeature("no-attribute-with-this-identifier", false);
  64. }
  65. static InputSource in(String content) throws UnsupportedEncodingException
  66. {
  67. return new InputSource(new ByteArrayInputStream(content.getBytes("us-ascii")));
  68. }
  69. @Test
  70. public void parseDocumentExpandsAmpersand() throws Exception
  71. {
  72. XMLReader xr = createXmlReader();
  73. StringGathererHandler sgh = new StringGathererHandler();
  74. xr.setContentHandler(sgh);
  75. xr.parse(in(SampleXmlDocuments.AMPERSAND_DOCUMENT));
  76. assertEquals("&", sgh.toString());
  77. }
  78. static class StringGathererHandler extends DefaultHandler
  79. {
  80. private StringBuilder sb = new StringBuilder();
  81. @Override
  82. public void characters(char[] ch, int start, int length) throws SAXException
  83. {
  84. sb.append(ch, start, length);
  85. }
  86. @Override
  87. public String toString()
  88. {
  89. return sb.toString();
  90. }
  91. @Override
  92. public InputSource resolveEntity(String publicId, String systemId) throws IOException, SAXException
  93. {
  94. throw new IOException();
  95. }
  96. }
  97. static class StringGathererContentHandler extends DefaultHandler
  98. {
  99. private StringBuilder sb = new StringBuilder();
  100. @Override
  101. public void characters(char[] ch, int start, int length) throws SAXException
  102. {
  103. sb.append(ch, start, length);
  104. }
  105. @Override
  106. public String toString()
  107. {
  108. return sb.toString();
  109. }
  110. }
  111. static class FailingEntityResolver implements EntityResolver
  112. {
  113. public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException
  114. {
  115. throw new IOException();
  116. }
  117. }
  118. @Test(expected = SAXParseException.class, timeout = 1000)
  119. public void parseBillionLaughsDoesNotExhaustMemory() throws Exception
  120. {
  121. StringGathererHandler sgh = new StringGathererHandler();
  122. XMLReader xr = createXmlReader();
  123. xr.setContentHandler(sgh);
  124. xr.parse(in(SampleXmlDocuments.BILLION_LAUGHS));
  125. assertEquals("", sgh.toString());
  126. }
  127. @Test
  128. public void externalEntityIsNotRead() throws Exception
  129. {
  130. StringGathererHandler sgh = new StringGathererHandler();
  131. XMLReader xr = createXmlReader();
  132. xr.setContentHandler(sgh);
  133. xr.parse(in(SampleXmlDocuments.externalResourceEntity()));
  134. assertEquals("", sgh.toString());
  135. }
  136. @Test
  137. public void externalEntityIsNotIncludedInResultUsingXmlReader() throws Exception
  138. {
  139. StringGathererContentHandler sgh = new StringGathererContentHandler();
  140. XMLReader xr = createXmlReader();
  141. xr.setContentHandler(sgh);
  142. xr.parse(in(SampleXmlDocuments.externalResourceEntity()));
  143. assertEquals("", sgh.toString());
  144. }
  145. @Test
  146. public void externalEntityIsNotReadUsingXmlReader() throws Exception
  147. {
  148. HttpAttemptDetector detector = new HttpAttemptDetector();
  149. new Thread(detector).start();
  150. StringGathererContentHandler sgh = new StringGathererContentHandler();
  151. XMLReader xr = createXmlReader();
  152. xr.setContentHandler(sgh);
  153. xr.parse(in(SampleXmlDocuments.externalResourceEntity(detector.getUrl())));
  154. assertFalse(detector.wasAttempted());
  155. }
  156. @Test
  157. public void dtdUriPointsToFile() throws Exception
  158. {
  159. StringGathererHandler sgh = new StringGathererHandler();
  160. XMLReader xr = createXmlReader();
  161. xr.setContentHandler(sgh);
  162. xr.parse(in(SampleXmlDocuments.EXTERNAL_DTD));
  163. assertEquals("", sgh.toString());
  164. }
  165. @Test
  166. public void dtdUriPointsToUrl() throws Exception
  167. {
  168. HttpAttemptDetector detector = new HttpAttemptDetector();
  169. new Thread(detector).start();
  170. String s = SampleXmlDocuments.externalUrlDtd(detector.getUrl());
  171. StringGathererHandler sgh = new StringGathererHandler();
  172. XMLReader xr = createXmlReader();
  173. xr.setContentHandler(sgh);
  174. xr.parse(in(s));
  175. assertFalse("I don't want to see HTTP connection attempts", detector.wasAttempted());
  176. assertEquals("", sgh.toString());
  177. }
  178. }