Package org.apache.wink.server.internal.providers.entity

Source Code of org.apache.wink.server.internal.providers.entity.SourceProviderTest$SourceResource

/*******************************************************************************
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*   http://www.apache.org/licenses/LICENSE-2.0
*  Unless required by applicable law or agreed to in writing,
*  software distributed under the License is distributed on an
*  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
*  KIND, either express or implied.  See the License for the
*  specific language governing permissions and limitations
*  under the License.
*******************************************************************************/

package org.apache.wink.server.internal.providers.entity;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.StringTokenizer;

import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.MessageBodyReader;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.apache.wink.common.RuntimeContext;
import org.apache.wink.common.internal.WinkConfiguration;
import org.apache.wink.common.internal.providers.entity.SourceProvider;
import org.apache.wink.common.internal.runtime.RuntimeContextTLS;
import org.apache.wink.server.internal.servlet.MockServletInvocationTest;
import org.apache.wink.test.mock.MockRequestConstructor;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;

public class SourceProviderTest extends MockServletInvocationTest {

    private static final String SOURCE       =
                                                 "<?xml version=\"1.0\" encoding=\"UTF-8\"?><message>this is a test message</message>";
    private static final byte[] SOURCE_BYTES = SOURCE.getBytes();
    private String TEST_CLASSES_PATH = null;

    @Override
    public void setUp() throws Exception {
        super.setUp();
        Mockery mockery = new Mockery();
        final RuntimeContext context = mockery.mock(RuntimeContext.class);
        mockery.checking(new Expectations() {{
            allowing(context).getAttribute(WinkConfiguration.class); will(returnValue(null));
        }});
       
        RuntimeContextTLS.setRuntimeContext(context);
    }
   
    @Override
    public void tearDown() {
        RuntimeContextTLS.setRuntimeContext(null);
    }

    private String getPath() {
        if (TEST_CLASSES_PATH == null) {
          String classpath = System.getProperty("java.class.path");
          StringTokenizer tokenizer = new StringTokenizer(classpath, System.getProperty("path.separator"));
          TEST_CLASSES_PATH = null;
          while (tokenizer.hasMoreTokens()) {
              TEST_CLASSES_PATH = tokenizer.nextToken();
              if (TEST_CLASSES_PATH.endsWith("test-classes")) {
                  break;
              }
          }
          // for windows:
          int driveIndex = TEST_CLASSES_PATH.indexOf(":");
          if(driveIndex != -1) {
              TEST_CLASSES_PATH = TEST_CLASSES_PATH.substring(driveIndex + 1);
          }
        }
        return TEST_CLASSES_PATH;
    }
   
    @Override
    protected Class<?>[] getClasses() {
        return new Class<?>[] {SourceResource.class};
    }

    @Path("source")
    public static class SourceResource {

        private static final DocumentBuilderFactory documentBuilderFactory =
                                                                               DocumentBuilderFactory
                                                                                   .newInstance();
        private static final TransformerFactory     transformerFactory     =
                                                                               TransformerFactory
                                                                                   .newInstance();

        @GET
        @Path("stream")
        public Source getStream() {
            return new StreamSource(new ByteArrayInputStream(SOURCE_BYTES));
        }

        @GET
        @Path("sax")
        public Source getSax() {
            return new SAXSource(new InputSource(new ByteArrayInputStream(SOURCE_BYTES)));
        }

        @GET
        @Path("dom")
        public Source getDom() throws Exception {
            return new DOMSource(documentBuilderFactory.newDocumentBuilder()
                .parse(new ByteArrayInputStream(SOURCE_BYTES)));
        }

        @POST
        @Path("stream")
        public String postStream(StreamSource source) throws Exception {
            return extractXml(source);
        }

        @POST
        @Path("sax")
        public String postSax(SAXSource source) throws Exception {
            return extractXml(source);
        }
       
        @POST
        @Path("saxwithdtd")
        public String postSaxWithDTD(SAXSource source) throws Exception {
            /*
             * we don't want to trigger a parse in this resource method.  We're testing to see what happened
             * with the SAXSource on the way here.
             */
            StringBuilder sb = new StringBuilder();
            String line;
            InputStream is = source.getInputSource().getByteStream();
            try {
                BufferedReader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
                while ((line = reader.readLine()) != null) {
                    sb.append(line).append("\n");
                }
            } finally {
                is.close();
            }
            return sb.toString();
        }

        @POST
        @Path("dom")
        public String postDom(DOMSource source) throws Exception {
            return extractXml(source);
        }
       
        @POST
        @Path("domwithdtd")
        public String postDomWithDTD(DOMSource source) throws Exception {
            /*
             * we don't want to trigger a parse in this resource method.  We're testing to see what happened
             * with the SAXSource on the way here.
             */
            return source.getNode().getFirstChild().getFirstChild().getTextContent();
        }

        private String extractXml(Source source) throws TransformerFactoryConfigurationError,
            TransformerConfigurationException, TransformerException {
            Transformer transformer = transformerFactory.newTransformer();
            StringWriter sw = new StringWriter();
            StreamResult sr = new StreamResult(sw);
            transformer.transform(source, sr);
            return sw.toString();
        }

    }

    public void testSourceProvider() throws Exception {
        // stream source
        SourceProvider provider = new SourceProvider.StreamSourceProvider();
        Source source = assertSourceReader(provider, StreamSource.class);
        assertSourceWriter(provider, source);

        // sax source
        provider = new SourceProvider.SAXSourceProvider();
        source = assertSourceReader(provider, SAXSource.class);
        assertSourceWriter(provider, source);

        // dom source
        provider = new SourceProvider.DOMSourceProvider();
        source = assertSourceReader(provider, DOMSource.class);
        assertSourceWriter(provider, source);
    }

    public void testSourceProviderInvocation() throws Exception {
        MockHttpServletRequest request =
            MockRequestConstructor.constructMockRequest("GET", "/source/stream", "text/xml");
        MockHttpServletResponse response = invoke(request);
        assertEquals(200, response.getStatus());
        assertEquals(SOURCE, response.getContentAsString());

        request = MockRequestConstructor.constructMockRequest("GET", "/source/sax", "text/xml");
        response = invoke(request);
        assertEquals(200, response.getStatus());
        assertEquals(SOURCE, response.getContentAsString());

        request = MockRequestConstructor.constructMockRequest("GET", "/source/dom", "text/xml");
        response = invoke(request);
        assertEquals(200, response.getStatus());
        // Ignore Xml declaration, since in 1.6 Transformer always generates xml
        // with "standalone" attribute
        // when DOMSource is serialized
        assertEqualsEgnoreXmlDecl(SOURCE, response.getContentAsString());

        request =
            MockRequestConstructor.constructMockRequest("POST",
                                                        "/source/stream",
                                                        "text/xml",
                                                        "text/xml",
                                                        SOURCE_BYTES);
        response = invoke(request);
        assertEquals(200, response.getStatus());
        assertEquals(SOURCE, response.getContentAsString());

        request =
            MockRequestConstructor.constructMockRequest("POST",
                                                        "/source/sax",
                                                        "text/xml",
                                                        "text/xml",
                                                        SOURCE_BYTES);
        response = invoke(request);
        assertEquals(200, response.getStatus());
        assertEquals(SOURCE, response.getContentAsString());

        request =
            MockRequestConstructor.constructMockRequest("POST",
                                                        "/source/dom",
                                                        "text/xml",
                                                        "text/xml",
                                                        SOURCE_BYTES);
        response = invoke(request);
        assertEquals(200, response.getStatus());
        // Ignore Xml declaration, since in 1.6 Transformer always generates xml
        // with "standalone" attribute
        // when DOMSource is serialized
        assertEqualsEgnoreXmlDecl(SOURCE, response.getContentAsString());

    }
   
    public void testSaxWithDTD() throws Exception {
       
        String path = getPath();
       
        final String SOURCE =
            "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
            "<!DOCTYPE data [<!ENTITY file SYSTEM \""+ path +"/etc/SourceProviderTest.txt\">]>" +
            "<message>&file;</message>";
       
        final byte[] SOURCE_BYTES = SOURCE.getBytes();
       
        MockHttpServletRequest request =
            MockRequestConstructor.constructMockRequest("POST",
                                                        "/source/saxwithdtd",
                                                        "application/xml",
                                                        "application/xml",
                                                        SOURCE_BYTES);
        MockHttpServletResponse response = invoke(request);
        assertEquals(200, response.getStatus());
        assertFalse("File content is visible but should not be.",
                response.getContentAsString().contains("YOU SHOULD NOT BE ABLE TO SEE THIS"));
        assertEquals(SOURCE, response.getContentAsString().trim());
    }
   
    public void testDomWithDTD() throws Exception {
       
        String path = getPath();
       
        final String SOURCE =
            "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
            "<!DOCTYPE data [<!ENTITY file SYSTEM \""+ path +"/etc/SourceProviderTest.txt\">]>" +
            "<message>&file;</message>";

        final byte[] SOURCE_BYTES = SOURCE.getBytes();
       
        MockHttpServletRequest request =
            MockRequestConstructor.constructMockRequest("POST",
                                                        "/source/domwithdtd",
                                                        "application/xml",
                                                        "application/xml",
                                                        SOURCE_BYTES);
        MockHttpServletResponse response = invoke(request);
        assertEquals(400, response.getStatus());
       
        // as a sanity check, let's make sure our xml is good:
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        InputSource is = new InputSource( new StringReader(SOURCE) );
        Document d = builder.parse( is );
        assertEquals("xml is bad", "YOU SHOULD NOT BE ABLE TO SEE THIS", d.getElementsByTagName("message").item(0).getTextContent().trim());
    }
       
    public void testDomWithDTDEntityExpansionAttack1() throws Exception {
       
        String classpath = System.getProperty("java.class.path");
        StringTokenizer tokenizer = new StringTokenizer(classpath, System.getProperty("path.separator"));
        String path = null;
        while (tokenizer.hasMoreTokens()) {
            path = tokenizer.nextToken();
            if (path.endsWith("test-classes")) {
                break;
            }
        }
       
        final String SOURCE = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" +
            "<!DOCTYPE root [" +
            "<!ENTITY % a \"x\">" +
            "<!ENTITY % b \"%a;%a;\">" +
            "]>" +
            "<message>&b;</message>";

        final byte[] SOURCE_BYTES = SOURCE.getBytes();
       
        MockHttpServletRequest request =
            MockRequestConstructor.constructMockRequest("POST",
                                                        "/source/domwithdtd",
                                                        "application/xml",
                                                        "application/xml",
                                                        SOURCE_BYTES);
       
        // The SAX parser will not allow entity refs inside a DTD.  There is no special Wink code necessary for this.
        MockHttpServletResponse response = invoke(request);
        assertEquals(400, response.getStatus());
       
        // as a sanity check, let's make sure the xml is good:
        XMLReader xmlReader = XMLReaderFactory.createXMLReader();
        try {
            xmlReader.parse(new InputSource(new ByteArrayInputStream(SOURCE_BYTES)));
            fail("expected SAXParseException");
        } catch (SAXParseException e) {
           
        }
    }
   
    public void testDomWithDTDEntityExpansionAttack2() throws Exception {
       
        String classpath = System.getProperty("java.class.path");
        StringTokenizer tokenizer = new StringTokenizer(classpath, System.getProperty("path.separator"));
        String path = null;
        while (tokenizer.hasMoreTokens()) {
            path = tokenizer.nextToken();
            if (path.endsWith("test-classes")) {
                break;
            }
        }
       
        final String SOURCE = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" +
            "<!DOCTYPE root [" +
            "<!ENTITY x32 \"foobar\">" +
            "<!ENTITY x31 \"&x32;&x32;\">" +
            "<!ENTITY x30 \"&x31;&x31;\">" +
            "<!ENTITY x29 \"&x30;&x30;\">" +
            "<!ENTITY x28 \"&x29;&x29;\">" +
            "<!ENTITY x27 \"&x28;&x28;\">" +
            "<!ENTITY x26 \"&x27;&x27;\">" +
            "<!ENTITY x25 \"&x26;&x26;\">" +
            "<!ENTITY x24 \"&x25;&x25;\">" +
            "<!ENTITY x23 \"&x24;&x24;\">" +
            "<!ENTITY x22 \"&x23;&x23;\">" +
            "<!ENTITY x21 \"&x22;&x22;\">" +
            "<!ENTITY x20 \"&x21;&x21;\">" +
            "<!ENTITY x19 \"&x20;&x20;\">" +
            "<!ENTITY x18 \"&x19;&x19;\">" +
            "<!ENTITY x17 \"&x18;&x18;\">" +
            "<!ENTITY x16 \"&x17;&x17;\">" +
            "<!ENTITY x15 \"&x16;&x16;\">" +
            "<!ENTITY x14 \"&x15;&x15;\">" +
            "<!ENTITY x13 \"&x14;&x14;\">" +
            "<!ENTITY x12 \"&x13;&x13;\">" +
            "<!ENTITY x11 \"&x12;&x12;\">" +
            "<!ENTITY x10 \"&x11;&x11;\">" +
            "<!ENTITY  x9 \"&x10;&x10;\">" +
            "<!ENTITY  x8 \"&x9;&x9;\">" +
            "<!ENTITY  x7 \"&x8;&x8;\">" +
            "<!ENTITY  x6 \"&x7;&x7;\">" +
            "<!ENTITY  x5 \"&x6;&x6;\">" +
            "<!ENTITY  x4 \"&x5;&x5;\">" +
            "<!ENTITY  x3 \"&x4;&x4;\">" +
            "<!ENTITY  x2 \"&x3;&x3;\">" +
            "<!ENTITY  x1 \"&x2;&x2;\">" +
            "]>" +
            "<message>&x1;</message>";

        final byte[] SOURCE_BYTES = SOURCE.getBytes();
       
        MockHttpServletRequest request =
            MockRequestConstructor.constructMockRequest("POST",
                                                        "/source/domwithdtd",
                                                        "application/xml",
                                                        "application/xml",
                                                        SOURCE_BYTES);
       
        MockHttpServletResponse response = invoke(request);
        assertEquals(400, response.getStatus());
    }

    // -- Helpers

    private void assertSourceWriter(SourceProvider provider, Source source) throws Exception {
        assertTrue(provider
            .isWriteable(source.getClass(), null, null, new MediaType("text", "xml")));
        assertTrue(provider.isWriteable(source.getClass(), null, null, new MediaType("application",
                                                                                     "xml")));
        assertTrue(provider.isWriteable(source.getClass(), null, null, new MediaType("application",
                                                                                     "atom+xml")));
        assertTrue(provider
            .isWriteable(source.getClass(), null, null, new MediaType("application", "atomsvc+xml")));
        assertFalse(provider.isWriteable(source.getClass(), null, null, new MediaType("text",
                                                                                      "plain")));

        ByteArrayOutputStream os = new ByteArrayOutputStream();
        provider.writeTo(source, null, null, null, new MediaType("text", "xml"), null, os);
        // Ignore Xml declaration, since in 1.6 Transformer always generates xml
        // with "standalone" attribute
        // when DOMSource is serialized
        assertEqualsEgnoreXmlDecl(SOURCE, os.toString());
    }

    @SuppressWarnings("unchecked")
    private Source assertSourceReader(SourceProvider provider, Class<?> sourceClass)
        throws Exception {
        ByteArrayInputStream inputStream = new ByteArrayInputStream(SOURCE.getBytes("UTF-8"));
        MessageBodyReader<Source> bodyReader = (MessageBodyReader<Source>)provider;
        assertTrue(bodyReader.isReadable(sourceClass, null, null, new MediaType("text", "xml")));
        assertTrue(bodyReader.isReadable(sourceClass, null, null, new MediaType("application",
                                                                                "xml")));
        assertTrue(bodyReader.isReadable(sourceClass, null, null, new MediaType("application",
                                                                                "atom+xml")));
        assertTrue(bodyReader.isReadable(sourceClass, null, null, new MediaType("application",
                                                                                "atomsvc+xml")));
        assertFalse(bodyReader.isReadable(sourceClass, null, null, new MediaType("text", "plain")));

        Source source =
            bodyReader.readFrom((Class<Source>)sourceClass,
                                null,
                                null,
                                new MediaType("text", "xml"),
                                null,
                                inputStream);
        assertNotNull(source);
        assertEquals(sourceClass, source.getClass());
        return source;
    }

    private void assertEqualsEgnoreXmlDecl(String expected, String actual) {
        expected = removeXmlDecl(expected);
        actual = removeXmlDecl(actual);
        assertEquals(expected, actual);
    }

    private String removeXmlDecl(String expected) {
        if (expected.indexOf("<?xml") >= 0) {
            expected = expected.substring(expected.indexOf("?>"));
        }
        return expected;
    }

}
TOP

Related Classes of org.apache.wink.server.internal.providers.entity.SourceProviderTest$SourceResource

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.