From 456a00161b47b8a3a13fc253c3a94ccacd69ea3b Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Thu, 30 Apr 2009 03:23:13 +0000 Subject: [PATCH] JETTY-980 & JETTY-1004 git-svn-id: svn+ssh://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/trunk@199 7e9141cc-0065-0410-87d8-b60c137991c4 --- VERSION.txt | 4 +- .../eclipse/jetty/embedded/FileServer.java | 4 +- .../org/eclipse/jetty/server/Response.java | 26 +- .../jetty/server/handler/ResourceHandler.java | 36 +++ .../eclipse/jetty/servlet/DefaultServlet.java | 11 +- .../jetty/servlet/DefaultServletTest.java | 269 +++++++++++++++++- .../java/org/eclipse/jetty/util/URIUtil.java | 6 +- .../jetty/util/resource/FileResource.java | 4 +- .../eclipse/jetty/util/resource/Resource.java | 88 +++++- 9 files changed, 414 insertions(+), 34 deletions(-) diff --git a/VERSION.txt b/VERSION.txt index f2a6ee4bbcb..cfe43c83e32 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1,9 +1,11 @@ jetty-7.0.0.M2-SNAPSHOT + JETTY-941 Linux chkconfig hint - + JETTY-959 CGI servlet doesn't kill the CGI in case the client disconnects + + JETTY-959 CGI servlet doesn't kill the CGI in case the client disconnects + + JETTY-980 Fixed ResourceHandler ? handling, and bad URI creation in listings + JETTY-996 Make start-stop-daemon optional + 273767 Update to use geronimo annotations spec 1.1.1 + JETTY-1003 java.lang.IllegalArgumentException: timeout can't be negative + + JETTY-1004 Canonical path handling includes ? in path segment jetty-7.0.0.M1 22 April 2009 + 271258 FORM Authentication dispatch handling avoids caching diff --git a/jetty-embedded-examples/src/main/java/org/eclipse/jetty/embedded/FileServer.java b/jetty-embedded-examples/src/main/java/org/eclipse/jetty/embedded/FileServer.java index 7fc8366ffb3..bfd7ed8d7c8 100644 --- a/jetty-embedded-examples/src/main/java/org/eclipse/jetty/embedded/FileServer.java +++ b/jetty-embedded-examples/src/main/java/org/eclipse/jetty/embedded/FileServer.java @@ -23,6 +23,7 @@ import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.DefaultHandler; import org.eclipse.jetty.server.handler.HandlerList; import org.eclipse.jetty.server.handler.ResourceHandler; +import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.resource.Resource; /* ------------------------------------------------------------ */ @@ -47,9 +48,10 @@ public class FileServer response.getWriter().println(listing); } }; + resource_handler.setWelcomeFiles(new String[]{"index.html"}); resource_handler.setResourceBase(args.length==2?args[1]:"."); - + Log.info("serving "+resource_handler.getBaseResource()); HandlerList handlers = new HandlerList(); handlers.setHandlers(new Handler[]{resource_handler,new DefaultHandler()}); server.setHandler(handlers); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java index 4abaf57fd9d..3dbb4546bf6 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java @@ -32,6 +32,7 @@ import org.eclipse.jetty.http.HttpGenerator; import org.eclipse.jetty.http.HttpHeaderValues; import org.eclipse.jetty.http.HttpHeaders; import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.http.HttpURI; import org.eclipse.jetty.http.HttpVersions; import org.eclipse.jetty.http.MimeTypes; import org.eclipse.jetty.io.BufferCache.CachedBuffer; @@ -419,12 +420,12 @@ public class Response implements HttpServletResponse { StringBuilder buf = _connection.getRequest().getRootURL(); if (location.startsWith("/")) - buf.append(URIUtil.canonicalPath(location)); + buf.append(location); else { String path=_connection.getRequest().getRequestURI(); String parent=(path.endsWith("/"))?path:URIUtil.parentPath(path); - location=URIUtil.canonicalPath(URIUtil.addPaths(parent,location)); + location=URIUtil.addPaths(parent,location); if(location==null) throw new IllegalStateException("path cannot be above root"); if (!location.startsWith("/")) @@ -433,6 +434,27 @@ public class Response implements HttpServletResponse } location=buf.toString(); + HttpURI uri = new HttpURI(location); + String path=uri.getDecodedPath(); + String canonical=URIUtil.canonicalPath(path); + if (canonical==null) + throw new IllegalArgumentException(); + if (!canonical.equals(path)) + { + buf = _connection.getRequest().getRootURL(); + buf.append(canonical); + if (uri.getQuery()!=null) + { + buf.append('?'); + buf.append(uri.getQuery()); + } + if (uri.getFragment()!=null) + { + buf.append('#'); + buf.append(uri.getFragment()); + } + location=buf.toString(); + } } resetBuffer(); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java index ef63a7998f9..8ee9f9957ed 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java @@ -36,6 +36,7 @@ import org.eclipse.jetty.server.handler.ContextHandler.Context; import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.resource.FileResource; import org.eclipse.jetty.util.resource.Resource; @@ -56,6 +57,7 @@ public class ResourceHandler extends AbstractHandler String[] _welcomeFiles={"index.html"}; MimeTypes _mimeTypes = new MimeTypes(); ByteArrayBuffer _cacheControl; + boolean _aliases; /* ------------------------------------------------------------ */ public ResourceHandler() @@ -74,12 +76,41 @@ public class ResourceHandler extends AbstractHandler _mimeTypes = mimeTypes; } + /* ------------------------------------------------------------ */ + /** + * @return True if resource aliases are allowed. + */ + public boolean isAliases() + { + return _aliases; + } + + /* ------------------------------------------------------------ */ + /** + * Set if resource aliases (eg symlink, 8.3 names, case insensitivity) are allowed. + * Allowing aliases can significantly increase security vulnerabilities. + * If this handler is deployed inside a ContextHandler, then the + * {@link ContextHandler#isAliases()} takes precedent. + * @param aliases True if aliases are supported. + */ + public void setAliases(boolean aliases) + { + _aliases = aliases; + } + /* ------------------------------------------------------------ */ public void doStart() throws Exception { Context scontext = ContextHandler.getCurrentContext(); _context = (scontext==null?null:scontext.getContextHandler()); + + if (_context!=null) + _aliases=_context.isAliases(); + + if (!_aliases && !FileResource.getCheckAliases()) + throw new IllegalStateException("Alias checking disabled"); + super.doStart(); } @@ -239,6 +270,11 @@ public class ResourceHandler extends AbstractHandler if (resource==null || !resource.exists()) return; + if (!_aliases && resource.getAlias()!=null) + { + Log.info(resource+" aliased to "+resource.getAlias()); + return; + } // We are going to server something base_request.setHandled(true); diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/DefaultServlet.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/DefaultServlet.java index 2a8190af660..532019b6c81 100644 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/DefaultServlet.java +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/DefaultServlet.java @@ -52,6 +52,7 @@ import org.eclipse.jetty.util.MultiPartOutputStream; import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.resource.FileResource; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.ResourceFactory; @@ -154,9 +155,13 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory _redirectWelcome=getInitBoolean("redirectWelcome",_redirectWelcome); _gzip=getInitBoolean("gzip",_gzip); - String aliases=_servletContext.getInitParameter("aliases"); - if (aliases!=null) - _contextHandler.setAliases(Boolean.parseBoolean(aliases)); + if (getInitParameter("aliases")!=null) + _contextHandler.setAliases(getInitBoolean("aliases",false)); + boolean aliases=_contextHandler.isAliases(); + if (!aliases && !FileResource.getCheckAliases()) + throw new IllegalStateException("Alias checking disabled"); + if (aliases) + _servletContext.log("Aliases are enabled"); _useFileMappedBuffer=getInitBoolean("useFileMappedBuffer",_useFileMappedBuffer); diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DefaultServletTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DefaultServletTest.java index 50c3510bc78..7e4c766ce29 100644 --- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DefaultServletTest.java +++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DefaultServletTest.java @@ -1,12 +1,16 @@ package org.eclipse.jetty.servlet; import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; import junit.framework.AssertionFailedError; import junit.framework.TestCase; import org.eclipse.jetty.server.LocalConnector; import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.IO; public class DefaultServletTest extends TestCase { @@ -42,37 +46,278 @@ public class DefaultServletTest extends TestCase server.stop(); } } - - public void testListingXSS() throws Exception + + public void testListingWithSession() throws Exception { - ServletHolder defholder = context.addServlet(DefaultServlet.class,"/listing/*"); + ServletHolder defholder = context.addServlet(DefaultServlet.class,"/*"); defholder.setInitParameter("dirAllowed","true"); defholder.setInitParameter("redirectWelcome","false"); defholder.setInitParameter("gzip","false"); - File resBase = new File("src/test/resources"); - - assertTrue("resBase.exists",resBase.exists()); - assertTrue("resBase.isDirectory",resBase.isDirectory()); + File testDir = new File("target/tests/" + getName()); + prepareEmptyTestDir(testDir); + + /* create some content in the docroot */ + File resBase = new File(testDir, "docroot"); + resBase.mkdirs(); + new File(resBase, "one").mkdir(); + new File(resBase, "two").mkdir(); + new File(resBase, "three").mkdir(); String resBasePath = resBase.getAbsolutePath(); defholder.setInitParameter("resourceBase",resBasePath); StringBuffer req1 = new StringBuffer(); - req1.append("GET /context/listing/; HTTP/1.1\n"); + req1.append("GET /context/;JSESSIONID=1234567890 HTTP/1.1\n"); req1.append("Host: localhost\n"); - req1.append("Connection: close\n"); req1.append("\n"); String response = connector.getResponses(req1.toString()); - assertResponseContains("listing/one/",response); - assertResponseContains("listing/two/",response); - assertResponseContains("listing/three/",response); + assertResponseContains("/one/;JSESSIONID=1234567890",response); + assertResponseContains("/two/;JSESSIONID=1234567890",response); + assertResponseContains("/three/;JSESSIONID=1234567890",response); assertResponseNotContains(" HTTP/1.1\n"); + req1.append("Host: localhost\n"); + req1.append("\n"); + + String response = connector.getResponses(req1.toString()); + + assertResponseContains("/one/",response); + assertResponseContains("/two/",response); + assertResponseContains("/three/",response); + assertResponseContains("/f%3F%3Fr",response); + + assertResponseNotContains("">Link + * + * The above example would parse incorrectly on various browsers as the "<" or '"' characters + * would end the href attribute value string prematurely. + * + * @param raw the raw text to defang. + * @return the defanged text. + */ + private static String defangURI(String raw) + { + StringBuffer buf = null; + + if (buf==null) + { + for (int i=0;i': + buf=new StringBuffer(raw.length()<<1); + break; + } + } + if (buf==null) + return raw; + } + + for (int i=0;i': + buf.append("%3E"); + continue; + default: + buf.append(c); + continue; + } + } + + return buf.toString(); + } + + private static String deTag(String raw) + { return StringUtil.replace( StringUtil.replace(raw,"<","<"), ">", ">"); }