From 135f3344a447de8c0d2ac862a89e67295d226aa5 Mon Sep 17 00:00:00 2001 From: Francois de Parscau Date: Tue, 5 Dec 2023 17:00:14 +0100 Subject: [PATCH] [CXF-8966] : fix validation of nil int in xsd --- .../source/XMLStreamDataReader.java | 53 +++++----- .../source/XMLStreamDataReaderTest.java | 98 +++++++++++++++++++ .../databinding/source/resources/schema.xsd | 11 +++ .../source/resources/test-invalid.xml | 5 + .../source/resources/test-valid.xml | 7 ++ 5 files changed, 144 insertions(+), 30 deletions(-) create mode 100644 core/src/test/java/org/apache/cxf/databinding/source/resources/schema.xsd create mode 100644 core/src/test/java/org/apache/cxf/databinding/source/resources/test-invalid.xml create mode 100644 core/src/test/java/org/apache/cxf/databinding/source/resources/test-valid.xml diff --git a/core/src/main/java/org/apache/cxf/databinding/source/XMLStreamDataReader.java b/core/src/main/java/org/apache/cxf/databinding/source/XMLStreamDataReader.java index 77e832366e1..6cb2c8252c1 100644 --- a/core/src/main/java/org/apache/cxf/databinding/source/XMLStreamDataReader.java +++ b/core/src/main/java/org/apache/cxf/databinding/source/XMLStreamDataReader.java @@ -28,7 +28,6 @@ import javax.xml.namespace.QName; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; -import javax.xml.stream.XMLStreamWriter; import javax.xml.transform.Source; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.sax.SAXSource; @@ -216,33 +215,37 @@ public void close() throws XMLStreamException { } private Element validate(XMLStreamReader input) throws XMLStreamException, IOException { - DOMSource ds = read(input); - final Element rootElement; - if (ds.getNode() instanceof Document) { - rootElement = ((Document)ds.getNode()).getDocumentElement(); - } else { - rootElement = (Element)ds.getNode(); - } - + Element rootElement; WoodstoxValidationImpl impl = new WoodstoxValidationImpl(); - XMLStreamWriter nullWriter = null; + XMLStreamReader inputWithoutXop = null; if (impl.canValidate()) { - nullWriter = StaxUtils.createXMLStreamWriter(new NUllOutputStream()); - impl.setupValidation(nullWriter, message.getExchange().getEndpoint(), - message.getExchange().getService().getServiceInfos().get(0)); + //filter xop node, which causes validation to fail + XMLStreamReader filteredReader = + StaxUtils.createFilteredReader(input, new StaxStreamFilter(XOP)); + try (CachedOutputStream out = new CachedOutputStream()) { + StaxUtils.copy(filteredReader, out); + inputWithoutXop = StaxUtils.createXMLStreamReader(out.getInputStream()); + impl.setupValidation(inputWithoutXop, message.getExchange().getEndpoint(), + message.getExchange().getService().getServiceInfos().get(0)); + } finally { + filteredReader.close(); + } } //check if the impl can still validate after the setup, possible issue loading schemas or similar if (impl.canValidate()) { //Can use the MSV libs and woodstox to handle the schema validation during - //parsing and processing. Much faster and single traversal - //filter xop node - XMLStreamReader reader = StaxUtils.createXMLStreamReader(ds); - XMLStreamReader filteredReader = - StaxUtils.createFilteredReader(reader, - new StaxStreamFilter(new QName[] {XOP})); + //parsing and processing. Much faster and single traversal + rootElement = StaxUtils.read(inputWithoutXop).getDocumentElement(); - StaxUtils.copy(filteredReader, nullWriter); } else { + + DOMSource ds = read(input); + if (ds.getNode() instanceof Document) { + rootElement = ((Document)ds.getNode()).getDocumentElement(); + } else { + rootElement = (Element)ds.getNode(); + } + //MSV not available, use a slower method of cloning the data, replace the xop's, validate LOG.fine("NO_MSV_AVAILABLE"); Element newElement = rootElement; @@ -314,14 +317,4 @@ public void setProperty(String prop, Object value) { message = (Message)value; } } - - static class NUllOutputStream extends OutputStream { - public void write(byte[] b, int off, int len) { - } - public void write(int b) { - } - - public void write(byte[] b) throws IOException { - } - } } diff --git a/core/src/test/java/org/apache/cxf/databinding/source/XMLStreamDataReaderTest.java b/core/src/test/java/org/apache/cxf/databinding/source/XMLStreamDataReaderTest.java index 8d6684fe66e..e0b5e0d7ab9 100755 --- a/core/src/test/java/org/apache/cxf/databinding/source/XMLStreamDataReaderTest.java +++ b/core/src/test/java/org/apache/cxf/databinding/source/XMLStreamDataReaderTest.java @@ -22,19 +22,45 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.net.URL; +import javax.xml.XMLConstants; import javax.xml.namespace.QName; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamReader; +import javax.xml.transform.dom.DOMSource; +import javax.xml.validation.Schema; +import javax.xml.validation.SchemaFactory; +import org.w3c.dom.Document; + +import com.ctc.wstx.msv.W3CSchemaFactory; + +import org.apache.cxf.endpoint.Endpoint; import org.apache.cxf.interceptor.Fault; +import org.apache.cxf.message.Exchange; +import org.apache.cxf.message.ExchangeImpl; import org.apache.cxf.message.Message; import org.apache.cxf.message.MessageImpl; +import org.apache.cxf.service.Service; +import org.apache.cxf.service.ServiceImpl; +import org.apache.cxf.service.model.MessageInfo; +import org.apache.cxf.service.model.MessagePartInfo; +import org.apache.cxf.service.model.ServiceInfo; import org.apache.cxf.staxutils.StaxUtils; +import org.codehaus.stax2.XMLStreamReader2; +import org.codehaus.stax2.validation.XMLValidationSchema; import org.junit.Test; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; /** * @@ -92,6 +118,78 @@ public void testParseNullByte() throws Exception { ((XMLStreamReader)obj).close(); } + @Test + public void testValid() throws Exception { + testValidate("resources/schema.xsd", "resources/test-valid.xml", false); + } + + @Test + public void testInValid() throws Exception { + testValidate("resources/schema.xsd", "resources/test-invalid.xml", true); + } + + + private void testValidate(String schemaPath, String xmlPath, boolean exceptionExpected) throws Exception { + + //create schema + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + documentBuilderFactory.setNamespaceAware(true); + DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); + URL schemaURI = getClass().getResource(schemaPath); + Document wsdl = documentBuilder.parse(schemaURI.openStream()); + String wsdlSystemId = schemaURI.toExternalForm(); + DOMSource source = new DOMSource(wsdl); + source.setSystemId(wsdlSystemId); + source.setSystemId(wsdlSystemId); + + XMLValidationSchema schemaw3c = + W3CSchemaFactory.newInstance(XMLValidationSchema.SCHEMA_ID_W3C_SCHEMA).createSchema(schemaURI); + SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + Schema schema = schemaFactory.newSchema(schemaURI); + + XMLStreamDataReader reader = new XMLStreamDataReader(); + reader.setSchema(schema); + + + InputStream testIS = getClass().getResourceAsStream(xmlPath); + Message msg = new MessageImpl(); + Exchange exchange = new ExchangeImpl(); + + ServiceInfo serviceInfo = new ServiceInfo(); + + Endpoint endpoint = mock(Endpoint.class); + when(endpoint.get(XMLValidationSchema.class.getName())).thenReturn(schemaw3c); + + Service svc = new ServiceImpl(serviceInfo); + + exchange.put(Service.class, svc); + exchange.put(Endpoint.class, endpoint); + + msg.setExchange(exchange); + msg.setContent(InputStream.class, testIS); + reader.setProperty(Message.class.getName(), msg); + + XMLInputFactory xmlFactory = XMLInputFactory.newInstance(); + XMLStreamReader2 xmlStreamReader = (XMLStreamReader2) xmlFactory.createXMLStreamReader(testIS, "utf-8"); + + MessageInfo messageInfo = new MessageInfo(null, + MessageInfo.Type.INPUT, + new QName("http://www.test.org/services", + "NullTestOperationRequest")); + MessagePartInfo messagePartInfo = new MessagePartInfo(new QName( + "http://www.test.org/services", "NullTestOperationRequest"), messageInfo); + messagePartInfo.setElement(true); + boolean exceptionCaught = false; + try { + reader.read(messagePartInfo, xmlStreamReader); + } catch (Fault fault) { + exceptionCaught = true; + } catch (Exception exc) { + fail(exc.getMessage()); + } + assertEquals(exceptionExpected, exceptionCaught); + } + private static class TestInputStream extends ByteArrayInputStream { private boolean closed; diff --git a/core/src/test/java/org/apache/cxf/databinding/source/resources/schema.xsd b/core/src/test/java/org/apache/cxf/databinding/source/resources/schema.xsd new file mode 100644 index 00000000000..a69a05d55ca --- /dev/null +++ b/core/src/test/java/org/apache/cxf/databinding/source/resources/schema.xsd @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/core/src/test/java/org/apache/cxf/databinding/source/resources/test-invalid.xml b/core/src/test/java/org/apache/cxf/databinding/source/resources/test-invalid.xml new file mode 100644 index 00000000000..74d88711633 --- /dev/null +++ b/core/src/test/java/org/apache/cxf/databinding/source/resources/test-invalid.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/core/src/test/java/org/apache/cxf/databinding/source/resources/test-valid.xml b/core/src/test/java/org/apache/cxf/databinding/source/resources/test-valid.xml new file mode 100644 index 00000000000..e96ff05fd6d --- /dev/null +++ b/core/src/test/java/org/apache/cxf/databinding/source/resources/test-valid.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file