diff --git a/flask/helpers.py b/flask/helpers.py index dcf25baf..3387f623 100644 --- a/flask/helpers.py +++ b/flask/helpers.py @@ -29,6 +29,7 @@ except ImportError: json_available = False from werkzeug import Headers, wrap_file, is_resource_modified, cached_property +from werkzeug.exceptions import NotFound from jinja2 import FileSystemLoader @@ -334,7 +335,7 @@ class _PackageBoundObject(object): .. versionadded:: 0.5 """ filename = posixpath.normpath(filename) - if filename.startswith('../'): + if filename.startswith(('/', '../')): raise NotFound() filename = os.path.join(self.root_path, 'static', filename) if not os.path.isfile(filename): diff --git a/tests/flask_tests.py b/tests/flask_tests.py index 384e2e91..b3835302 100644 --- a/tests/flask_tests.py +++ b/tests/flask_tests.py @@ -20,6 +20,7 @@ from logging import StreamHandler from contextlib import contextmanager from datetime import datetime from werkzeug import parse_date, parse_options_header +from werkzeug.exceptions import NotFound from cStringIO import StringIO example_path = os.path.join(os.path.dirname(__file__), '..', 'examples') @@ -645,6 +646,25 @@ class ModuleTestCase(unittest.TestCase): assert flask.url_for('admin.static', filename='test.txt') \ == '/admin/static/test.txt' + def test_safe_access(self): + from moduleapp import app + + with app.test_request_context(): + f = app.view_functions['admin.static'] + + try: + rv = f('/etc/passwd') + except NotFound: + pass + else: + assert 0, 'expected exception' + try: + rv = f('../__init__.py') + except NotFound: + pass + else: + assert 0, 'expected exception' + class SendfileTestCase(unittest.TestCase):