Merge remote-tracking branch 'origin/jetty-8'

Conflicts:
	jetty-start/src/main/resources/org/eclipse/jetty/start/start.config
	jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartInputStream.java
	test-jetty-webapp/pom.xml
	tests/test-integration/pom.xml
This commit is contained in:
Jan Bartel 2013-01-07 12:47:32 +11:00
commit 8d0e776b2a
10 changed files with 452 additions and 10 deletions

View File

@ -24,6 +24,8 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/**
* This rule can be used to protect against invalid unicode characters in a url making it into applications.
@ -36,6 +38,8 @@ import org.eclipse.jetty.util.URIUtil;
*/
public class ValidUrlRule extends Rule
{
private static final Logger LOG = Log.getLogger(ValidUrlRule.class);
String _code = "400";
String _reason = "Illegal Url";
@ -72,12 +76,16 @@ public class ValidUrlRule extends Rule
public String matchAndApply(String target, HttpServletRequest request, HttpServletResponse response) throws IOException
{
// best to decide the request uri and validate that
// String uri = request.getRequestURI();
String uri = URIUtil.decodePath(request.getRequestURI());
for (int i = 0; i < uri.length(); ++i)
for (int i = 0; i < uri.length();)
{
if (!isValidChar(uri.charAt(i)))
int codepoint = uri.codePointAt(i);
if (!isValidChar(uri.codePointAt(i)))
{
int code = Integer.parseInt(_code);
// status code 400 and up are error codes so include a reason
@ -93,17 +101,20 @@ public class ValidUrlRule extends Rule
// we have matched, return target and consider it is handled
return target;
}
i += Character.charCount(codepoint);
}
// we have not matched so return null
return null;
}
protected boolean isValidChar(char c)
protected boolean isValidChar(int codepoint)
{
Character.UnicodeBlock block = Character.UnicodeBlock.of(c);
Character.UnicodeBlock block = Character.UnicodeBlock.of(codepoint);
return (!Character.isISOControl(c)) && block != null && block != Character.UnicodeBlock.SPECIALS;
LOG.debug("{} {} {} {}", Character.charCount(codepoint), codepoint, block, Character.isISOControl(codepoint));
return (!Character.isISOControl(codepoint)) && block != null && block != Character.UnicodeBlock.SPECIALS;
}
public String toString()

View File

@ -22,8 +22,10 @@ import static org.junit.Assert.assertEquals;
import junit.framework.Assert;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
@SuppressWarnings("unused")
public class ValidUrlRuleTest extends AbstractRuleTestCase
{
private ValidUrlRule _rule;
@ -70,6 +72,46 @@ public class ValidUrlRuleTest extends AbstractRuleTestCase
assertEquals("foo",_response.getReason());
}
@Test
public void testInvalidJsp() throws Exception
{
_rule.setCode("405");
_rule.setReason("foo");
_request.setRequestURI("/jsp/bean1.jsp%00");
String result = _rule.matchAndApply(_request.getRequestURI(), _request, _response);
assertEquals(405,_response.getStatus());
assertEquals("foo",_response.getReason());
}
@Ignore("Not working in jetty-9")
@Test
public void testInvalidShamrock() throws Exception
{
_rule.setCode("405");
_rule.setReason("foo");
_request.setRequestURI("/jsp/shamrock-%00%E2%98%98.jsp");
String result = _rule.matchAndApply(_request.getRequestURI(), _request, _response);
assertEquals(405,_response.getStatus());
assertEquals("foo",_response.getReason());
}
@Ignore("Not working in jetty-9")
@Test
public void testValidShamrock() throws Exception
{
_rule.setCode("405");
_rule.setReason("foo");
_request.setRequestURI("/jsp/shamrock-%E2%98%98.jsp");
String result = _rule.matchAndApply(_request.getRequestURI(), _request, _response);
assertEquals(200,_response.getStatus());
}
@Test
public void testCharacters() throws Exception
{

View File

@ -128,8 +128,8 @@ $(jetty.home)/lib/jndi/** exists $(jetty.home)/lib/jndi
$(jetty.home)/lib/jetty-jaas-${version}.jar ! available org.eclipse.jetty.jaas.JAASLoginService
[All,annotations]
$(jetty.home)/lib/jetty-annotations-$(version).jar ! available org.eclipse.jetty.annotations.AnnotationFinder
$(jetty.home)/lib/annotations/** exists $(jetty.home)/lib/jndi
$(jetty.home)/lib/jetty-annotations-$(version).jar ! available org.eclipse.jetty.annotations.AnnotationParser
$(jetty.home)/lib/annotations/** exists $(jetty.home)/lib/annotations
[All,setuid]
$(jetty.home)/lib/jetty-setuid-$(version).jar ! available org.eclipse.jetty.setuid.SetUID

View File

@ -476,7 +476,7 @@ public class MultiPartInputStreamParser
// Get first boundary
String line=((ReadLineInputStream)_in).readLine();
if (line == null || line.length() == 0)
if (line == null)
throw new IOException("Missing content for multipart request");
boolean badFormatLogged = false;
@ -492,7 +492,7 @@ public class MultiPartInputStreamParser
line=(line==null?line:line.trim());
}
if (line == null || line.length() == 0)
if (line == null)
throw new IOException("Missing initial multi part boundary");
// Read each part

View File

@ -89,6 +89,10 @@ public class FileResource extends URLResource
// Try standard API to convert URL to file.
_file =new File(new URI(url.toString()));
}
catch (URISyntaxException e)
{
throw e;
}
catch (Exception e)
{
LOG.ignore(e);

View File

@ -0,0 +1,98 @@
//
// ========================================================================
// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.util.resource;
import static org.hamcrest.Matchers.*;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import org.eclipse.jetty.toolchain.test.TestingDir;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.UrlEncoded;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
public class FileResourceTest
{
@Rule
public TestingDir testdir = new TestingDir();
private URI createDummyFile(String name) throws IOException
{
File file = testdir.getFile(name);
file.createNewFile();
return file.toURI();
}
private URL decode(URL url) throws MalformedURLException
{
String raw = url.toExternalForm();
String decoded = UrlEncoded.decodeString(raw,0,raw.length(),StringUtil.__UTF8_CHARSET);
return new URL(decoded);
}
@Test
public void testExist_Normal() throws Exception
{
createDummyFile("a.jsp");
URI ref = testdir.getDir().toURI().resolve("a.jsp");
FileResource fileres = new FileResource(decode(ref.toURL()));
Assert.assertThat("FileResource: " + fileres,fileres.exists(),is(true));
}
@Ignore("Cannot get null to be seen by FileResource")
@Test
public void testExist_BadNull() throws Exception
{
createDummyFile("a.jsp");
try {
// request with null at end
URI ref = testdir.getDir().toURI().resolve("a.jsp%00");
FileResource fileres = new FileResource(decode(ref.toURL()));
Assert.assertThat("FileResource: " + fileres,fileres.exists(),is(false));
} catch(URISyntaxException e) {
// Valid path
}
}
@Ignore("Validation shouldn't be done in FileResource")
@Test
public void testExist_BadNullX() throws Exception
{
createDummyFile("a.jsp");
try {
// request with null and x at end
URI ref = testdir.getDir().toURI().resolve("a.jsp%00x");
FileResource fileres = new FileResource(decode(ref.toURL()));
Assert.assertThat("FileResource: " + fileres,fileres.exists(),is(false));
} catch(URISyntaxException e) {
// Valid path
}
}
}

View File

@ -77,6 +77,13 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<forkMode>always</forkMode>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
@ -100,11 +107,26 @@
<artifactId>jetty-client</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-helper</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-jsp</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.tests</groupId>
<artifactId>test-webapp-rfc2616</artifactId>

View File

@ -0,0 +1,221 @@
//
// ========================================================================
// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.test.jsp;
import static org.hamcrest.Matchers.*;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URI;
import org.apache.jasper.servlet.JspServlet;
import org.eclipse.jetty.security.HashLoginService;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.util.IO;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
public class JspMatchingTest
{
private static Server server;
private static URI serverURI;
@BeforeClass
public static void startServer() throws Exception
{
server = new Server();
SelectChannelConnector connector = new SelectChannelConnector();
connector.setPort(0);
server.addConnector(connector);
// Configure LoginService
HashLoginService login = new HashLoginService();
login.setName("Test Realm");
File realmFile = MavenTestingUtils.getTestResourceFile("realm.properties");
login.setConfig(realmFile.getAbsolutePath());
server.addBean(login);
// Configure WebApp
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");
File webappBase = MavenTestingUtils.getTestResourceDir("docroots/jsp");
context.setResourceBase(webappBase.getAbsolutePath());
context.setClassLoader(Thread.currentThread().getContextClassLoader());
// add default servlet
ServletHolder defaultServHolder = context.addServlet(DefaultServlet.class,"/");
defaultServHolder.setInitParameter("aliases","true"); // important!
// add jsp
ServletHolder jsp = context.addServlet(JspServlet.class,"*.jsp");
jsp.setInitParameter("classpath",context.getClassPath());
// add context
server.setHandler(context);
server.start();
serverURI = new URI("http://localhost:" + connector.getLocalPort() + "/");
}
@AfterClass
public static void stopServer() throws Exception
{
server.stop();
}
@Test
public void testGetBeanRef() throws Exception
{
URI uri = serverURI.resolve("/dump.jsp");
HttpURLConnection conn = null;
try
{
conn = (HttpURLConnection)uri.toURL().openConnection();
conn.setConnectTimeout(1000);
conn.setReadTimeout(1000);
Assert.assertThat(conn.getResponseCode(),is(200));
// make sure that jsp actually ran, and didn't just get passed onto
// the default servlet to return the jsp source
String body = getResponseBody(conn);
Assert.assertThat("Body",body,not(containsString("<%@")));
Assert.assertThat("Body",body,not(containsString("<jsp:")));
}
finally
{
close(conn);
}
}
@Test
public void testGetBeanRefInvalid_null() throws Exception
{
URI uri = serverURI.resolve("/dump.jsp%00");
HttpURLConnection conn = null;
try
{
conn = (HttpURLConnection)uri.toURL().openConnection();
conn.setConnectTimeout(1000);
conn.setReadTimeout(1000);
Assert.assertThat("Response Code",conn.getResponseCode(),is(404));
}
finally
{
close(conn);
}
}
@Ignore("DefaultServlet + aliasing breaks this test ATM")
@Test
public void testGetBeanRefInvalid_nullx() throws Exception
{
URI uri = serverURI.resolve("/dump.jsp%00x");
HttpURLConnection conn = null;
try
{
conn = (HttpURLConnection)uri.toURL().openConnection();
conn.setConnectTimeout(1000);
conn.setReadTimeout(1000);
Assert.assertThat("Response Code",conn.getResponseCode(),is(404));
}
finally
{
close(conn);
}
}
@Ignore("DefaultServlet + aliasing breaks this test ATM")
@Test
public void testGetBeanRefInvalid_nullslash() throws Exception
{
URI uri = serverURI.resolve("/dump.jsp%00/");
HttpURLConnection conn = null;
try
{
conn = (HttpURLConnection)uri.toURL().openConnection();
conn.setConnectTimeout(1000);
conn.setReadTimeout(1000);
Assert.assertThat("Response Code",conn.getResponseCode(),is(404));
}
finally
{
close(conn);
}
}
@Ignore("DefaultServlet + aliasing breaks this test ATM")
@Test
public void testGetBeanRefInvalid_nullxslash() throws Exception
{
URI uri = serverURI.resolve("/dump.jsp%00x/");
HttpURLConnection conn = null;
try
{
conn = (HttpURLConnection)uri.toURL().openConnection();
conn.setConnectTimeout(1000);
conn.setReadTimeout(1000);
Assert.assertThat("Response Code",conn.getResponseCode(),is(404));
}
finally
{
close(conn);
}
}
protected String getResponseBody(HttpURLConnection conn) throws IOException
{
InputStream in = null;
try
{
in = conn.getInputStream();
return IO.toString(in);
}
finally
{
IO.close(in);
}
}
private void close(HttpURLConnection conn)
{
conn.disconnect();
}
}

View File

@ -0,0 +1,23 @@
<html><head>
<%@ page import="java.util.Enumeration" %>
</head><body>
<h1>JSP Dump</h1>
<table border="1">
<tr><th>Request URI:</th><td><%= request.getRequestURI() %></td></tr>
<tr><th>ServletPath:</th><td><%= request.getServletPath() %></td></tr>
<tr><th>PathInfo:</th><td><%= request.getPathInfo() %></td></tr>
<%
Enumeration e =request.getParameterNames();
while(e.hasMoreElements())
{
String name = (String)e.nextElement();
%>
<tr>
<th>getParameter("<%= name %>")</th>
<td><%= request.getParameter(name) %></td></tr>
<% } %>
</table>
</body></html>

View File

@ -0,0 +1,21 @@
#
# This file defines users passwords and roles for a HashUserRealm
#
# The format is
# <username>: <password>[,<rolename> ...]
#
# Passwords may be clear text, obfuscated or checksummed. The class
# org.eclipse.util.Password should be used to generate obfuscated
# passwords or password checksums
#
# If DIGEST Authentication is used, the password must be in a recoverable
# format, either plain text or OBF:.
#
jetty: MD5:164c88b302622e17050af52c89945d44,user
admin: CRYPT:adpexzg3FUZAk,server-administrator,content-administrator,admin
other: OBF:1xmk1w261u9r1w1c1xmq,user
plain: plain,user
user: password,user
# This entry is for digest auth. The credential is a MD5 hash of username:realmname:password
digest: MD5:6e120743ad67abfbc385bc2bb754e297,user