1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.xml.secure;
19
20 import static org.junit.jupiter.api.Assertions.assertFalse;
21
22 import java.io.StringWriter;
23
24 import javax.xml.XMLConstants;
25 import javax.xml.parsers.DocumentBuilder;
26 import javax.xml.parsers.SAXParser;
27 import javax.xml.transform.Transformer;
28 import javax.xml.transform.TransformerException;
29 import javax.xml.transform.stream.StreamResult;
30 import javax.xml.validation.Validator;
31
32 import org.junit.jupiter.api.Assumptions;
33 import org.junit.jupiter.api.Tag;
34 import org.junit.jupiter.api.Test;
35 import org.w3c.dom.Document;
36 import org.xml.sax.SAXException;
37 import org.xml.sax.XMLReader;
38
39
40
41
42
43
44
45
46
47
48
49 class ResetSecureTest {
50
51
52
53
54 private static final String UNLISTED = AttackTestSupport.resourceUrl("referenced.xml").toString();
55
56 private static String entityPayload(final String entitySystemId) {
57 return "<?xml version=\"1.0\"?>\n"
58 + "<!DOCTYPE root [\n <!ENTITY xxe SYSTEM \"" + entitySystemId + "\">\n]>\n"
59 + "<root>&xxe;</root>";
60 }
61
62 @Test
63 @Tag("dom")
64 void documentBuilderResetKeepsEntityResolverFloor() throws Exception {
65 Assumptions.assumeTrue(AttackTestSupport.DOM_RESOLVES_INTERNAL_ENTITIES, "platform DOM does not resolve user-defined entities");
66 final DocumentBuilder builder = SecureDocumentBuilderFactory.newInstance().newDocumentBuilder();
67 AttackTestSupport.assumeDoesNotThrow(builder::reset);
68 try {
69 final Document doc = builder.parse(AttackTestSupport.inputSource(entityPayload(UNLISTED)));
70 assertFalse(doc.getDocumentElement().getTextContent().contains(AttackTestSupport.LEAKED_MARKER), "external entity leaked after reset");
71 } catch (final SAXException blocked) {
72
73 }
74 }
75
76 @Test
77 @Tag("sax")
78 void saxParserResetKeepsEntityResolverFloor() throws Exception {
79 final SAXParser parser = SecureSAXParserFactory.newInstance().newSAXParser();
80
81 parser.getXMLReader();
82 AttackTestSupport.assumeDoesNotThrow(parser::reset);
83 final XMLReader reader = parser.getXMLReader();
84 final String text;
85 try {
86 text = AttackTestSupport.captureCharacters(reader, entityPayload(UNLISTED));
87 } catch (final SAXException blocked) {
88 return;
89 }
90 assertFalse(text.contains(AttackTestSupport.LEAKED_MARKER), "external entity leaked after reset:\n" + text);
91 }
92
93 @Test
94 @Tag("sax")
95 void saxParserResetKeepsFloorOnReaderVendedBeforeReset() throws Exception {
96 final SAXParser parser = SecureSAXParserFactory.newInstance().newSAXParser();
97
98
99 final XMLReader reader = parser.getXMLReader();
100 AttackTestSupport.assumeDoesNotThrow(parser::reset);
101 final String text;
102 try {
103 text = AttackTestSupport.captureCharacters(reader, entityPayload(UNLISTED));
104 } catch (final SAXException blocked) {
105 return;
106 }
107 assertFalse(text.contains(AttackTestSupport.LEAKED_MARKER), "external entity leaked through a reader obtained before reset:\n" + text);
108 }
109
110 @Test
111 @Tag("trax")
112 void transformerResetKeepsUriResolverFloor() throws Exception {
113
114 final Transformer transformer = SecureTransformerFactory.newInstance()
115 .newTemplates(AttackTestSupport.resourceSource("with-document.xsl")).newTransformer();
116 AttackTestSupport.assumeDoesNotThrow(transformer::reset);
117 final StringWriter sink = new StringWriter();
118 try {
119 transformer.transform(AttackTestSupport.streamSource("<root/>"), new StreamResult(sink));
120 } catch (final TransformerException blocked) {
121 return;
122 }
123 assertFalse(sink.toString().contains(AttackTestSupport.LEAKED_MARKER), "document() leaked after reset:\n" + sink);
124 }
125
126 @Test
127 @Tag("schema")
128 void validatorResetKeepsResourceResolverFloor() throws Exception {
129
130
131 final Validator validator = SecureSchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema().newValidator();
132 AttackTestSupport.assumeDoesNotThrow(validator::reset);
133 validator.setErrorHandler(AttackTestSupport.STRICT_REPORTER);
134
135
136 AttackTestSupport.assertParseFails(() -> validator.validate(AttackTestSupport.resourceSource("schema-location-instance.xml")),
137 "Validator after reset", SAXException.class, SecurityException.class);
138 }
139 }