Chris McDonough
2011-09-08 05b8a60dc1e9bac7e8f7edd2a3f41f6bd8131307
add additional oob tests and optimize _secure_path
3 files modified
92 ■■■■ changed files
pyramid/static.py 15 ●●●● patch | view | raw | blame | history
pyramid/tests/test_integration.py 5 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_static.py 72 ●●●●● patch | view | raw | blame | history
pyramid/static.py
@@ -1,3 +1,4 @@
import os
from os.path import normcase, normpath, join, getmtime, getsize, isdir, exists
from pkg_resources import resource_exists, resource_filename, resource_isdir
import mimetypes
@@ -132,9 +133,6 @@
        path = _secure_path(path_tuple)
        if path is None:
            # belt-and-suspenders security; this should never be true
            # unless someone screws up the traversal_path code
            # (request.subpath is computed via traversal_path too)
            return HTTPNotFound('Out of bounds: %s' % request.url)
        if self.package_name: # package resource
@@ -168,13 +166,16 @@
            url = url + '?' + qs
        return HTTPMovedPermanently(url)
has_insecure_pathelement = set(['..', '.', '/', '']).intersection
has_insecure_pathelement = set(['..', '.', '']).intersection
contains_slash = set(['/', os.sep]).intersection
@lru_cache(1000)
def _secure_path(path_tuple):
    if has_insecure_pathelement(path_tuple):
        # belt-and-suspenders security; this should never be true
        # unless someone screws up the traversal_path code
        # (request.subpath is computed via traversal_path too)
        return None
    for item in path_tuple:
        if '../' in item:
            return None
    if any([contains_slash(item) for item in path_tuple]):
        return None
    return '/'.join([x.encode('utf-8') for x in path_tuple])
pyramid/tests/test_integration.py
@@ -153,14 +153,9 @@
    def test_oob_dotdotslash_encoded(self):
        self.testapp.get('/static/%2E%2E%2F/test_integration.py', status=404)
        # XXX pdb this
    def test_oob_slash(self):
        self.testapp.get('/%2F/test_integration.py', status=404)
    def test_oob_dotdotslash_encoded(self):
        self.testapp.get('/static/%2E%2E%2F/test_integration.py', status=404)
class TestStaticAppUsingAbsPath(TestStaticAppBase, unittest.TestCase):
    package = 'pyramid.tests.pkgs.static_abspath'
pyramid/tests/test_static.py
@@ -46,9 +46,43 @@
        response = inst(context, request)
        self.assertTrue('<html>static</html>' in response.body)
    def test_resource_out_of_bounds(self):
    def test_oob_singledot(self):
        inst = self._makeOne('pyramid.tests:fixtures/static')
        request = self._makeRequest({'PATH_INFO':'/./index.html'})
        context = DummyContext()
        response = inst(context, request)
        self.assertEqual(response.status, '200 OK')
        self.assertTrue('<html>static</html>' in response.body)
    def test_oob_emptyelement(self):
        inst = self._makeOne('pyramid.tests:fixtures/static')
        request = self._makeRequest({'PATH_INFO':'//index.html'})
        context = DummyContext()
        response = inst(context, request)
        self.assertEqual(response.status, '200 OK')
        self.assertTrue('<html>static</html>' in response.body)
    def test_oob_dotdotslash(self):
        inst = self._makeOne('pyramid.tests:fixtures/static')
        request = self._makeRequest({'PATH_INFO':'/subdir/../../minimal.pt'})
        context = DummyContext()
        response = inst(context, request)
        self.assertEqual(response.status, '404 Not Found')
    def test_oob_dotdotslash_encoded(self):
        inst = self._makeOne('pyramid.tests:fixtures/static')
        request = self._makeRequest(
            {'PATH_INFO':'/subdir/%2E%2E%2F%2E%2E/minimal.pt'})
        context = DummyContext()
        response = inst(context, request)
        self.assertEqual(response.status, '404 Not Found')
    def test_oob_os_sep(self):
        import os
        inst = self._makeOne('pyramid.tests:fixtures/static')
        dds = '..' + os.sep
        request = self._makeRequest({'PATH_INFO':'/subdir/%s%sminimal.pt' %
                                     (dds, dds)})
        context = DummyContext()
        response = inst(context, request)
        self.assertEqual(response.status, '404 Not Found')
@@ -168,7 +202,23 @@
        response = inst(context, request)
        self.assertTrue('<html>static</html>' in response.body)
    def test_resource_out_of_bounds(self):
    def test_oob_singledot(self):
        inst = self._makeOne('pyramid.tests:fixtures/static')
        request = self._makeRequest()
        request.subpath = ('.', 'index.html')
        context = DummyContext()
        response = inst(context, request)
        self.assertEqual(response.status, '404 Not Found')
    def test_oob_emptyelement(self):
        inst = self._makeOne('pyramid.tests:fixtures/static')
        request = self._makeRequest()
        request.subpath = ('', 'index.html')
        context = DummyContext()
        response = inst(context, request)
        self.assertEqual(response.status, '404 Not Found')
    def test_oob_dotdotslash(self):
        inst = self._makeOne('pyramid.tests:fixtures/static')
        request = self._makeRequest()
        request.subpath = ('subdir', '..', '..', 'minimal.pt')
@@ -176,6 +226,24 @@
        response = inst(context, request)
        self.assertEqual(response.status, '404 Not Found')
    def test_oob_dotdotslash_encoded(self):
        inst = self._makeOne('pyramid.tests:fixtures/static')
        request = self._makeRequest()
        request.subpath = ('subdir', '%2E%2E', '%2E%2E', 'minimal.pt')
        context = DummyContext()
        response = inst(context, request)
        self.assertEqual(response.status, '404 Not Found')
    def test_oob_os_sep(self):
        import os
        inst = self._makeOne('pyramid.tests:fixtures/static')
        dds = '..' + os.sep
        request = self._makeRequest()
        request.subpath = ('subdir', dds, dds, 'minimal.pt')
        context = DummyContext()
        response = inst(context, request)
        self.assertEqual(response.status, '404 Not Found')
    def test_resource_doesnt_exist(self):
        inst = self._makeOne('pyramid.tests:fixtures/static')
        request = self._makeRequest()