Author: bdelacretaz Date: Mon Jan 5 08:15:27 2009 New Revision: 731608 URL: http://svn.apache.org/viewvc?rev=731608&view=rev Log: SLING-760 - move escaping code to DefaultErrorHandlerServlet (and SlingMainServlet, but that's only used as a last resort IIUC) and add tests
Added: incubator/sling/trunk/launchpad/testing/src/test/java/org/apache/sling/launchpad/webapp/integrationtest/issues/SLING760Test.java (with props) incubator/sling/trunk/launchpad/testing/src/test/resources/integration-test/issues/sling760/ incubator/sling/trunk/launchpad/testing/src/test/resources/integration-test/issues/sling760/throw-with-markup.esp Modified: incubator/sling/trunk/commons/testing/src/main/java/org/apache/sling/commons/testing/integration/HttpTestBase.java incubator/sling/trunk/engine/src/main/java/org/apache/sling/engine/ResponseUtil.java incubator/sling/trunk/engine/src/main/java/org/apache/sling/engine/impl/SlingMainServlet.java incubator/sling/trunk/engine/src/test/java/org/apache/sling/engine/ResponseUtilTest.java incubator/sling/trunk/servlets/resolver/pom.xml incubator/sling/trunk/servlets/resolver/src/main/java/org/apache/sling/servlets/resolver/defaults/DefaultErrorHandlerServlet.java Modified: incubator/sling/trunk/commons/testing/src/main/java/org/apache/sling/commons/testing/integration/HttpTestBase.java URL: http://svn.apache.org/viewvc/incubator/sling/trunk/commons/testing/src/main/java/org/apache/sling/commons/testing/integration/HttpTestBase.java?rev=731608&r1=731607&r2=731608&view=diff ============================================================================== --- incubator/sling/trunk/commons/testing/src/main/java/org/apache/sling/commons/testing/integration/HttpTestBase.java (original) +++ incubator/sling/trunk/commons/testing/src/main/java/org/apache/sling/commons/testing/integration/HttpTestBase.java Mon Jan 5 08:15:27 2009 @@ -27,6 +27,8 @@ import java.util.List; import java.util.Map; +import javax.servlet.http.HttpServletResponse; + import junit.framework.TestCase; import org.apache.commons.httpclient.Credentials; @@ -282,11 +284,15 @@ return getContent(url, expectedContentType, null); } + protected String getContent(String url, String expectedContentType, List<NameValuePair> params) throws IOException { + return getContent(url, expectedContentType, params, HttpServletResponse.SC_OK); + } + /** retrieve the contents of given URL and assert its content type * @param expectedContentType use CONTENT_TYPE_DONTCARE if must not be checked * @throws IOException * @throws HttpException */ - protected String getContent(String url, String expectedContentType, List<NameValuePair> params) throws IOException { + protected String getContent(String url, String expectedContentType, List<NameValuePair> params, int expectedStatusCode) throws IOException { final GetMethod get = new GetMethod(url); if(params != null) { final NameValuePair [] nvp = new NameValuePair[0]; @@ -301,7 +307,8 @@ while( (n = is.read(buffer, 0, buffer.length)) > 0) { content.append(new String(buffer, 0, n, charset)); } - assertEquals("Expected status 200 for " + url + " (content=" + content + ")",200,status); + assertEquals("Expected status " + expectedStatusCode + " for " + url + " (content=" + content + ")", + expectedStatusCode,status); final Header h = get.getResponseHeader("Content-Type"); if(expectedContentType == null) { if(h!=null) { Modified: incubator/sling/trunk/engine/src/main/java/org/apache/sling/engine/ResponseUtil.java URL: http://svn.apache.org/viewvc/incubator/sling/trunk/engine/src/main/java/org/apache/sling/engine/ResponseUtil.java?rev=731608&r1=731607&r2=731608&view=diff ============================================================================== --- incubator/sling/trunk/engine/src/main/java/org/apache/sling/engine/ResponseUtil.java (original) +++ incubator/sling/trunk/engine/src/main/java/org/apache/sling/engine/ResponseUtil.java Mon Jan 5 08:15:27 2009 @@ -18,9 +18,65 @@ */ package org.apache.sling.engine; +import java.io.IOException; +import java.io.Writer; + /** Response-related utilities */ public class ResponseUtil { + private static class XmlEscapingWriter extends Writer { + private final Writer target; + + XmlEscapingWriter(Writer target) { + this.target = target; + } + + @Override + public void close() throws IOException { + target.close(); + } + + @Override + public void flush() throws IOException { + target.flush(); + } + + @Override + public void write(char[] buffer, int offset, int length) throws IOException { + for(int i = offset; i < offset + length; i++) { + write(buffer[i]); + } + } + + @Override + public void write(char[] cbuf) throws IOException { + write(cbuf, 0, cbuf.length); + } + + @Override + public void write(int c) throws IOException { + if(c == '&') { + target.write("&"); + } else if(c == '<') { + target.write("<"); + } else if(c == '>') { + target.write(">"); + } else { + target.write(c); + } + } + + @Override + public void write(String str, int off, int len) throws IOException { + write(str.toCharArray(), off, len); + } + + @Override + public void write(String str) throws IOException { + write(str.toCharArray()); + } + } + /** Escape xml text */ public static String escapeXml(String input) { if(input == null) { @@ -42,4 +98,10 @@ } return b.toString(); } + + /** Return a Writer that writes escaped XML text to target + */ + public static Writer getXmlEscapingWriter(Writer target) { + return new XmlEscapingWriter(target); + } } Modified: incubator/sling/trunk/engine/src/main/java/org/apache/sling/engine/impl/SlingMainServlet.java URL: http://svn.apache.org/viewvc/incubator/sling/trunk/engine/src/main/java/org/apache/sling/engine/impl/SlingMainServlet.java?rev=731608&r1=731607&r2=731608&view=diff ============================================================================== --- incubator/sling/trunk/engine/src/main/java/org/apache/sling/engine/impl/SlingMainServlet.java (original) +++ incubator/sling/trunk/engine/src/main/java/org/apache/sling/engine/impl/SlingMainServlet.java Mon Jan 5 08:15:27 2009 @@ -58,6 +58,7 @@ import org.apache.sling.api.resource.ResourceResolver; import org.apache.sling.api.servlets.ServletResolver; import org.apache.sling.commons.mime.MimeTypeService; +import org.apache.sling.engine.ResponseUtil; import org.apache.sling.engine.impl.auth.MissingRepositoryException; import org.apache.sling.engine.impl.auth.SlingAuthenticator; import org.apache.sling.engine.impl.filter.RequestSlingFilterChain; @@ -516,17 +517,17 @@ PrintWriter pw = response.getWriter(); pw.println("<html><head><title>"); - pw.println(message); + pw.println(ResponseUtil.escapeXml(message)); pw.println("</title></head><body><h1>"); if (throwable != null) { - pw.println(throwable.toString()); + pw.println(ResponseUtil.escapeXml(throwable.toString())); } else if (message != null) { - pw.println(message); + pw.println(ResponseUtil.escapeXml(message)); } else { pw.println("Internal error (no Exception to report)"); } pw.println("</h1><p>"); - pw.println("RequestURI=" + request.getRequestURI()); + pw.println("RequestURI=" + ResponseUtil.escapeXml(request.getRequestURI())); if (servletName != null) { pw.println("</p>Servlet=" + servletName + "<p>"); } Modified: incubator/sling/trunk/engine/src/test/java/org/apache/sling/engine/ResponseUtilTest.java URL: http://svn.apache.org/viewvc/incubator/sling/trunk/engine/src/test/java/org/apache/sling/engine/ResponseUtilTest.java?rev=731608&r1=731607&r2=731608&view=diff ============================================================================== --- incubator/sling/trunk/engine/src/test/java/org/apache/sling/engine/ResponseUtilTest.java (original) +++ incubator/sling/trunk/engine/src/test/java/org/apache/sling/engine/ResponseUtilTest.java Mon Jan 5 08:15:27 2009 @@ -16,6 +16,10 @@ */ package org.apache.sling.engine; +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; + import junit.framework.TestCase; public class ResponseUtilTest extends TestCase { @@ -31,4 +35,12 @@ assertEquals("<bonnie> & </clyde> && others", ResponseUtil.escapeXml("<bonnie> & </clyde> && others")); } + + public void testXmlEscapingWriter() throws IOException { + final StringWriter sw = new StringWriter(); + final Writer w = ResponseUtil.getXmlEscapingWriter(sw); + w.write("<bonnie> & </clyde> && others"); + w.flush(); + assertEquals("<bonnie> & </clyde> && others", sw.toString()); + } } Added: incubator/sling/trunk/launchpad/testing/src/test/java/org/apache/sling/launchpad/webapp/integrationtest/issues/SLING760Test.java URL: http://svn.apache.org/viewvc/incubator/sling/trunk/launchpad/testing/src/test/java/org/apache/sling/launchpad/webapp/integrationtest/issues/SLING760Test.java?rev=731608&view=auto ============================================================================== --- incubator/sling/trunk/launchpad/testing/src/test/java/org/apache/sling/launchpad/webapp/integrationtest/issues/SLING760Test.java (added) +++ incubator/sling/trunk/launchpad/testing/src/test/java/org/apache/sling/launchpad/webapp/integrationtest/issues/SLING760Test.java Mon Jan 5 08:15:27 2009 @@ -0,0 +1,53 @@ +/* + * 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.sling.launchpad.webapp.integrationtest.issues; + +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.httpclient.methods.GetMethod; +import org.apache.sling.commons.testing.integration.HttpTestBase; + +public class SLING760Test extends HttpTestBase { + public static final String TEST_PATH = "/" + SLING760Test.class.getSimpleName(); + + /** Verify that all instances of our error message are escaped in response, which + * is generated by the default Sling error handler */ + public void testEscapedErrorMessages() throws Exception { + final String [] mustContain = { "<characters/>", "filtered & escaped" }; + final String [] mustNotContain = { "<characters/>", "filtered & escaped" }; + + final TestNode tn = new TestNode(HTTP_BASE_URL + TEST_PATH, null); + + try { + uploadTestScript(tn.scriptPath, "issues/sling760/throw-with-markup.esp", "html.esp"); + final String content = getContent(tn.nodeUrl + ".html", CONTENT_TYPE_HTML, + null, HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + + for(String str : mustContain) { + assertTrue("Content must contain " + str + " (" + content + ")", content.contains(str)); + } + + for(String str : mustNotContain) { + assertFalse("Content must NOT contain " + str + " (" + content + ")", content.contains(str)); + } + } finally { + tn.delete(); + } + } +} Propchange: incubator/sling/trunk/launchpad/testing/src/test/java/org/apache/sling/launchpad/webapp/integrationtest/issues/SLING760Test.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: incubator/sling/trunk/launchpad/testing/src/test/java/org/apache/sling/launchpad/webapp/integrationtest/issues/SLING760Test.java ------------------------------------------------------------------------------ svn:keywords = Author Date Id Revision Rev URL Added: incubator/sling/trunk/launchpad/testing/src/test/resources/integration-test/issues/sling760/throw-with-markup.esp URL: http://svn.apache.org/viewvc/incubator/sling/trunk/launchpad/testing/src/test/resources/integration-test/issues/sling760/throw-with-markup.esp?rev=731608&view=auto ============================================================================== --- incubator/sling/trunk/launchpad/testing/src/test/resources/integration-test/issues/sling760/throw-with-markup.esp (added) +++ incubator/sling/trunk/launchpad/testing/src/test/resources/integration-test/issues/sling760/throw-with-markup.esp Mon Jan 5 08:15:27 2009 @@ -0,0 +1,5 @@ +<% +// SLING-760: test an error message with characters that must +// be escaped +throw("This string contains <characters/> that must be filtered & escaped"); +%> \ No newline at end of file Modified: incubator/sling/trunk/servlets/resolver/pom.xml URL: http://svn.apache.org/viewvc/incubator/sling/trunk/servlets/resolver/pom.xml?rev=731608&r1=731607&r2=731608&view=diff ============================================================================== --- incubator/sling/trunk/servlets/resolver/pom.xml (original) +++ incubator/sling/trunk/servlets/resolver/pom.xml Mon Jan 5 08:15:27 2009 @@ -89,7 +89,7 @@ <dependency> <groupId>org.apache.sling</groupId> <artifactId>org.apache.sling.engine</artifactId> - <version>2.0.2-incubator</version> + <version>2.0.3-incubator-SNAPSHOT</version> </dependency> <dependency> <groupId>org.apache.sling</groupId> Modified: incubator/sling/trunk/servlets/resolver/src/main/java/org/apache/sling/servlets/resolver/defaults/DefaultErrorHandlerServlet.java URL: http://svn.apache.org/viewvc/incubator/sling/trunk/servlets/resolver/src/main/java/org/apache/sling/servlets/resolver/defaults/DefaultErrorHandlerServlet.java?rev=731608&r1=731607&r2=731608&view=diff ============================================================================== --- incubator/sling/trunk/servlets/resolver/src/main/java/org/apache/sling/servlets/resolver/defaults/DefaultErrorHandlerServlet.java (original) +++ incubator/sling/trunk/servlets/resolver/src/main/java/org/apache/sling/servlets/resolver/defaults/DefaultErrorHandlerServlet.java Mon Jan 5 08:15:27 2009 @@ -30,6 +30,7 @@ import org.apache.sling.api.SlingConstants; import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.api.request.RequestProgressTracker; +import org.apache.sling.engine.ResponseUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -66,17 +67,22 @@ // write the exception message if (req.getAttribute(SlingConstants.ERROR_EXCEPTION) instanceof Throwable) { + final PrintWriter escapingWriter = new PrintWriter(ResponseUtil.getXmlEscapingWriter(pw)); Throwable throwable = (Throwable) req.getAttribute(SlingConstants.ERROR_EXCEPTION); pw.println("<h3>Exception:</h3>"); pw.println("<pre>"); - printStackTrace(pw, throwable); + pw.flush(); + printStackTrace(escapingWriter, throwable); + escapingWriter.flush(); pw.println("</pre>"); if (req instanceof SlingHttpServletRequest) { RequestProgressTracker tracker = ((SlingHttpServletRequest) req).getRequestProgressTracker(); pw.println("<h3>Request Progress:</h3>"); pw.println("<pre>"); - tracker.dump(pw); + pw.flush(); + tracker.dump(escapingWriter); + escapingWriter.flush(); pw.println("</pre>"); } } @@ -118,9 +124,11 @@ * response HTML text with the header, and an introductory phrase. */ private PrintWriter sendIntro(HttpServletResponse response, int statusCode, - String statusMessage, String requestUri, String servletName) + String statusMessageIn, String requestUri, String servletName) throws IOException { + final String statusMessage = ResponseUtil.escapeXml(statusMessageIn); + // set the status code and content type in the response final PrintWriter pw = response.getWriter(); if(!response.isCommitted()) { @@ -139,7 +147,7 @@ log.warn("Response already committed, unable to change status, output might not be well formed"); } pw.println("<h1>" + statusMessage + " (" + statusCode + ")</h1>"); - pw.print("<p>The requested URL " + requestUri + pw.print("<p>The requested URL " + ResponseUtil.escapeXml(requestUri) + " resulted in an error"); if (servletName != null) {