1919package org .apache .xml .security .test .stax ;
2020
2121import java .io .BufferedReader ;
22+ import java .io .ByteArrayInputStream ;
2223import java .io .ByteArrayOutputStream ;
2324import java .io .InputStream ;
2425import java .io .InputStreamReader ;
5152import org .junit .jupiter .api .Test ;
5253import org .xmlunit .matchers .CompareMatcher ;
5354
55+ import static org .hamcrest .MatcherAssert .assertThat ;
56+ import static org .hamcrest .Matchers .equalTo ;
57+ import static org .hamcrest .Matchers .is ;
5458import static org .junit .jupiter .api .Assertions .assertEquals ;
5559import static org .junit .jupiter .api .Assertions .assertFalse ;
5660import static org .junit .jupiter .api .Assertions .assertTrue ;
@@ -105,6 +109,70 @@ public void testIdentityTransformSource() throws Exception {
105109 MatcherAssert .assertThat (readTestFile (), CompareMatcher .isSimilarTo (baos .toString (StandardCharsets .UTF_8 .name ())));
106110 }
107111
112+ @ Test
113+ public void testDocumentDeclaration () throws Exception {
114+ String xml = "<?xml version='1.1' encoding='ISO-8859-1' standalone='yes'?>\n "
115+ + "<Document/>" ;
116+ ByteArrayInputStream xmlInput = new ByteArrayInputStream (xml .getBytes (StandardCharsets .ISO_8859_1 ));
117+ XMLInputFactory xmlInputFactory = XMLInputFactory .newInstance ();
118+ XMLStreamReader stdXmlStreamReader = xmlInputFactory .createXMLStreamReader (xmlInput );
119+ InboundSecurityContextImpl securityContext = new InboundSecurityContextImpl ();
120+ InputProcessorChainImpl inputProcessorChain = new InputProcessorChainImpl (securityContext );
121+ inputProcessorChain .addProcessor (new EventReaderProcessor (stdXmlStreamReader ));
122+ XMLSecurityProperties securityProperties = new XMLSecurityProperties ();
123+ securityProperties .setSkipDocumentEvents (false );
124+ XMLSecurityStreamReader xmlSecurityStreamReader = new XMLSecurityStreamReader (inputProcessorChain , securityProperties );
125+ advanceToFirstEvent (xmlSecurityStreamReader );
126+ assertThat (xmlSecurityStreamReader .getEventType (), is (XMLStreamConstants .START_DOCUMENT ));
127+ assertThat (xmlSecurityStreamReader .getVersion (), is (equalTo ("1.1" )));
128+ assertThat (xmlSecurityStreamReader .getCharacterEncodingScheme (), is (equalTo ("ISO-8859-1" )));
129+ assertThat (xmlSecurityStreamReader .isStandalone (), is (true ));
130+ assertThat (xmlSecurityStreamReader .standaloneSet (), is (true ));
131+
132+ assertThat (xmlSecurityStreamReader .hasNext (), is (true ));
133+ assertThat (xmlSecurityStreamReader .next (), is (XMLStreamConstants .START_ELEMENT ));
134+ // Strictly speaking, we should assert that getVersion() etc. throw when not on a START_DOCUMENT event.
135+ // However, we have to be lenient to compensate for XML reader implementations such as Xalan,
136+ // which access getVersion() etc. when they're positioned on START_ELEMENT, _beyond_ START_DOCUMENT.
137+ assertThat (xmlSecurityStreamReader .getVersion (), is (equalTo ("1.1" )));
138+ assertThat (xmlSecurityStreamReader .getCharacterEncodingScheme (), is (equalTo ("ISO-8859-1" )));
139+ assertThat (xmlSecurityStreamReader .isStandalone (), is (true ));
140+ assertThat (xmlSecurityStreamReader .standaloneSet (), is (true ));
141+ }
142+
143+ @ Test
144+ public void testDocumentDeclarationWhenSkipDocumentEvents () throws Exception {
145+ String xml = "<?xml version='1.1' encoding='ISO-8859-1' standalone='yes'?>\n "
146+ + "<Document/>" ;
147+ ByteArrayInputStream xmlInput = new ByteArrayInputStream (xml .getBytes (StandardCharsets .ISO_8859_1 ));
148+ XMLInputFactory xmlInputFactory = XMLInputFactory .newInstance ();
149+ XMLStreamReader stdXmlStreamReader = xmlInputFactory .createXMLStreamReader (xmlInput );
150+ InboundSecurityContextImpl securityContext = new InboundSecurityContextImpl ();
151+ InputProcessorChainImpl inputProcessorChain = new InputProcessorChainImpl (securityContext );
152+ inputProcessorChain .addProcessor (new EventReaderProcessor (stdXmlStreamReader ));
153+ XMLSecurityProperties securityProperties = new XMLSecurityProperties ();
154+ securityProperties .setSkipDocumentEvents (true );
155+ XMLSecurityStreamReader xmlSecurityStreamReader = new XMLSecurityStreamReader (inputProcessorChain , securityProperties );
156+ advanceToFirstEvent (xmlSecurityStreamReader );
157+ assertThat (xmlSecurityStreamReader .getEventType (), is (XMLStreamConstants .START_ELEMENT ));
158+ assertThat (xmlSecurityStreamReader .getVersion (), is (equalTo ("1.1" )));
159+ assertThat (xmlSecurityStreamReader .getCharacterEncodingScheme (), is (equalTo ("ISO-8859-1" )));
160+ assertThat (xmlSecurityStreamReader .isStandalone (), is (true ));
161+ assertThat (xmlSecurityStreamReader .standaloneSet (), is (true ));
162+ }
163+
164+ /**
165+ * This method advances the reader until it's <i>on</i> the first event, if that's not already the case.
166+ * Depending on the implementation, the {@code xmlStreamReader} may be positioned <i>before</i> or <i>on</i>
167+ * the first event upon creation.
168+ */
169+ private static void advanceToFirstEvent (XMLStreamReader xmlStreamReader ) throws XMLStreamException {
170+ if (xmlStreamReader .getEventType () <= 0 ) {
171+ assertThat (xmlStreamReader .hasNext (), is (true ));
172+ xmlStreamReader .next ();
173+ }
174+ }
175+
108176 @ Test
109177 public void testCorrectness () throws Exception {
110178 XMLSecurityProperties securityProperties = new XMLSecurityProperties ();
@@ -123,6 +191,9 @@ public void testCorrectness() throws Exception {
123191 "org/apache/xml/security/c14n/inExcl/plain-soap-1.1.xml" ));
124192
125193 //hmm why does a streamreader return a DOCUMENT_EVENT before we did call next() ??
194+ // A: Because some implementations of XMLStreamReader are positioned _on_ the first event rather than before it.
195+ // Woodstox is a typical example of such an implementation.
196+ // We have to compensate for both types of implementation, see the method advanceToFirstEvent(XMLStreamReader).
126197 int stdXMLEventType = stdXmlStreamReader .getEventType ();
127198 int secXMLEventType = xmlSecurityStreamReader .getEventType ();
128199 do {
@@ -213,9 +284,9 @@ public void testCorrectness() throws Exception {
213284 assertEquals (stdXmlStreamReader .getTextLength (), xmlSecurityStreamReader .getTextLength ());
214285 break ;
215286 case XMLStreamConstants .START_DOCUMENT :
216- assertEquals (stdXmlStreamReader . getCharacterEncodingScheme (), xmlSecurityStreamReader .getCharacterEncodingScheme ());
287+ assertEquals (StandardCharsets . UTF_8 . name (), xmlSecurityStreamReader .getCharacterEncodingScheme ());
217288 assertEquals (stdXmlStreamReader .getEncoding (), xmlSecurityStreamReader .getEncoding ());
218- // assertEquals(stdXmlStreamReader.getVersion(), xmlSecurityStreamReader.getVersion());
289+ assertEquals (stdXmlStreamReader .getVersion (), xmlSecurityStreamReader .getVersion ());
219290 break ;
220291 case XMLStreamConstants .END_DOCUMENT :
221292 break ;
@@ -269,17 +340,24 @@ private String readTestFile() throws Exception {
269340 return stringBuilder .toString ();
270341 }
271342
343+ private static XMLStreamReader createXmlStreamReader () throws XMLStreamException {
344+ XMLInputFactory xmlInputFactory = XMLInputFactory .newInstance ();
345+ xmlInputFactory .setProperty (XMLInputFactory .IS_COALESCING , true );
346+ xmlInputFactory .setProperty (XMLInputFactory .IS_NAMESPACE_AWARE , true );
347+ return xmlInputFactory .createXMLStreamReader (InputProcessor .class .getClassLoader ().getResourceAsStream (
348+ "org/apache/xml/security/c14n/inExcl/plain-soap-1.1.xml" ));
349+ }
350+
272351 class EventReaderProcessor implements InputProcessor {
273352
274353 private XMLStreamReader xmlStreamReader ;
275354
355+ EventReaderProcessor (XMLStreamReader xmlStreamReader ) {
356+ this .xmlStreamReader = xmlStreamReader ;
357+ }
358+
276359 EventReaderProcessor () throws Exception {
277- XMLInputFactory xmlInputFactory = XMLInputFactory .newInstance ();
278- xmlInputFactory .setProperty (XMLInputFactory .IS_COALESCING , true );
279- xmlInputFactory .setProperty (XMLInputFactory .IS_NAMESPACE_AWARE , true );
280- xmlStreamReader =
281- xmlInputFactory .createXMLStreamReader (this .getClass ().getClassLoader ().getResourceAsStream (
282- "org/apache/xml/security/c14n/inExcl/plain-soap-1.1.xml" ));
360+ this (createXmlStreamReader ());
283361 }
284362
285363 @ Override
0 commit comments