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.assertTrue;
22 import static org.junit.jupiter.api.Assertions.fail;
23
24 import javax.xml.parsers.DocumentBuilderFactory;
25 import javax.xml.parsers.ParserConfigurationException;
26 import javax.xml.xpath.XPathExpressionException;
27 import javax.xml.xpath.XPathFactory;
28
29 import org.junit.jupiter.api.Tag;
30 import org.junit.jupiter.api.Test;
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61 @Tag("xpath3")
62 class SaxonXPathExternalCallsTest {
63
64 private static final String MARKER = "All your base are belong to us";
65 private static final String SAXON_XPATH_FACTORY_CLASS = "net.sf.saxon.xpath.XPathFactoryImpl";
66
67 private static void assertCallExcludesMarker(final XPathFactory factory, final String expression) {
68 final String result;
69 try {
70 result = evaluateAsString(factory, expression);
71 } catch (final Exception e) {
72 return;
73 }
74 assertFalse(result.contains(MARKER),
75 "Securing did not block the external reference; result contained marker '" + MARKER + "'.\nFull result:\n" + result);
76 }
77
78 private static void assertCallLeaksMarker(final XPathFactory factory, final String expression) {
79 final String result;
80 try {
81 result = evaluateAsString(factory, expression);
82 } catch (final Exception e) {
83 fail("Unconfigured Saxon XPath should resolve the external resource, but threw: " + e);
84 return;
85 }
86 assertTrue(result.contains(MARKER),
87 "Expected marker '" + MARKER + "' in result, got: " + result);
88 }
89
90 private static String docExpression() {
91 return "doc('" + AttackTestSupport.resourceUrl("referenced.xml") + "')/leaked";
92 }
93
94 private static String evaluateAsString(final XPathFactory factory, final String expression)
95 throws ParserConfigurationException, XPathExpressionException {
96 return factory.newXPath().evaluate(expression,
97 DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument());
98 }
99
100 private static String jsonDocExpression() {
101 return "json-doc('" + AttackTestSupport.resourceUrl("referenced.json") + "')?leaked";
102 }
103
104
105
106
107
108
109
110
111
112 private static XPathFactory saxonXPathFactory() {
113 try {
114 return (XPathFactory) Class.forName(SAXON_XPATH_FACTORY_CLASS).getDeclaredConstructor().newInstance();
115 } catch (final ReflectiveOperationException e) {
116 throw new AssertionError("Cannot instantiate " + SAXON_XPATH_FACTORY_CLASS, e);
117 }
118 }
119
120 private static XPathFactory secureSaxonXPathFactory() {
121 return SaxonProvider.configure(saxonXPathFactory());
122 }
123
124 private static String unparsedTextExpression() {
125 return "unparsed-text('" + AttackTestSupport.resourceUrl("referenced.txt") + "')";
126 }
127
128 @Test
129 void secureXPathBlocksDoc() {
130 assertCallExcludesMarker(secureSaxonXPathFactory(), docExpression());
131 }
132
133 @Test
134 void secureXPathBlocksJsonDoc() {
135 assertCallExcludesMarker(secureSaxonXPathFactory(), jsonDocExpression());
136 }
137
138 @Test
139 void secureXPathBlocksUnparsedText() {
140 assertCallExcludesMarker(secureSaxonXPathFactory(), unparsedTextExpression());
141 }
142
143 @Test
144 void unconfiguredXPathLeaksDoc() {
145 assertCallLeaksMarker(saxonXPathFactory(), docExpression());
146 }
147
148 @Test
149 void unconfiguredXPathLeaksJsonDoc() {
150 assertCallLeaksMarker(saxonXPathFactory(), jsonDocExpression());
151 }
152
153 @Test
154 void unconfiguredXPathLeaksUnparsedText() {
155 assertCallLeaksMarker(saxonXPathFactory(), unparsedTextExpression());
156 }
157 }