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.StringReader;
26 import java.io.StringWriter;
27 import java.util.Properties;
28
29 import javax.xml.parsers.DocumentBuilderFactory;
30 import javax.xml.transform.ErrorListener;
31 import javax.xml.transform.OutputKeys;
32 import javax.xml.transform.Transformer;
33 import javax.xml.transform.TransformerException;
34 import javax.xml.transform.TransformerFactory;
35 import javax.xml.transform.URIResolver;
36 import javax.xml.transform.dom.DOMSource;
37 import javax.xml.transform.stream.StreamResult;
38 import javax.xml.transform.stream.StreamSource;
39
40 import org.junit.jupiter.api.Tag;
41 import org.junit.jupiter.api.Test;
42
43 @Tag("trax")
44 class SecureTransformerTest {
45
46
47
48
49 private static SecureTransformer wrap(final URIResolver carried) throws Exception {
50 final Transformer delegate = TransformerFactory.newInstance().newTransformer(AttackTestSupport.resourceSource("with-document.xsl"));
51 delegate.setURIResolver(carried);
52 return new SecureTransformer(delegate, null, null, false);
53 }
54
55 @Test
56 void adoptsAResolverTheDelegateAlreadyCarries() throws Exception {
57 final URIResolver carried = (href, base) -> new StreamSource(new StringReader("<opted-in/>"));
58 final SecureTransformer transformer = wrap(carried);
59 assertSame(carried, transformer.getURIResolver(), "the resolver the delegate carried must survive the wrapping");
60 final StringWriter output = new StringWriter();
61 transformer.transform(AttackTestSupport.streamSource("<root/>"), new StreamResult(output));
62 assertTrue(output.toString().contains("opted-in"), "the carried resolver must answer document()");
63 assertFalse(output.toString().contains(AttackTestSupport.LEAKED_MARKER), "the real resource must not be fetched");
64 }
65
66 @Test
67 void carriesTheAdoptedResolverThroughReset() throws Exception {
68 final URIResolver carried = (href, base) -> new StreamSource(new StringReader("<opted-in/>"));
69 final SecureTransformer transformer = wrap(carried);
70
71 transformer.reset();
72 assertSame(carried, transformer.getURIResolver(), "reset must restore the resolver the delegate carried");
73 final StringWriter output = new StringWriter();
74 transformer.transform(AttackTestSupport.streamSource("<root/>"), new StreamResult(output));
75 assertTrue(output.toString().contains("opted-in"), "the carried resolver must still answer document() after a reset");
76 assertFalse(output.toString().contains(AttackTestSupport.LEAKED_MARKER), "the real resource must not be fetched");
77 }
78
79 @Test
80 void forwardsEveryTransformerMethod() throws Exception {
81 final TransformerFactory factory = TransformerFactory.newInstance();
82 final SecureTransformer transformer = new SecureTransformer(factory
83 .newTemplates(new StreamSource(new StringReader(
84 "<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'><xsl:template match='/'/></xsl:stylesheet>")))
85 .newTransformer(), null, null, false);
86 transformer.clearParameters();
87 transformer.setParameter("p", "v");
88 assertNotNull(transformer.getParameter("p"));
89 transformer.setOutputProperty(OutputKeys.METHOD, "xml");
90 assertNotNull(transformer.getOutputProperty(OutputKeys.METHOD));
91 transformer.setOutputProperties(new Properties());
92 assertNotNull(transformer.getOutputProperties());
93 transformer.setErrorListener(new ErrorListener() {
94
95 @Override
96 public void error(final TransformerException e) {
97 }
98
99 @Override
100 public void fatalError(final TransformerException e) {
101 }
102
103 @Override
104 public void warning(final TransformerException e) {
105 }
106 });
107 assertNotNull(transformer.getErrorListener());
108 transformer.setURIResolver((href, base) -> null);
109 assertNotNull(transformer.getURIResolver());
110 transformer.transform(new DOMSource(DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument()), new StreamResult(new StringWriter()));
111 transformer.reset();
112 }
113
114 @Test
115 void keepsTheFloorUnderACarriedResolverThatDeclines() throws Exception {
116
117 final SecureTransformer transformer = wrap((href, base) -> null);
118 final StringWriter output = new StringWriter();
119 transformer.transform(AttackTestSupport.streamSource("<root/>"), new StreamResult(output));
120 assertFalse(output.toString().contains(AttackTestSupport.LEAKED_MARKER), "document() the resolver declined must not be fetched");
121 }
122 }