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.assertEquals;
21 import static org.junit.jupiter.api.Assertions.assertFalse;
22 import static org.junit.jupiter.api.Assertions.assertInstanceOf;
23 import static org.junit.jupiter.api.Assertions.assertNotNull;
24 import static org.junit.jupiter.api.Assertions.assertNotSame;
25 import static org.junit.jupiter.api.Assertions.assertThrows;
26 import static org.junit.jupiter.api.Assertions.assertTrue;
27
28 import java.io.StringReader;
29
30 import javax.xml.XMLConstants;
31 import javax.xml.parsers.DocumentBuilderFactory;
32 import javax.xml.parsers.FactoryConfigurationError;
33 import javax.xml.parsers.SAXParserFactory;
34 import javax.xml.stream.XMLInputFactory;
35 import javax.xml.transform.TransformerFactory;
36 import javax.xml.transform.TransformerFactoryConfigurationError;
37 import javax.xml.validation.SchemaFactory;
38 import javax.xml.xpath.XPathFactory;
39
40 import org.junit.jupiter.api.Assumptions;
41 import org.junit.jupiter.api.Tag;
42 import org.junit.jupiter.api.Test;
43 import org.w3c.dom.Document;
44 import org.xml.sax.InputSource;
45 import org.xml.sax.helpers.DefaultHandler;
46
47
48
49
50
51
52
53
54 class SecureFactoriesSmokeTest {
55
56 private static final String BENIGN_XML = "<?xml version=\"1.0\"?>\n<root><child>hello</child></root>\n";
57
58 @Test
59 @Tag("dom")
60 void benignDocumentParses() throws Exception {
61 final Document doc = SecureDocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(new StringReader(BENIGN_XML)));
62 assertNotNull(doc);
63 assertNotNull(doc.getDocumentElement());
64 }
65
66
67
68 @Test
69 @Tag("dom")
70 void explicitClassNameDocumentBuilderFactoryIsSecure() throws Exception {
71 Assumptions.assumeTrue(AttackTestSupport.DOM_SUPPORTS_SECURE_PROCESSING, "platform DOM does not support FEATURE_SECURE_PROCESSING");
72 final Class<?> impl = DocumentBuilderFactory.newInstance().getClass();
73 final DocumentBuilderFactory factory = SecureDocumentBuilderFactory.newInstance(impl.getName(), impl.getClassLoader());
74 assertTrue(factory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING));
75 }
76
77 @Test
78 @Tag("dom")
79 void explicitClassNameNSDocumentBuilderFactoryIsNamespaceAware() throws Exception {
80 final Class<?> impl = DocumentBuilderFactory.newInstance().getClass();
81 final DocumentBuilderFactory factory = SecureDocumentBuilderFactory.newNSInstance(impl.getName(), impl.getClassLoader());
82 assertTrue(factory.isNamespaceAware());
83 if (AttackTestSupport.DOM_SUPPORTS_SECURE_PROCESSING) {
84 assertTrue(factory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING));
85 }
86 }
87
88 @Test
89 @Tag("sax")
90 void explicitClassNameNSSAXParserFactoryIsNamespaceAware() throws Exception {
91 final Class<?> impl = SAXParserFactory.newInstance().getClass();
92 final SAXParserFactory factory = SecureSAXParserFactory.newNSInstance(impl.getName(), impl.getClassLoader());
93 assertTrue(factory.isNamespaceAware());
94 if (AttackTestSupport.SAX_SUPPORTS_SECURE_PROCESSING) {
95 assertTrue(factory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING));
96 }
97 }
98
99 @Test
100 @Tag("sax")
101 void explicitClassNameSAXParserFactoryIsSecure() throws Exception {
102 Assumptions.assumeTrue(AttackTestSupport.SAX_SUPPORTS_SECURE_PROCESSING, "platform SAX does not support FEATURE_SECURE_PROCESSING");
103 final Class<?> impl = SAXParserFactory.newInstance().getClass();
104 final SAXParserFactory factory = SecureSAXParserFactory.newInstance(impl.getName(), impl.getClassLoader());
105 assertTrue(factory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING));
106 }
107
108 @Test
109 @Tag("schema")
110 void explicitClassNameSchemaFactoryIsSecure() throws Exception {
111 final Class<?> impl = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI).getClass();
112 final SchemaFactory factory = SecureSchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI, impl.getName(), impl.getClassLoader());
113
114 assertInstanceOf(SecureSchema.class, factory.newSchema());
115 }
116
117 @Test
118 @Tag("trax")
119 void explicitClassNameTransformerFactoryIsSecure() {
120 final Class<?> impl = TransformerFactory.newInstance().getClass();
121 final TransformerFactory factory = SecureTransformerFactory.newInstance(impl.getName(), impl.getClassLoader());
122 assertTrue(factory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING));
123 }
124
125 @Test
126 @Tag("xpath")
127 void explicitClassNameXPathFactoryIsSecure() throws Exception {
128 final Class<?> impl = XPathFactory.newInstance().getClass();
129 final XPathFactory factory = SecureXPathFactory.newInstance(XPathFactory.DEFAULT_OBJECT_MODEL_URI, impl.getName(), impl.getClassLoader());
130 assertTrue(factory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING));
131 }
132
133 @Test
134 @Tag("stax")
135 void factoryIdXMLInputFactoryIsSecure() {
136 final String factoryId = "org.apache.commons.xml.secure.test.staxFactory";
137
138 System.setProperty(factoryId, XMLInputFactory.newInstance().getClass().getName());
139 try {
140 final XMLInputFactory factory = SecureXMLInputFactory.newFactory(factoryId, getClass().getClassLoader());
141 assertEquals(Boolean.TRUE, factory.getProperty(XMLInputFactory.SUPPORT_DTD));
142 } finally {
143 System.clearProperty(factoryId);
144 }
145 }
146
147 @Test
148 @Tag("stax")
149 void newDefaultFactoryXMLInputFactoryIsSecure() {
150 final XMLInputFactory factory = SecureXMLInputFactory.newDefaultFactory();
151 assertEquals(Boolean.TRUE, factory.getProperty(XMLInputFactory.SUPPORT_DTD));
152 }
153
154
155
156
157 @Test
158 @Tag("dom")
159 void newDefaultInstanceDocumentBuilderFactoryIsUsable() throws Exception {
160 final DocumentBuilderFactory factory = SecureDocumentBuilderFactory.newDefaultInstance();
161 assertNotNull(factory.newDocumentBuilder().parse(new InputSource(new StringReader(BENIGN_XML))).getDocumentElement());
162 if (AttackTestSupport.DOM_SUPPORTS_SECURE_PROCESSING) {
163 assertTrue(factory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING));
164 }
165 }
166
167 @Test
168 @Tag("sax")
169 void newDefaultInstanceSAXParserFactoryIsUsable() throws Exception {
170 final SAXParserFactory factory = SecureSAXParserFactory.newDefaultInstance();
171 factory.newSAXParser().parse(new InputSource(new StringReader(BENIGN_XML)), new DefaultHandler());
172 if (AttackTestSupport.SAX_SUPPORTS_SECURE_PROCESSING) {
173 assertTrue(factory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING));
174 }
175 }
176
177 @Test
178 @Tag("schema")
179 void newDefaultInstanceSchemaFactoryIsSecure() throws Exception {
180 final SchemaFactory factory = SecureSchemaFactory.newDefaultInstance();
181
182 assertInstanceOf(SecureSchema.class, factory.newSchema());
183 }
184
185 @Test
186 @Tag("trax")
187 void newDefaultInstanceTransformerFactoryIsSecure() {
188
189 if (AttackTestSupport.IS_ANDROID) {
190 assertThrows(TransformerFactoryConfigurationError.class, SecureTransformerFactory::newDefaultInstance);
191 return;
192 }
193 final TransformerFactory factory = SecureTransformerFactory.newDefaultInstance();
194 assertTrue(factory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING));
195 }
196
197 @Test
198 @Tag("xpath")
199 void newDefaultInstanceXPathFactoryIsSecure() throws Exception {
200 final XPathFactory factory = SecureXPathFactory.newDefaultInstance();
201 assertTrue(factory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING));
202 }
203
204 @Test
205 @Tag("dom")
206 void newDefaultNSInstanceDocumentBuilderFactoryIsNamespaceAware() throws Exception {
207 final DocumentBuilderFactory factory = SecureDocumentBuilderFactory.newDefaultNSInstance();
208 assertTrue(factory.isNamespaceAware());
209 if (AttackTestSupport.DOM_SUPPORTS_SECURE_PROCESSING) {
210 assertTrue(factory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING));
211 }
212 }
213
214 @Test
215 @Tag("sax")
216 void newDefaultNSInstanceSAXParserFactoryIsNamespaceAware() throws Exception {
217 final SAXParserFactory factory = SecureSAXParserFactory.newDefaultNSInstance();
218 assertTrue(factory.isNamespaceAware());
219 if (AttackTestSupport.SAX_SUPPORTS_SECURE_PROCESSING) {
220 assertTrue(factory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING));
221 }
222 }
223
224 @Test
225 @Tag("dom")
226 void newDocumentBuilderFactoryDisablesXIncludeAndValidation() {
227 final DocumentBuilderFactory factory = SecureDocumentBuilderFactory.newInstance();
228 if (AttackTestSupport.DOM_SUPPORTS_XINCLUDE) {
229 assertFalse(factory.isXIncludeAware(), "XInclude must be off by default");
230 }
231 assertFalse(factory.isValidating(), "Validation must be off by default");
232 }
233
234 @Test
235 @Tag("dom")
236 void newDocumentBuilderFactoryEnablesSecureProcessing() throws Exception {
237 Assumptions.assumeTrue(AttackTestSupport.DOM_SUPPORTS_SECURE_PROCESSING, "platform DOM does not support FEATURE_SECURE_PROCESSING");
238 final DocumentBuilderFactory factory = SecureDocumentBuilderFactory.newInstance();
239 assertTrue(factory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING), "FEATURE_SECURE_PROCESSING must be on");
240 }
241
242 @Test
243 @Tag("dom")
244 void newDocumentBuilderFactoryReturnsFreshInstance() {
245 final DocumentBuilderFactory a = SecureDocumentBuilderFactory.newInstance();
246 final DocumentBuilderFactory b = SecureDocumentBuilderFactory.newInstance();
247 assertNotNull(a);
248 assertNotNull(b);
249 assertNotSame(a, b);
250 }
251
252 @Test
253 @Tag("stax")
254 void newFactoryReturnsFreshInstance() {
255 final XMLInputFactory a = SecureXMLInputFactory.newFactory();
256 final XMLInputFactory b = SecureXMLInputFactory.newFactory();
257 assertNotSame(a, b);
258 assertEquals(Boolean.TRUE, a.getProperty(XMLInputFactory.SUPPORT_DTD));
259 }
260
261
262
263 @Test
264 @Tag("dom")
265 void newNSInstanceDocumentBuilderFactoryIsNamespaceAware() throws Exception {
266 final DocumentBuilderFactory factory = SecureDocumentBuilderFactory.newNSInstance();
267 assertTrue(factory.isNamespaceAware());
268 assertNotNull(factory.newDocumentBuilder().parse(new InputSource(new StringReader(BENIGN_XML))).getDocumentElement());
269 if (AttackTestSupport.DOM_SUPPORTS_SECURE_PROCESSING) {
270 assertTrue(factory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING));
271 }
272 }
273
274 @Test
275 @Tag("sax")
276 void newNSInstanceSAXParserFactoryIsNamespaceAware() throws Exception {
277 final SAXParserFactory factory = SecureSAXParserFactory.newNSInstance();
278 assertTrue(factory.isNamespaceAware());
279 factory.newSAXParser().parse(new InputSource(new StringReader(BENIGN_XML)), new DefaultHandler());
280 if (AttackTestSupport.SAX_SUPPORTS_SECURE_PROCESSING) {
281 assertTrue(factory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING));
282 }
283 }
284
285 @Test
286 @Tag("sax")
287 void newSAXParserFactoryReturnsFreshInstance() {
288 final SAXParserFactory a = SecureSAXParserFactory.newInstance();
289 final SAXParserFactory b = SecureSAXParserFactory.newInstance();
290 assertNotSame(a, b);
291 assertFalse(a.isValidating());
292 if (AttackTestSupport.SAX_SUPPORTS_XINCLUDE) {
293 assertFalse(a.isXIncludeAware());
294 }
295 }
296
297 @Test
298 @Tag("schema")
299 void newSchemaFactoryReturnsFreshInstance() throws Exception {
300 final SchemaFactory a = SecureSchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
301 final SchemaFactory b = SecureSchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
302 assertNotSame(a, b);
303
304 assertInstanceOf(SecureSchema.class, a.newSchema());
305 }
306
307 @Test
308 @Tag("trax")
309 void newTransformerFactoryReturnsFreshInstance() {
310 final TransformerFactory a = SecureTransformerFactory.newInstance();
311 final TransformerFactory b = SecureTransformerFactory.newInstance();
312 assertNotSame(a, b);
313 }
314
315 @Test
316 @Tag("stax")
317 void newXMLInputFactoryReturnsFreshInstance() {
318 final XMLInputFactory a = SecureXMLInputFactory.newInstance();
319 final XMLInputFactory b = SecureXMLInputFactory.newInstance();
320 assertNotSame(a, b);
321 assertEquals(Boolean.TRUE, a.getProperty(XMLInputFactory.SUPPORT_DTD));
322 assertEquals(Boolean.FALSE, a.getProperty(XMLInputFactory.IS_VALIDATING));
323 }
324
325 @Test
326 @Tag("xpath")
327 void newXPathFactoryReturnsFreshInstance() throws Exception {
328 final XPathFactory a = SecureXPathFactory.newInstance();
329 final XPathFactory b = SecureXPathFactory.newInstance();
330 assertNotSame(a, b);
331 assertTrue(a.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING));
332 }
333
334
335
336
337
338 @Test
339 @Tag("dom")
340 @Tag("sax")
341 @Tag("stax")
342 @Tag("trax")
343 @Tag("xpath")
344 @Tag("schema")
345 void publicClassesDoNotExtendTheirJaxpFactoryType() {
346 assertFalse(DocumentBuilderFactory.class.isAssignableFrom(SecureDocumentBuilderFactory.class));
347 assertFalse(SAXParserFactory.class.isAssignableFrom(SecureSAXParserFactory.class));
348 assertFalse(SchemaFactory.class.isAssignableFrom(SecureSchemaFactory.class));
349 assertFalse(TransformerFactory.class.isAssignableFrom(SecureTransformerFactory.class));
350 assertFalse(XMLInputFactory.class.isAssignableFrom(SecureXMLInputFactory.class));
351 assertFalse(XPathFactory.class.isAssignableFrom(SecureXPathFactory.class));
352 }
353
354 @Test
355 @Tag("dom")
356 void unknownFactoryClassNameThrows() {
357 assertThrows(FactoryConfigurationError.class, () -> SecureDocumentBuilderFactory.newInstance("no.such.FactoryClass", null));
358 }
359 }