This is an automated email from the ASF dual-hosted git repository.
coheigea pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cxf.git
The following commit(s) were added to refs/heads/main by this push:
new ea98a784aa0 Reject unexpected SOAP Envelope children before the Body
(#3710)
ea98a784aa0 is described below
commit ea98a784aa075512fc477f949c8df61426d019a9
Author: Colm O hEigeartaigh <[email protected]>
AuthorDate: Thu Nov 12 13:15:03 2026 +0000
Reject unexpected SOAP Envelope children before the Body (#3710)
---
.../binding/soap/interceptor/Messages.properties | 1 +
.../soap/interceptor/ReadHeadersInterceptor.java | 17 ++++++
.../interceptor/ReadHeadersInterceptorTest.java | 62 +++++++++++++++++++++-
3 files changed, 79 insertions(+), 1 deletion(-)
diff --git
a/rt/bindings/soap/src/main/java/org/apache/cxf/binding/soap/interceptor/Messages.properties
b/rt/bindings/soap/src/main/java/org/apache/cxf/binding/soap/interceptor/Messages.properties
index 8d5e174d3e0..e8a64fd4da3 100644
---
a/rt/bindings/soap/src/main/java/org/apache/cxf/binding/soap/interceptor/Messages.properties
+++
b/rt/bindings/soap/src/main/java/org/apache/cxf/binding/soap/interceptor/Messages.properties
@@ -26,6 +26,7 @@ MUST_UNDERSTAND=MustUnderstand headers: {0} are not
understood.
PARSER_EXC=Could not create DOM DocumentBuilder.
NO_OPERATION=No such operation: {0}
NO_SOAP_BODY=No soap body
+UNEXPECTED_ENVELOPE_CHILD=Unexpected element {0} before the SOAP Body
ATTACHMENT_IO=Attachment IO Exception: {0}
INVALID_VERSION="{0}", the namespace on the "{1}" element, is not a valid SOAP
version.
INVALID_11_VERSION=A SOAP 1.2 message is not valid when sent to a SOAP 1.1
only endpoint.
diff --git
a/rt/bindings/soap/src/main/java/org/apache/cxf/binding/soap/interceptor/ReadHeadersInterceptor.java
b/rt/bindings/soap/src/main/java/org/apache/cxf/binding/soap/interceptor/ReadHeadersInterceptor.java
index 7376c059e69..938214e5768 100644
---
a/rt/bindings/soap/src/main/java/org/apache/cxf/binding/soap/interceptor/ReadHeadersInterceptor.java
+++
b/rt/bindings/soap/src/main/java/org/apache/cxf/binding/soap/interceptor/ReadHeadersInterceptor.java
@@ -358,6 +358,7 @@ public class ReadHeadersInterceptor extends
AbstractSoapInterceptor {
private final String header;
private final String body;
private final String envelope;
+ private final SoapVersion version;
private final List<XMLEvent> events = new ArrayList<>(8);
private List<XMLEvent> envEvents;
private List<XMLEvent> bodyEvents;
@@ -378,6 +379,7 @@ public class ReadHeadersInterceptor extends
AbstractSoapInterceptor {
}
HeadersProcessor(SoapVersion version) {
+ this.version = version;
this.header = version.getHeader().getLocalPart();
this.ns = version.getEnvelope().getNamespaceURI();
this.envelope = version.getEnvelope().getLocalPart();
@@ -392,6 +394,9 @@ public class ReadHeadersInterceptor extends
AbstractSoapInterceptor {
switch (event) {
case XMLStreamConstants.START_ELEMENT:
read++;
+ if (doc == null && read == 2) {
+ checkEnvelopeChild(reader);
+ }
addEvent(eventFactory.createStartElement(new
QName(reader.getNamespaceURI(), reader
.getLocalName(),
reader.getPrefix()), null, null));
for (int i = 0; i < reader.getNamespaceCount(); i++) {
@@ -442,6 +447,18 @@ public class ReadHeadersInterceptor extends
AbstractSoapInterceptor {
return doc;
}
+ /**
+ * Before the SOAP Body, the only child the Envelope may have is the
SOAP Header. Reject anything
+ * else here, as the events read before the Header are buffered in
memory.
+ */
+ private void checkEnvelopeChild(XMLStreamReader reader) {
+ if (!ns.equals(reader.getNamespaceURI())
+ || !header.equals(reader.getLocalName()) &&
!body.equals(reader.getLocalName())) {
+ throw new SoapFault(new Message("UNEXPECTED_ENVELOPE_CHILD",
LOG, reader.getName()),
+ version.getSender());
+ }
+ }
+
private void addEvent(XMLEvent event) {
if (event.isStartElement()) {
lastStartElementQName = event.asStartElement().getName();
diff --git
a/rt/bindings/soap/src/test/java/org/apache/cxf/binding/soap/interceptor/ReadHeadersInterceptorTest.java
b/rt/bindings/soap/src/test/java/org/apache/cxf/binding/soap/interceptor/ReadHeadersInterceptorTest.java
index 906d42a28b3..04b53c7ebfe 100644
---
a/rt/bindings/soap/src/test/java/org/apache/cxf/binding/soap/interceptor/ReadHeadersInterceptorTest.java
+++
b/rt/bindings/soap/src/test/java/org/apache/cxf/binding/soap/interceptor/ReadHeadersInterceptorTest.java
@@ -31,6 +31,8 @@ import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.apache.cxf.binding.soap.Soap11;
+import org.apache.cxf.binding.soap.Soap12;
+import org.apache.cxf.binding.soap.SoapFault;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.headers.Header;
import org.apache.cxf.helpers.CastUtils;
@@ -44,6 +46,8 @@ import org.junit.runners.Parameterized;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
/**
*
@@ -130,9 +134,65 @@ public class ReadHeadersInterceptorTest {
assertEquals(expectedTransferredAttributeCount,
attributes.getLength());
}
+ @Test
+ public void testWhitespaceAndCommentsBeforeHeader() throws Exception {
+ SoapMessage message = setUpMessage(("<soap:Envelope
xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'>"
+ + "\n <!-- comment -->\n "
+ + "<soap:Header><x:AuthToken
xmlns:x='http://example.com/schema'>Token</x:AuthToken></soap:Header>"
+ + "<soap:Body><ns2:payload xmlns:ns2='urn:tmp:foo'/></soap:Body>"
+ + "</soap:Envelope>").getBytes());
+ interceptor.handleMessage(message);
+ assertEquals(1, message.getHeaders().size());
+ }
+
+ @Test
+ public void testNoHeader() throws Exception {
+ SoapMessage message = setUpMessage(("<soap:Envelope
xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'>"
+ + "<soap:Body><ns2:payload xmlns:ns2='urn:tmp:foo'/></soap:Body>"
+ + "</soap:Envelope>").getBytes());
+ interceptor.handleMessage(message);
+ assertTrue(message.getHeaders().isEmpty());
+ }
+
+ @Test
+ public void testUnexpectedElementBeforeBody() throws Exception {
+ SoapMessage message = setUpMessage(("<soap:Envelope
xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'>"
+ + "<foo><bar/></foo>"
+ + "<soap:Body><ns2:payload xmlns:ns2='urn:tmp:foo'/></soap:Body>"
+ + "</soap:Envelope>").getBytes());
+ SoapFault fault = assertThrows(SoapFault.class, () ->
interceptor.handleMessage(message));
+ assertEquals(Soap11.getInstance().getSender(), fault.getFaultCode());
+ }
+
+ @Test
+ public void testUnexpectedElementBeforeHeader() throws Exception {
+ SoapMessage message = setUpMessage(("<soap:Envelope
xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'>"
+ + "<soap:Fault/>"
+ + "<soap:Header><x:AuthToken
xmlns:x='http://example.com/schema'>Token</x:AuthToken></soap:Header>"
+ + "<soap:Body><ns2:payload xmlns:ns2='urn:tmp:foo'/></soap:Body>"
+ + "</soap:Envelope>").getBytes());
+ assertThrows(SoapFault.class, () ->
interceptor.handleMessage(message));
+ }
+
+ @Test
+ public void testUnexpectedElementBeforeBodySoap12() throws Exception {
+ SoapMessage message = new SoapMessage(Soap12.getInstance());
+ message.setContent(XMLStreamReader.class,
StaxUtils.createXMLStreamReader(new ByteArrayInputStream(
+ ("<soap:Envelope
xmlns:soap='http://www.w3.org/2003/05/soap-envelope'>"
+ + "<foo/>"
+ + "<soap:Body><ns2:payload xmlns:ns2='urn:tmp:foo'/></soap:Body>"
+ + "</soap:Envelope>").getBytes())));
+ SoapFault fault = assertThrows(SoapFault.class, () ->
interceptor.handleMessage(message));
+ assertEquals(Soap12.getInstance().getSender(), fault.getFaultCode());
+ }
+
private SoapMessage setUpMessage() throws Exception {
+ return setUpMessage(TEST_SOAP);
+ }
+
+ private SoapMessage setUpMessage(byte[] soap) throws Exception {
SoapMessage message = new SoapMessage(Soap11.getInstance());
- message.setContent(XMLStreamReader.class,
StaxUtils.createXMLStreamReader(new ByteArrayInputStream(TEST_SOAP)));
+ message.setContent(XMLStreamReader.class,
StaxUtils.createXMLStreamReader(new ByteArrayInputStream(soap)));
return message;
}