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.assertInstanceOf;
22 import static org.junit.jupiter.api.Assertions.assertNull;
23 import static org.junit.jupiter.api.Assertions.assertSame;
24 import static org.junit.jupiter.api.Assertions.assertThrows;
25 import static org.junit.jupiter.api.Assertions.assertTrue;
26
27 import java.io.StringReader;
28 import java.util.HashMap;
29 import java.util.Map;
30 import java.util.Properties;
31
32 import javax.xml.XMLConstants;
33 import javax.xml.parsers.DocumentBuilderFactory;
34 import javax.xml.transform.ErrorListener;
35 import javax.xml.transform.Source;
36 import javax.xml.transform.Templates;
37 import javax.xml.transform.Transformer;
38 import javax.xml.transform.TransformerConfigurationException;
39 import javax.xml.transform.TransformerFactory;
40 import javax.xml.transform.URIResolver;
41 import javax.xml.transform.dom.DOMSource;
42 import javax.xml.transform.sax.SAXSource;
43 import javax.xml.transform.sax.SAXTransformerFactory;
44 import javax.xml.transform.sax.TemplatesHandler;
45 import javax.xml.transform.sax.TransformerHandler;
46 import javax.xml.transform.stream.StreamSource;
47
48 import org.junit.jupiter.api.Tag;
49 import org.junit.jupiter.api.Test;
50 import org.xml.sax.InputSource;
51 import org.xml.sax.XMLFilter;
52
53
54
55
56 @Tag("trax")
57 class SecureTransformerFactoryTest {
58
59 private static class NullProductsFactory extends SAXTransformerFactory {
60
61 private final SAXTransformerFactory delegate = (SAXTransformerFactory) TransformerFactory.newInstance();
62
63 private final Map<String, Object> attributes = new HashMap<>();
64
65 @Override
66 public Source getAssociatedStylesheet(final Source source, final String media, final String title, final String charset)
67 throws TransformerConfigurationException {
68 return delegate.getAssociatedStylesheet(source, media, title, charset);
69 }
70
71 @Override
72 public Object getAttribute(final String name) {
73 return attributes.get(name);
74 }
75
76 @Override
77 public ErrorListener getErrorListener() {
78 return delegate.getErrorListener();
79 }
80
81 @Override
82 public boolean getFeature(final String name) {
83 return delegate.getFeature(name);
84 }
85
86 @Override
87 public URIResolver getURIResolver() {
88 return delegate.getURIResolver();
89 }
90
91 @Override
92 public Templates newTemplates(final Source source) {
93 return null;
94 }
95
96 @Override
97 public TemplatesHandler newTemplatesHandler() {
98 return null;
99 }
100
101 @Override
102 public Transformer newTransformer() {
103 return null;
104 }
105
106 @Override
107 public Transformer newTransformer(final Source source) {
108 return null;
109 }
110
111 @Override
112 public TransformerHandler newTransformerHandler() {
113 return null;
114 }
115
116 @Override
117 public TransformerHandler newTransformerHandler(final Source source) {
118 return null;
119 }
120
121 @Override
122 public TransformerHandler newTransformerHandler(final Templates templates) {
123 return null;
124 }
125
126 @Override
127 public XMLFilter newXMLFilter(final Source source) {
128 return null;
129 }
130
131 @Override
132 public XMLFilter newXMLFilter(final Templates templates) {
133 return null;
134 }
135
136 @Override
137 public void setAttribute(final String name, final Object value) {
138 attributes.put(name, value);
139 }
140
141 @Override
142 public void setErrorListener(final ErrorListener listener) {
143 delegate.setErrorListener(listener);
144 }
145
146 @Override
147 public void setFeature(final String name, final boolean value) throws TransformerConfigurationException {
148 delegate.setFeature(name, value);
149 }
150
151 @Override
152 public void setURIResolver(final URIResolver resolver) {
153 delegate.setURIResolver(resolver);
154 }
155 }
156
157 private static final class RejectingFeatureFactory extends NullProductsFactory {
158
159
160
161
162
163
164 @Override
165 public void setFeature(final String name, final boolean value) throws TransformerConfigurationException {
166 throw new TransformerConfigurationException(name);
167 }
168 }
169
170 private static void associatedStylesheet(final SAXTransformerFactory factory, final Source source) {
171 try {
172 factory.getAssociatedStylesheet(source, null, null, null);
173 } catch (final TransformerConfigurationException | NullPointerException expected) {
174
175 }
176 }
177
178 private static StreamSource stylesheet() {
179 return new StreamSource(new StringReader(
180 "<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>" + "<xsl:template match='/'/></xsl:stylesheet>"));
181 }
182
183 @Test
184 void preservesNullResultsFromEveryWrappableProduct() throws Exception {
185 final SAXTransformerFactory factory = (SAXTransformerFactory) SecureTransformerFactory.secure(new NullProductsFactory());
186 final Templates templates = TransformerFactory.newInstance().newTemplates(stylesheet());
187 assertNull(factory.newTemplates(stylesheet()));
188 assertNull(factory.newTemplatesHandler());
189 assertNull(factory.newTransformer());
190 assertNull(factory.newTransformer(stylesheet()));
191 assertNull(factory.newTransformerHandler());
192 assertNull(factory.newTransformerHandler(stylesheet()));
193 assertNull(factory.newTransformerHandler(templates));
194
195 assertThrows(TransformerConfigurationException.class, () -> factory.newXMLFilter(stylesheet()));
196 factory.setAttribute("test", "value");
197 assertEquals("value", factory.getAttribute("test"));
198 factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
199 }
200
201 @Test
202 void rejectsDelegatesThatCannotEnableSecureProcessing() {
203 assertThrows(SecureException.class, () -> SecureTransformerFactory.secure(new RejectingFeatureFactory()));
204 }
205
206 @Test
207 void rejectsForeignTemplatesFromNewTransformerHandler() throws Exception {
208 final SAXTransformerFactory factory = (SAXTransformerFactory) SecureTransformerFactory.newInstance();
209 final Templates own = factory.newTemplates(stylesheet());
210
211 final Templates foreign = new Templates() {
212
213 @Override
214 public Properties getOutputProperties() {
215 return own.getOutputProperties();
216 }
217
218 @Override
219 public Transformer newTransformer() throws TransformerConfigurationException {
220 return own.newTransformer();
221 }
222 };
223
224 final TransformerConfigurationException exception = assertThrows(TransformerConfigurationException.class,
225 () -> factory.newTransformerHandler(foreign));
226
227 if (exception.getCause() instanceof ClassCastException) {
228 assertTrue(exception.getMessage().contains(foreign.getClass().getName()), exception.getMessage());
229 }
230 }
231
232 @Test
233 void rejectsNullInputOnEveryFactoryMethod() {
234 final SAXTransformerFactory factory = (SAXTransformerFactory) SecureTransformerFactory.newInstance();
235 assertThrows(NullPointerException.class, () -> factory.newTemplates(null));
236 assertThrows(NullPointerException.class, () -> factory.newTransformer(null));
237 assertThrows(NullPointerException.class, () -> factory.newTransformerHandler((Source) null));
238 assertThrows(NullPointerException.class, () -> factory.newTransformerHandler((Templates) null));
239 assertThrows(NullPointerException.class, () -> factory.newXMLFilter((Source) null));
240 assertThrows(NullPointerException.class, () -> factory.newXMLFilter((Templates) null));
241 }
242
243 @Test
244 void rejectsTemplatesThatProduceNoTransformer() {
245 final Templates templates = new Templates() {
246
247 @Override
248 public Properties getOutputProperties() {
249 return new Properties();
250 }
251
252 @Override
253 public Transformer newTransformer() {
254
255 return null;
256 }
257 };
258 final SAXTransformerFactory factory = (SAXTransformerFactory) SecureTransformerFactory.newInstance();
259
260 assertThrows(TransformerConfigurationException.class, () -> factory.newXMLFilter(templates));
261 }
262
263 @Test
264 void securesAssociatedStylesheetSourcesOfEverySupportedShape() throws Exception {
265 final SAXTransformerFactory factory = (SAXTransformerFactory) SecureTransformerFactory.newInstance();
266 associatedStylesheet(factory, new StreamSource(new StringReader("<root/>")));
267 associatedStylesheet(factory, new StreamSource(new StringReader("<root>")));
268 associatedStylesheet(factory, new SAXSource(new InputSource(new StringReader("<root/>"))));
269 associatedStylesheet(factory, new SAXSource());
270 associatedStylesheet(factory,
271 new SAXSource(SecureSAXParserFactory.newXMLReader(false), new InputSource(new StringReader("<root/>"))));
272 associatedStylesheet(factory,
273 new DOMSource(DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument()));
274 }
275
276 @Test
277 void wrapsClassCastExceptionFromNewTransformerHandler() throws Exception {
278 final ClassCastException cause = new ClassCastException("Unsupported Templates implementation");
279 final SAXTransformerFactory factory = (SAXTransformerFactory) SecureTransformerFactory.secure(new NullProductsFactory() {
280
281
282
283
284
285
286 @Override
287 public TransformerHandler newTransformerHandler(final Templates templates) {
288 throw cause;
289 }
290 });
291 final Templates templates = TransformerFactory.newInstance().newTemplates(stylesheet());
292 final TransformerConfigurationException exception = assertThrows(TransformerConfigurationException.class,
293 () -> factory.newTransformerHandler(templates));
294 assertSame(cause, exception.getCause());
295 assertTrue(exception.getMessage().contains(templates.getClass().getName()), exception.getMessage());
296 }
297
298 @Test
299 void wrapsEveryStandardAndSaxFactoryProduct() throws Exception {
300 final SAXTransformerFactory factory = (SAXTransformerFactory) SecureTransformerFactory.newInstance();
301 final URIResolver resolver = (href, base) -> null;
302 factory.setURIResolver(resolver);
303 assertSame(resolver, factory.getURIResolver());
304 factory.setErrorListener(factory.getErrorListener());
305 try {
306 factory.setAttribute("indent-number", 2);
307
308 assertThrows(IllegalArgumentException.class, () -> factory.getAttribute("indent-number"));
309 } catch (final IllegalArgumentException e) {
310
311 }
312 final Templates templates = factory.newTemplates(stylesheet());
313 assertInstanceOf(SecureTemplates.class, templates);
314 assertInstanceOf(SecureTransformer.class, factory.newTransformer());
315 assertInstanceOf(SecureTransformer.class, factory.newTransformer(stylesheet()));
316 assertInstanceOf(SecureTemplatesHandler.class, factory.newTemplatesHandler());
317 assertInstanceOf(SecureTransformerHandler.class, factory.newTransformerHandler());
318 assertInstanceOf(SecureTransformerHandler.class, factory.newTransformerHandler(stylesheet()));
319 assertInstanceOf(SecureTransformerHandler.class, factory.newTransformerHandler(templates));
320 assertInstanceOf(SecureXMLFilter.class, factory.newXMLFilter(stylesheet()));
321 assertInstanceOf(SecureXMLFilter.class, factory.newXMLFilter(templates));
322 final Templates rawTemplates = TransformerFactory.newInstance().newTemplates(stylesheet());
323 assertInstanceOf(SecureXMLFilter.class, factory.newXMLFilter(rawTemplates));
324 associatedStylesheet(factory, new StreamSource(new StringReader("<root/>")));
325 assertInstanceOf(SecureTransformer.class, SecureTransformerFactory.newInstance().newTransformer());
326 }
327 }