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 import static org.junit.jupiter.api.Assertions.assertNotNull;
22 import static org.junit.jupiter.api.Assertions.assertSame;
23 import static org.junit.jupiter.api.Assertions.assertTrue;
24
25 import java.io.StringWriter;
26 import java.util.Properties;
27
28 import javax.xml.transform.Templates;
29 import javax.xml.transform.Transformer;
30 import javax.xml.transform.TransformerConfigurationException;
31 import javax.xml.transform.TransformerFactory;
32 import javax.xml.transform.URIResolver;
33 import javax.xml.transform.sax.SAXTransformerFactory;
34 import javax.xml.transform.sax.TransformerHandler;
35 import javax.xml.transform.stream.StreamResult;
36
37 import org.junit.jupiter.api.Assumptions;
38 import org.junit.jupiter.api.Tag;
39 import org.junit.jupiter.api.Test;
40
41
42
43
44
45
46
47 @Tag("trax")
48 class TransformerHandlerTest {
49
50
51
52
53 private static TransformerHandler assumeAcceptsForeignImplementation(final Templates callers) {
54 try {
55 return ((SAXTransformerFactory) TransformerFactory.newInstance()).newTransformerHandler(callers);
56 } catch (final TransformerConfigurationException e) {
57 Assumptions.abort("the implementation does not accept a foreign Templates: " + e.getMessage());
58 return null;
59 }
60 }
61
62
63
64
65 private static Templates callersTemplates(final Templates compiled, final URIResolver carried) {
66 return new Templates() {
67
68 @Override
69 public Properties getOutputProperties() {
70 return compiled.getOutputProperties();
71 }
72
73 @Override
74 public Transformer newTransformer() throws TransformerConfigurationException {
75 final Transformer transformer = compiled.newTransformer();
76 transformer.setURIResolver(carried);
77 return transformer;
78 }
79 };
80 }
81
82 private static String transformViaHandler(final TransformerHandler handler) throws Exception {
83 final StringWriter sink = new StringWriter();
84 handler.setResult(new StreamResult(sink));
85 SaxSurfaceTestSupport.feed(handler, SaxSurfaceTestSupport.rootInput());
86 return sink.toString();
87 }
88
89 @Test
90 void secureGetTransformerDoesNotLeakDocument() throws Exception {
91
92 final SAXTransformerFactory factory = SaxSurfaceTestSupport.secureFactory();
93 final TransformerHandler handler = factory.newTransformerHandler(AttackTestSupport.resourceSource("with-document.xsl"));
94 final StringWriter sink = new StringWriter();
95 handler.getTransformer().transform(AttackTestSupport.streamSource("<root/>"), new StreamResult(sink));
96 assertFalse(sink.toString().contains(AttackTestSupport.LEAKED_MARKER), "document() through getTransformer() leaked");
97 }
98
99 @Test
100 void secureHandlerKeepsAResolverTheCallersTemplatesSet() throws Exception {
101 final Templates compiled = TransformerFactory.newInstance().newTemplates(AttackTestSupport.resourceSource("with-document.xsl"));
102 final URIResolver carried = (href, base) -> null;
103 final Templates callers = callersTemplates(compiled, carried);
104
105 final TransformerHandler nativeTransformerHandler = assumeAcceptsForeignImplementation(callers);
106 Assumptions.assumeTrue(nativeTransformerHandler.getTransformer().getURIResolver() == carried,
107 "the implementation does not keep a resolver set by the caller's Templates");
108
109 final TransformerHandler handler = SaxSurfaceTestSupport.secureFactory().newTransformerHandler(callers);
110 assertSame(carried, handler.getTransformer().getURIResolver(), "the securing must not lose a resolver the implementation kept");
111 assertFalse(transformViaHandler(handler).contains(AttackTestSupport.LEAKED_MARKER), "what that resolver declined must not be fetched");
112 }
113
114 @Test
115 void secureTransformerHandlerDoesNotLeakDocument() throws Exception {
116 final SAXTransformerFactory factory = SaxSurfaceTestSupport.secureFactory();
117 final TransformerHandler handler = factory.newTransformerHandler(AttackTestSupport.resourceSource("with-document.xsl"));
118 assertFalse(transformViaHandler(handler).contains(AttackTestSupport.LEAKED_MARKER), "document() through TransformerHandler leaked");
119 }
120
121 @Test
122 void secureTransformerHandlerFromTemplatesDoesNotLeakDocument() throws Exception {
123 final SAXTransformerFactory factory = SaxSurfaceTestSupport.secureFactory();
124 final Templates templates = factory.newTemplates(AttackTestSupport.resourceSource("with-document.xsl"));
125 assertNotNull(templates, "stylesheet failed to compile");
126 final TransformerHandler handler = factory.newTransformerHandler(templates);
127 assertFalse(transformViaHandler(handler).contains(AttackTestSupport.LEAKED_MARKER),
128 "document() through TransformerHandler(Templates) leaked");
129 }
130
131 @Test
132 void unconfiguredTransformerHandlerLeaksDocument() throws Exception {
133 final SAXTransformerFactory factory = (SAXTransformerFactory) TransformerFactory.newInstance();
134 final TransformerHandler handler = factory.newTransformerHandler(AttackTestSupport.resourceSource("with-document.xsl"));
135 assertTrue(transformViaHandler(handler).contains(AttackTestSupport.LEAKED_MARKER),
136 "unconfigured TransformerHandler should resolve document()");
137 }
138 }