diff --git a/tornado/test/web_test.py b/tornado/test/web_test.py index 5938b7b6..829dfe48 100644 --- a/tornado/test/web_test.py +++ b/tornado/test/web_test.py @@ -967,7 +967,8 @@ class StaticFileTest(WebTestCase): return [('/static_url/(.*)', StaticUrlHandler), ('/abs_static_url/(.*)', AbsoluteStaticUrlHandler), - ('/override_static_url/(.*)', OverrideStaticUrlHandler)] + ('/override_static_url/(.*)', OverrideStaticUrlHandler), + ('/root_static/(.*)', StaticFileHandler, dict(path='/'))] def get_app_kwargs(self): return dict(static_path=relpath('static')) @@ -1203,6 +1204,15 @@ class StaticFileTest(WebTestCase): # is probably a packaging error). self.assertEqual(response.code, 403) + def test_root_static_path(self): + # Sometimes people set the StaticFileHandler's path to '/' + # to disable Tornado's path validation (in conjunction with + # their own validation in get_absolute_path). Make sure + # that the stricter validation in 4.2.1 doesn't break them. + path = os.path.join(os.path.dirname(__file__), 'static/robots.txt') + response = self.get_and_head('/root_static' + path) + self.assertEqual(response.code, 200) + @wsgi_safe class StaticDefaultFilenameTest(WebTestCase): diff --git a/tornado/web.py b/tornado/web.py index afa9ca58..dd0df5ef 100644 --- a/tornado/web.py +++ b/tornado/web.py @@ -2404,7 +2404,14 @@ class StaticFileHandler(RequestHandler): # We must add it back to `root` so that we only match files # in a directory named `root` instead of files starting with # that prefix. - root = os.path.abspath(root) + os.path.sep + root = os.path.abspath(root) + if not root.endswith(os.path.sep): + # abspath always removes a trailing slash, except when + # root is '/'. This is an unusual case, but several projects + # have independently discovered this technique to disable + # Tornado's path validation and (hopefully) do their own, + # so we need to support it. + root += os.path.sep # The trailing slash also needs to be temporarily added back # the requested path so a request to root/ will match. if not (absolute_path + os.path.sep).startswith(root):