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.apache.commons.xml.secure.AttackTestSupport.assertParseFails;
21 import static org.apache.commons.xml.secure.AttackTestSupport.assertParseSucceeds;
22 import static org.apache.commons.xml.secure.AttackTestSupport.inputSource;
23 import static org.apache.commons.xml.secure.AttackTestSupport.resourceUrl;
24 import static org.apache.commons.xml.secure.AttackTestSupport.strictDocumentBuilder;
25 import static org.apache.commons.xml.secure.AttackTestSupport.strictXMLReader;
26
27 import javax.xml.XMLConstants;
28 import javax.xml.parsers.DocumentBuilder;
29 import javax.xml.parsers.DocumentBuilderFactory;
30 import javax.xml.parsers.SAXParser;
31 import javax.xml.parsers.SAXParserFactory;
32
33 import org.junit.jupiter.api.Assumptions;
34 import org.junit.jupiter.api.Tag;
35 import org.junit.jupiter.api.Test;
36 import org.junit.jupiter.api.function.ThrowingSupplier;
37 import org.xml.sax.SAXException;
38 import org.xml.sax.XMLReader;
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59 @Tag("schema")
60 class SchemaLocationPropertyTest {
61
62 private static final String SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
63 private static final String SCHEMA_FEATURE = "http://apache.org/xml/features/validation/schema";
64 private static final String EXTERNAL_NO_NS = "http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation";
65 private static final String EXTERNAL_SCHEMA_LOCATION = "http://apache.org/xml/properties/schema/external-schemaLocation";
66
67
68
69
70 private static final String NO_NS_INSTANCE = "<root>x</root>";
71
72 private static final String LEAKED_NS = "http://example.org/leaked";
73
74
75
76
77 private static final String NAMESPACED_INSTANCE = "<l:leaked xmlns:l=\"" + LEAKED_NS + "\">x</l:leaked>";
78
79
80
81
82
83 private static <T> T configureOrSkip(final ThrowingSupplier<T> setup) {
84 try {
85 return setup.get();
86 } catch (final Throwable t) {
87 return Assumptions.abort("Parser does not support schema validation through these features/properties: " + t);
88 }
89 }
90
91 private static String namespacedLocation() {
92 return LEAKED_NS + " " + resourceUrl("included.xsd");
93 }
94
95 private static String noNamespaceLocation() {
96 return resourceUrl("no-namespace.xsd").toString();
97 }
98
99 private static DocumentBuilder permissiveValidatingDom(final String property, final String value) {
100 return configureOrSkip(() -> {
101 final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
102 factory.setNamespaceAware(true);
103 factory.setValidating(true);
104 factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, false);
105 factory.setAttribute(SCHEMA_LANGUAGE, XMLConstants.W3C_XML_SCHEMA_NS_URI);
106 factory.setAttribute(property, value);
107 return strictDocumentBuilder(factory);
108 });
109 }
110
111 private static XMLReader permissiveValidatingSax(final String property, final String value) {
112 return configureOrSkip(() -> {
113 final SAXParserFactory factory = SAXParserFactory.newInstance();
114 factory.setNamespaceAware(true);
115 factory.setValidating(true);
116 factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, false);
117 factory.setFeature(SCHEMA_FEATURE, true);
118 final SAXParser parser = factory.newSAXParser();
119 parser.setProperty(SCHEMA_LANGUAGE, XMLConstants.W3C_XML_SCHEMA_NS_URI);
120 parser.setProperty(property, value);
121 return strictXMLReader(parser.getXMLReader());
122 });
123 }
124
125 private static DocumentBuilder secureValidatingDom(final String property, final String value) {
126 return configureOrSkip(() -> {
127 final DocumentBuilderFactory factory = SecureDocumentBuilderFactory.newInstance();
128 factory.setNamespaceAware(true);
129 factory.setValidating(true);
130 factory.setAttribute(SCHEMA_LANGUAGE, XMLConstants.W3C_XML_SCHEMA_NS_URI);
131 factory.setAttribute(property, value);
132 return strictDocumentBuilder(factory);
133 });
134 }
135
136 private static XMLReader secureValidatingSax(final String property, final String value) {
137 return configureOrSkip(() -> {
138 final SAXParserFactory factory = SecureSAXParserFactory.newInstance();
139 factory.setNamespaceAware(true);
140 factory.setValidating(true);
141 final SAXParser parser = factory.newSAXParser();
142 parser.setProperty(SCHEMA_LANGUAGE, XMLConstants.W3C_XML_SCHEMA_NS_URI);
143 parser.setProperty(property, value);
144 return strictXMLReader(parser.getXMLReader());
145 });
146 }
147
148 @Test
149 void permissiveDomFetchesNoNamespaceSchemaLocation() {
150 final DocumentBuilder builder = permissiveValidatingDom(EXTERNAL_NO_NS, noNamespaceLocation());
151 assertParseSucceeds(() -> builder.parse(inputSource(NO_NS_INSTANCE)), "DOM external-noNamespaceSchemaLocation (permissive)");
152 }
153
154 @Test
155 void permissiveDomFetchesSchemaLocation() {
156 final DocumentBuilder builder = permissiveValidatingDom(EXTERNAL_SCHEMA_LOCATION, namespacedLocation());
157 assertParseSucceeds(() -> builder.parse(inputSource(NAMESPACED_INSTANCE)), "DOM external-schemaLocation (permissive)");
158 }
159
160 @Test
161 void permissiveSaxFetchesNoNamespaceSchemaLocation() {
162 final XMLReader reader = permissiveValidatingSax(EXTERNAL_NO_NS, noNamespaceLocation());
163 assertParseSucceeds(() -> reader.parse(inputSource(NO_NS_INSTANCE)), "SAX external-noNamespaceSchemaLocation (permissive)");
164 }
165
166 @Test
167 void permissiveSaxFetchesSchemaLocation() {
168 final XMLReader reader = permissiveValidatingSax(EXTERNAL_SCHEMA_LOCATION, namespacedLocation());
169 assertParseSucceeds(() -> reader.parse(inputSource(NAMESPACED_INSTANCE)), "SAX external-schemaLocation (permissive)");
170 }
171
172 @Test
173 void secureDomRefusesNoNamespaceSchemaLocation() {
174 final DocumentBuilder builder = secureValidatingDom(EXTERNAL_NO_NS, noNamespaceLocation());
175 assertParseFails(() -> builder.parse(inputSource(NO_NS_INSTANCE)), "DOM external-noNamespaceSchemaLocation", SAXException.class);
176 }
177
178 @Test
179 void secureDomRefusesSchemaLocation() {
180 final DocumentBuilder builder = secureValidatingDom(EXTERNAL_SCHEMA_LOCATION, namespacedLocation());
181 assertParseFails(() -> builder.parse(inputSource(NAMESPACED_INSTANCE)), "DOM external-schemaLocation", SAXException.class);
182 }
183
184 @Test
185 void secureSaxRefusesNoNamespaceSchemaLocation() {
186 final XMLReader reader = secureValidatingSax(EXTERNAL_NO_NS, noNamespaceLocation());
187 assertParseFails(() -> reader.parse(inputSource(NO_NS_INSTANCE)), "SAX external-noNamespaceSchemaLocation", SAXException.class);
188 }
189
190 @Test
191 void secureSaxRefusesSchemaLocation() {
192 final XMLReader reader = secureValidatingSax(EXTERNAL_SCHEMA_LOCATION, namespacedLocation());
193 assertParseFails(() -> reader.parse(inputSource(NAMESPACED_INSTANCE)), "SAX external-schemaLocation", SAXException.class);
194 }
195 }