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.assertNotNull;
23 import static org.junit.jupiter.api.Assertions.assertNull;
24 import static org.junit.jupiter.api.Assertions.assertSame;
25 import static org.junit.jupiter.api.Assertions.assertThrows;
26 import static org.junit.jupiter.api.Assertions.assertTrue;
27
28 import javax.xml.XMLConstants;
29 import javax.xml.validation.SchemaFactory;
30 import javax.xml.validation.TypeInfoProvider;
31 import javax.xml.validation.ValidatorHandler;
32
33 import org.junit.jupiter.api.Tag;
34 import org.junit.jupiter.api.Test;
35 import org.w3c.dom.ls.LSResourceResolver;
36 import org.xml.sax.Attributes;
37 import org.xml.sax.ContentHandler;
38 import org.xml.sax.ErrorHandler;
39 import org.xml.sax.Locator;
40 import org.xml.sax.SAXNotRecognizedException;
41 import org.xml.sax.SAXNotSupportedException;
42 import org.xml.sax.SAXParseException;
43 import org.xml.sax.helpers.AttributesImpl;
44 import org.xml.sax.helpers.DefaultHandler;
45
46 @Tag("schema")
47 class SecureValidatorHandlerTest {
48
49
50
51
52 private static final class RecordingValidatorHandler extends ValidatorHandler {
53
54 boolean startDocumentCalled;
55
56 boolean endDocumentCalled;
57
58 boolean startPrefixMappingCalled;
59
60 String startPrefixMappingPrefix;
61
62 String startPrefixMappingUri;
63
64 boolean endPrefixMappingCalled;
65
66 String endPrefixMappingPrefix;
67
68 boolean startElementCalled;
69
70 String startElementUri;
71
72 String startElementLocalName;
73
74 String startElementQName;
75
76 Attributes startElementAttrs;
77
78 boolean charactersCalled;
79
80 char[] charactersChars;
81
82 int charactersStart;
83
84 int charactersLength;
85
86 boolean ignorableWhitespaceCalled;
87
88 boolean processingInstructionCalled;
89
90 String piTarget;
91
92 String piData;
93
94 boolean endElementCalled;
95
96 String endElementUri;
97
98 String endElementLocalName;
99
100 String endElementQName;
101
102 boolean skippedEntityCalled;
103
104 String skippedEntityName;
105
106 boolean setDocumentLocatorCalled;
107
108 boolean setContentHandlerCalled;
109
110 boolean setErrorHandlerCalled;
111
112 boolean setFeatureCalled;
113
114 String setFeatureName;
115
116 boolean setFeatureValue;
117
118 boolean setPropertyCalled;
119
120 String setPropertyName;
121
122 Object setPropertyValue;
123
124 ContentHandler contentHandler;
125
126 ErrorHandler errorHandler;
127
128 LSResourceResolver resourceResolver;
129
130 @Override
131 public void characters(final char[] ch, final int start, final int length) {
132 charactersCalled = true;
133 charactersChars = ch.clone();
134 charactersStart = start;
135 charactersLength = length;
136 }
137
138 @Override
139 public void endDocument() {
140 endDocumentCalled = true;
141 }
142
143 @Override
144 public void endElement(final String uri, final String localName, final String qName) {
145 endElementCalled = true;
146 endElementUri = uri;
147 endElementLocalName = localName;
148 endElementQName = qName;
149 }
150
151 @Override
152 public void endPrefixMapping(final String prefix) {
153 endPrefixMappingCalled = true;
154 endPrefixMappingPrefix = prefix;
155 }
156
157 @Override
158 public ContentHandler getContentHandler() {
159 return contentHandler;
160 }
161
162 @Override
163 public ErrorHandler getErrorHandler() {
164 return errorHandler;
165 }
166
167 @Override
168 public boolean getFeature(final String name) {
169 return false;
170 }
171
172 @Override
173 public Object getProperty(final String name) {
174 return null;
175 }
176
177 @Override
178 public LSResourceResolver getResourceResolver() {
179 return resourceResolver;
180 }
181
182 @Override
183 public TypeInfoProvider getTypeInfoProvider() {
184 return null;
185 }
186
187 @Override
188 public void ignorableWhitespace(final char[] ch, final int start, final int length) {
189 ignorableWhitespaceCalled = true;
190 }
191
192 @Override
193 public void processingInstruction(final String target, final String data) {
194 processingInstructionCalled = true;
195 piTarget = target;
196 piData = data;
197 }
198
199 @Override
200 public void setContentHandler(final ContentHandler handler) {
201 setContentHandlerCalled = true;
202 contentHandler = handler;
203 }
204
205 @Override
206 public void setDocumentLocator(final Locator locator) {
207 setDocumentLocatorCalled = true;
208 }
209
210 @Override
211 public void setErrorHandler(final ErrorHandler handler) {
212 setErrorHandlerCalled = true;
213 errorHandler = handler;
214 }
215
216 @Override
217 public void setFeature(final String name, final boolean value) {
218 setFeatureCalled = true;
219 setFeatureName = name;
220 setFeatureValue = value;
221 }
222
223 @Override
224 public void setProperty(final String name, final Object value) {
225 setPropertyCalled = true;
226 setPropertyName = name;
227 setPropertyValue = value;
228 }
229
230 @Override
231 public void setResourceResolver(final LSResourceResolver resolver) {
232 resourceResolver = resolver;
233 }
234
235 @Override
236 public void skippedEntity(final String name) {
237 skippedEntityCalled = true;
238 skippedEntityName = name;
239 }
240
241 @Override
242 public void startDocument() {
243 startDocumentCalled = true;
244 }
245
246 @Override
247 public void startElement(final String uri, final String localName, final String qName, final Attributes atts) {
248 startElementCalled = true;
249 startElementUri = uri;
250 startElementLocalName = localName;
251 startElementQName = qName;
252 startElementAttrs = atts;
253 }
254
255 @Override
256 public void startPrefixMapping(final String prefix, final String uri) {
257 startPrefixMappingCalled = true;
258 startPrefixMappingPrefix = prefix;
259 startPrefixMappingUri = uri;
260 }
261 }
262
263 private static ValidatorHandler newValidatorHandler() throws Exception {
264 final SchemaFactory factory = SecureSchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
265 return factory.newSchema().newValidatorHandler();
266 }
267
268 @Test
269 void constructorInstallsFloorOnDelegate() throws Exception {
270 final ValidatorHandler delegate = newValidatorHandler();
271 final SecureValidatorHandler handler = new SecureValidatorHandler(delegate);
272
273 assertNotNull(delegate.getResourceResolver(), "delegate resource resolver must be set to the floor");
274 assertInstanceOf(FallbackIgnoreLSResourceResolver.class, delegate.getResourceResolver(),
275 "delegate resolver must be a FallbackIgnoreLSResourceResolver");
276
277 assertNull(handler.getResourceResolver());
278 }
279
280 @Test
281 void constructorRejectsNullDelegate() {
282 assertThrows(NullPointerException.class, () -> new SecureValidatorHandler(null));
283 }
284
285 @Test
286 void delegatesFeature() throws Exception {
287 final ValidatorHandler delegate = newValidatorHandler();
288 final SecureValidatorHandler handler = new SecureValidatorHandler(delegate);
289
290 final String feature = XMLConstants.FEATURE_SECURE_PROCESSING;
291 final boolean delegateValue;
292 try {
293 delegateValue = delegate.getFeature(feature);
294 } catch (SAXNotRecognizedException | SAXNotSupportedException e) {
295
296 assertThrows(SAXNotRecognizedException.class, () -> handler.getFeature(feature));
297 return;
298 }
299 assertEquals(delegateValue, handler.getFeature(feature));
300 }
301
302 @Test
303 void delegatesGetContentHandler() throws Exception {
304 final ValidatorHandler delegate = newValidatorHandler();
305 final SecureValidatorHandler handler = new SecureValidatorHandler(delegate);
306 final ContentHandler ch = new DefaultHandler();
307 delegate.setContentHandler(ch);
308 assertSame(ch, handler.getContentHandler());
309 }
310
311 @Test
312 void delegatesGetErrorHandler() throws Exception {
313 final ValidatorHandler delegate = newValidatorHandler();
314 final SecureValidatorHandler handler = new SecureValidatorHandler(delegate);
315 final ErrorHandler eh = new ErrorHandler() {
316
317 @Override
318 public void error(final SAXParseException e) {
319 }
320
321 @Override
322 public void fatalError(final SAXParseException e) {
323 }
324
325 @Override
326 public void warning(final SAXParseException e) {
327 }
328 };
329 delegate.setErrorHandler(eh);
330 assertSame(eh, handler.getErrorHandler());
331 }
332
333 @Test
334 void delegatesProperty() throws Exception {
335 final ValidatorHandler delegate = newValidatorHandler();
336 final SecureValidatorHandler handler = new SecureValidatorHandler(delegate);
337
338
339 final String property = "http://XMLConstants/property/accessExternalDTD";
340 final Object delegateValue;
341 try {
342 delegateValue = delegate.getProperty(property);
343 } catch (SAXNotRecognizedException | SAXNotSupportedException e) {
344 assertThrows(SAXNotRecognizedException.class, () -> handler.getProperty(property));
345 return;
346 }
347 assertSame(delegateValue, handler.getProperty(property));
348 }
349
350 @Test
351 void delegatesSetContentHandler() throws Exception {
352 final ValidatorHandler delegate = newValidatorHandler();
353 final SecureValidatorHandler handler = new SecureValidatorHandler(delegate);
354 final ContentHandler ch = new DefaultHandler();
355 handler.setContentHandler(ch);
356 assertSame(ch, delegate.getContentHandler());
357 }
358
359 @Test
360 void delegatesSetDocumentLocator() throws Exception {
361 final ValidatorHandler delegate = newValidatorHandler();
362 final SecureValidatorHandler handler = new SecureValidatorHandler(delegate);
363 final Locator locator = new Locator() {
364
365 @Override
366 public int getColumnNumber() {
367 return 0;
368 }
369
370 @Override
371 public int getLineNumber() {
372 return 0;
373 }
374
375 @Override
376 public String getPublicId() {
377 return null;
378 }
379
380 @Override
381 public String getSystemId() {
382 return null;
383 }
384 };
385 handler.setDocumentLocator(locator);
386
387 }
388
389 @Test
390 void delegatesSetErrorHandler() throws Exception {
391 final ValidatorHandler delegate = newValidatorHandler();
392 final SecureValidatorHandler handler = new SecureValidatorHandler(delegate);
393 final ErrorHandler eh = new ErrorHandler() {
394
395 @Override
396 public void error(final SAXParseException e) {
397 }
398
399 @Override
400 public void fatalError(final SAXParseException e) {
401 }
402
403 @Override
404 public void warning(final SAXParseException e) {
405 }
406 };
407 handler.setErrorHandler(eh);
408 assertSame(eh, delegate.getErrorHandler());
409 }
410
411 @Test
412 void delegatesTypeInfoProvider() throws Exception {
413 final ValidatorHandler delegate = newValidatorHandler();
414 final SecureValidatorHandler handler = new SecureValidatorHandler(delegate);
415 assertSame(delegate.getTypeInfoProvider(), handler.getTypeInfoProvider());
416 }
417
418 @Test
419 void forwardsCharacters() throws Exception {
420 final RecordingValidatorHandler delegate = new RecordingValidatorHandler();
421 final SecureValidatorHandler handler = new SecureValidatorHandler(delegate);
422 final char[] ch = { 'a', 'b' };
423 handler.characters(ch, 0, 2);
424 assertTrue(delegate.charactersCalled);
425 assertEquals(0, delegate.charactersStart);
426 assertEquals(2, delegate.charactersLength);
427 }
428
429 @Test
430 void forwardsEndDocument() throws Exception {
431 final RecordingValidatorHandler delegate = new RecordingValidatorHandler();
432 final SecureValidatorHandler handler = new SecureValidatorHandler(delegate);
433 handler.endDocument();
434 assertTrue(delegate.endDocumentCalled);
435 }
436
437 @Test
438 void forwardsEndElement() throws Exception {
439 final RecordingValidatorHandler delegate = new RecordingValidatorHandler();
440 final SecureValidatorHandler handler = new SecureValidatorHandler(delegate);
441 handler.endElement("uri", "local", "qName");
442 assertTrue(delegate.endElementCalled);
443 assertEquals("uri", delegate.endElementUri);
444 assertEquals("local", delegate.endElementLocalName);
445 assertEquals("qName", delegate.endElementQName);
446 }
447
448 @Test
449 void forwardsEndPrefixMapping() throws Exception {
450 final RecordingValidatorHandler delegate = new RecordingValidatorHandler();
451 final SecureValidatorHandler handler = new SecureValidatorHandler(delegate);
452 handler.endPrefixMapping("p");
453 assertTrue(delegate.endPrefixMappingCalled);
454 assertEquals("p", delegate.endPrefixMappingPrefix);
455 }
456
457 @Test
458 void forwardsGetProperty() throws Exception {
459 final RecordingValidatorHandler delegate = new RecordingValidatorHandler();
460 final SecureValidatorHandler handler = new SecureValidatorHandler(delegate);
461
462 assertNull(handler.getProperty("any"));
463 }
464
465 @Test
466 void forwardsIgnorableWhitespace() throws Exception {
467 final RecordingValidatorHandler delegate = new RecordingValidatorHandler();
468 final SecureValidatorHandler handler = new SecureValidatorHandler(delegate);
469 final char[] ch = { ' ' };
470 handler.ignorableWhitespace(ch, 0, 1);
471 assertTrue(delegate.ignorableWhitespaceCalled);
472 }
473
474 @Test
475 void forwardsProcessingInstruction() throws Exception {
476 final RecordingValidatorHandler delegate = new RecordingValidatorHandler();
477 final SecureValidatorHandler handler = new SecureValidatorHandler(delegate);
478 handler.processingInstruction("target", "data");
479 assertTrue(delegate.processingInstructionCalled);
480 assertEquals("target", delegate.piTarget);
481 assertEquals("data", delegate.piData);
482 }
483
484 @Test
485 void forwardsSetFeature() throws Exception {
486 final RecordingValidatorHandler delegate = new RecordingValidatorHandler();
487 final SecureValidatorHandler handler = new SecureValidatorHandler(delegate);
488 handler.setFeature("f", true);
489 assertTrue(delegate.setFeatureCalled);
490 assertEquals("f", delegate.setFeatureName);
491 assertTrue(delegate.setFeatureValue);
492 }
493
494 @Test
495 void forwardsSetProperty() throws Exception {
496 final RecordingValidatorHandler delegate = new RecordingValidatorHandler();
497 final SecureValidatorHandler handler = new SecureValidatorHandler(delegate);
498 final Object value = new Object();
499 handler.setProperty("p", value);
500 assertTrue(delegate.setPropertyCalled);
501 assertEquals("p", delegate.setPropertyName);
502 assertSame(value, delegate.setPropertyValue);
503 }
504
505 @Test
506 void forwardsSkippedEntity() throws Exception {
507 final RecordingValidatorHandler delegate = new RecordingValidatorHandler();
508 final SecureValidatorHandler handler = new SecureValidatorHandler(delegate);
509 handler.skippedEntity("name");
510 assertTrue(delegate.skippedEntityCalled);
511 assertEquals("name", delegate.skippedEntityName);
512 }
513
514 @Test
515 void forwardsStartDocument() throws Exception {
516 final RecordingValidatorHandler delegate = new RecordingValidatorHandler();
517 final SecureValidatorHandler handler = new SecureValidatorHandler(delegate);
518 handler.startDocument();
519 assertTrue(delegate.startDocumentCalled);
520 }
521
522 @Test
523 void forwardsStartElement() throws Exception {
524 final RecordingValidatorHandler delegate = new RecordingValidatorHandler();
525 final SecureValidatorHandler handler = new SecureValidatorHandler(delegate);
526 final Attributes attrs = new AttributesImpl();
527 handler.startElement("uri", "local", "qName", attrs);
528 assertTrue(delegate.startElementCalled);
529 assertEquals("uri", delegate.startElementUri);
530 assertEquals("local", delegate.startElementLocalName);
531 assertEquals("qName", delegate.startElementQName);
532 assertSame(attrs, delegate.startElementAttrs);
533 }
534
535 @Test
536 void forwardsStartPrefixMapping() throws Exception {
537 final RecordingValidatorHandler delegate = new RecordingValidatorHandler();
538 final SecureValidatorHandler handler = new SecureValidatorHandler(delegate);
539 handler.startPrefixMapping("p", "u");
540 assertTrue(delegate.startPrefixMappingCalled);
541 assertEquals("p", delegate.startPrefixMappingPrefix);
542 assertEquals("u", delegate.startPrefixMappingUri);
543 }
544
545 @Test
546 void getResourceResolverReturnsDelegateAfterSet() throws Exception {
547 final ValidatorHandler delegate = newValidatorHandler();
548 final SecureValidatorHandler handler = new SecureValidatorHandler(delegate);
549 final LSResourceResolver resolver = (type, namespaceURI, publicId, systemId, baseURI) -> null;
550 handler.setResourceResolver(resolver);
551 assertSame(resolver, handler.getResourceResolver(), "getResourceResolver must return the caller-supplied resolver");
552 }
553
554 @Test
555 void setResourceResolverDoesNotReplaceFloorOnDelegate() throws Exception {
556 final ValidatorHandler delegate = newValidatorHandler();
557 final SecureValidatorHandler handler = new SecureValidatorHandler(delegate);
558 final LSResourceResolver resolver = (type, namespaceURI, publicId, systemId, baseURI) -> null;
559 final LSResourceResolver before = delegate.getResourceResolver();
560 handler.setResourceResolver(resolver);
561
562 assertSame(before, delegate.getResourceResolver(), "delegate resolver must stay the floor");
563 assertSame(resolver, handler.getResourceResolver(), "wrapper must expose caller resolver");
564 }
565
566 @Test
567 void setResourceResolverNullClearsDelegate() throws Exception {
568 final ValidatorHandler delegate = newValidatorHandler();
569 final SecureValidatorHandler handler = new SecureValidatorHandler(delegate);
570 final LSResourceResolver resolver = (type, namespaceURI, publicId, systemId, baseURI) -> null;
571 handler.setResourceResolver(resolver);
572 assertSame(resolver, handler.getResourceResolver());
573 handler.setResourceResolver(null);
574 assertNull(handler.getResourceResolver(), "null resolver must clear the floor delegate");
575 }
576
577 }