Previously StaticFileHandler would allow access to files whose name
starts with the static root directory, not just those that are actually
in the directory.
The bug was introduced in Tornado 3.1 via commits
7b03cd62fb and
60952528.
include tornado/test/options_test.cfg
include tornado/test/static/robots.txt
include tornado/test/static/dir/index.html
+include tornado/test/static_foo.txt
include tornado/test/templates/utf8.html
include tornado/test/test.crt
include tornado/test/test.key
"options_test.cfg",
"static/robots.txt",
"static/dir/index.html",
+ "static_foo.txt",
"templates/utf8.html",
"test.crt",
"test.key",
--- /dev/null
+This file should not be served by StaticFileHandler even though
+its name starts with "static".
response = self.get_and_head('/static/blarg')
self.assertEqual(response.code, 404)
+ def test_path_traversal_protection(self):
+ with ExpectLog(gen_log, ".*not in root static directory"):
+ response = self.get_and_head('/static/../static_foo.txt')
+ # Attempted path traversal should result in 403, not 200
+ # (which means the check failed and the file was served)
+ # or 404 (which means that the file didn't exist and
+ # is probably a packaging error).
+ self.assertEqual(response.code, 403)
+
@wsgi_safe
class StaticDefaultFilenameTest(WebTestCase):
.. versionadded:: 3.1
"""
- root = os.path.abspath(root)
- # os.path.abspath strips a trailing /
- # it needs to be temporarily added back for requests to root/
+ # os.path.abspath strips a trailing /.
+ # 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
+ # 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):
raise HTTPError(403, "%s is not in root static directory",
self.path)