Allow serving static files in subdirectories
Change-Id: I82447dd9ce47ffc44727d9f886a45a9fa1a2463e
Signed-Off-By: James Y Knight <jyknight@google.com>
(cherry picked from commit b8ddd05fbf
)
This commit is contained in:

committed by
Shawn O. Pearce

parent
183d0f865a
commit
7bb1f97b98
@@ -89,34 +89,53 @@ public class StaticServlet extends HttpServlet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final File staticBase;
|
private final File staticBase;
|
||||||
|
private final String staticBasePath;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
StaticServlet(final SitePaths site) {
|
StaticServlet(final SitePaths site) {
|
||||||
staticBase = site.static_dir;
|
File f;
|
||||||
|
try {
|
||||||
|
f = site.static_dir.getCanonicalFile();
|
||||||
|
} catch (IOException e) {
|
||||||
|
f = site.static_dir.getAbsoluteFile();
|
||||||
|
}
|
||||||
|
staticBase = f;
|
||||||
|
staticBasePath = staticBase.getPath() + File.separator;
|
||||||
}
|
}
|
||||||
|
|
||||||
private File local(final HttpServletRequest req) {
|
private File local(final HttpServletRequest req) {
|
||||||
final String name = req.getPathInfo();
|
final String name = req.getPathInfo();
|
||||||
if (name.length() < 2 || !name.startsWith("/")) {
|
if (name.length() < 2 || !name.startsWith("/") || isUnreasonableName(name)) {
|
||||||
// Too short to be a valid file name, or doesn't start with
|
// Too short to be a valid file name, or doesn't start with
|
||||||
// the path info separator like we expected.
|
// the path info separator like we expected.
|
||||||
//
|
//
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name.indexOf('/', 1) > 0 || name.indexOf('\\', 1) > 0) {
|
final File p = new File(staticBase, name.substring(1));
|
||||||
// Contains a path separator. Don't serve it as the client
|
|
||||||
// might be trying something evil like "/../../etc/passwd".
|
// Ensure that the requested file is *actually* within the static dir base.
|
||||||
// This static servlet is just meant to facilitate simple
|
try {
|
||||||
// assets like banner images.
|
if (!p.getCanonicalFile().getPath().startsWith(staticBasePath))
|
||||||
//
|
return null;
|
||||||
|
} catch (IOException e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final File p = new File(staticBase, name.substring(1));
|
|
||||||
return p.isFile() ? p : null;
|
return p.isFile() ? p : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean isUnreasonableName(String name) {
|
||||||
|
if (name.charAt(name.length() -1) == '/') return true; // no suffix
|
||||||
|
if (name.indexOf('\\') >= 0) return true; // no windows/dos stlye paths
|
||||||
|
if (name.startsWith("../")) return true; // no "../etc/passwd"
|
||||||
|
if (name.contains("/../")) return true; // no "foo/../etc/passwd"
|
||||||
|
if (name.contains("/./")) return true; // "foo/./foo" is insane to ask
|
||||||
|
if (name.contains("//")) return true; // windows UNC path can be "//..."
|
||||||
|
|
||||||
|
return false; // is a reasonable name
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected long getLastModified(final HttpServletRequest req) {
|
protected long getLastModified(final HttpServletRequest req) {
|
||||||
final File p = local(req);
|
final File p = local(req);
|
||||||
|
Reference in New Issue
Block a user