From 86986b5ae1f63f964388bbba176a52b78bc48a04 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Sun, 29 Jul 2012 16:21:45 -0500 Subject: [PATCH] improved router, thanks Jonathan --- VERSION | 2 +- gluon/rewrite.py | 7 +++--- gluon/tests/test_router.py | 44 ++++++++++++++++++++++++++++++++++++++ gluon/tests/test_routes.py | 1 + router.example.py | 10 +++++---- 5 files changed, 56 insertions(+), 8 deletions(-) diff --git a/VERSION b/VERSION index b4485c6d..9f29813a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-07-29 13:56:56) dev +Version 2.00.0 (2012-07-29 16:21:39) dev diff --git a/gluon/rewrite.py b/gluon/rewrite.py index 72454520..25b2250a 100644 --- a/gluon/rewrite.py +++ b/gluon/rewrite.py @@ -46,9 +46,9 @@ def _router_default(): domains = None, exclusive_domain = False, map_hyphen = False, - acfe_match = r'\w+$', # legal app/ctlr/fcn/ext - file_match = r'([-+=@$%\w]+[./]?)+$', # legal static file (path) name - args_match = r'([\w@ -]+[=.]?)*$', # legal arg in args + acfe_match = r'\w+$', # legal app/ctlr/fcn/ext + file_match = r'([-+=@$%\w]+[./]?)+$', # legal static subpath + args_match = r'([\w@ -]+[=.]?)*$', # legal arg in args ) return router @@ -966,6 +966,7 @@ class MapUrlIn(object): for name in self.args: bad_static = bad_static or name in ('', '.', '..') or not self.router._file_match.match(name) if bad_static: + log_rewrite('bad static path=%s' % file) raise HTTP(400, thread.routes.error_message % 'invalid request', web2py_error='invalid static file') # diff --git a/gluon/tests/test_router.py b/gluon/tests/test_router.py index 50b2e2c1..d3822919 100644 --- a/gluon/tests/test_router.py +++ b/gluon/tests/test_router.py @@ -13,6 +13,7 @@ if os.path.isdir('gluon'): sys.path.append(os.path.realpath('gluon')) # running from web2py base else: sys.path.append(os.path.realpath('../')) # running from gluon/tests/ + os.environ['web2py_path'] = os.path.realpath('../../') # for settings from rewrite import load, filter_url, filter_err, get_effective_router, map_url_out from html import URL @@ -794,6 +795,49 @@ class TestRouter(unittest.TestCase): self.assertEqual(filter_err(399), 399) self.assertEqual(filter_err(400), 400) + def test_router_static_path(self): + ''' + Test validation of static paths + Stock pattern: file_match = r'([-+=@$%\w]+[./]?)+$' + + ''' + load(rdict=dict()) + self.assertEqual(filter_url('http://domain.com/welcome/static/path/to/static'), "%s/applications/welcome/static/path/to/static" % root) + self.assertRaises(HTTP, filter_url, 'http://domain.com/welcome/static/bad/path/to/st~tic') + self.assertEqual(filter_url('http://domain.com/welcome/static/path/to--/static'), "%s/applications/welcome/static/path/to--/static" % root) + self.assertEqual(filter_url('http://domain.com/welcome/static/path/==to--/static'), "%s/applications/welcome/static/path/==to--/static" % root) + self.assertEqual(filter_url('http://domain.com/welcome/static/path/-+=@$%/static'), "%s/applications/welcome/static/path/-+=@$%%/static" % root) + self.assertRaises(HTTP, filter_url, 'http://domain.com/welcome/static/bad/path/to/.static') + self.assertRaises(HTTP, filter_url, 'http://domain.com/welcome/static/bad/path/to/s..tatic') + self.assertRaises(HTTP, filter_url, 'http://domain.com/welcome/static/bad/path/to//static') + self.assertRaises(HTTP, filter_url, 'http://domain.com/welcome/static/bad/path/to/#static') + + router_static = dict( + BASE = dict( + file_match = r'([-+=@$%#\w]+[./]?)+$', # legal static path + ), + ) + load(rdict=router_static) + self.assertEqual(filter_url('http://domain.com/welcome/static/path/to/#static'), "%s/applications/welcome/static/path/to/#static" % root) + + router_static = dict( + BASE = dict( + file_match = r'[-+=@$%#.\w]+$', # legal static path element + ), + ) + load(rdict=router_static) + self.assertEqual(filter_url('http://domain.com/welcome/static/path/to/static'), "%s/applications/welcome/static/path/to/static" % root) + self.assertRaises(HTTP, filter_url, 'http://domain.com/welcome/static/bad/path/to/st~tic') + self.assertEqual(filter_url('http://domain.com/welcome/static/path/to--/static'), "%s/applications/welcome/static/path/to--/static" % root) + self.assertEqual(filter_url('http://domain.com/welcome/static/path/==to--/static'), "%s/applications/welcome/static/path/==to--/static" % root) + self.assertEqual(filter_url('http://domain.com/welcome/static/path/-+=@$%/static'), "%s/applications/welcome/static/path/-+=@$%%/static" % root) + self.assertRaises(HTTP, filter_url, 'http://domain.com/welcome/static/bad/path/to//static') + self.assertEqual(filter_url('http://domain.com/welcome/static/path/to/#static'), "%s/applications/welcome/static/path/to/#static" % root) + self.assertRaises(HTTP, filter_url, 'http://domain.com/welcome/static/bad/path/./static') + self.assertRaises(HTTP, filter_url, 'http://domain.com/welcome/static/bad/path/../static') + self.assertEqual(filter_url('http://domain.com/welcome/static/path/.../static'), "%s/applications/welcome/static/path/.../static" % root) + self.assertEqual(filter_url('http://domain.com/welcome/static/path/to/.static'), "%s/applications/welcome/static/path/to/.static" % root) + def test_router_args(self): ''' Test URL args parsing/generation diff --git a/gluon/tests/test_routes.py b/gluon/tests/test_routes.py index 7a0672db..c3b171d6 100644 --- a/gluon/tests/test_routes.py +++ b/gluon/tests/test_routes.py @@ -13,6 +13,7 @@ if os.path.isdir('gluon'): sys.path.append(os.path.realpath('gluon')) # running from web2py base else: sys.path.append(os.path.realpath('../')) # running from gluon/tests/ + os.environ['web2py_path'] = os.path.realpath('../../') # for settings from rewrite import load, filter_url, filter_err, get_effective_router, regex_filter_out, regex_select from html import URL diff --git a/router.example.py b/router.example.py index cc016a04..06710770 100644 --- a/router.example.py +++ b/router.example.py @@ -64,7 +64,9 @@ # map_static: By default, the default application is not stripped from static URLs. # Set map_static=True to override this policy. # acfe_match: regex for valid application, controller, function, extension /a/c/f.e -# file_match: regex for valid file (used for static file names) +# file_match: regex for valid subpath (used for static file paths) +# if file_match does not contain '/', it is uses to validate each element of a static file subpath, +# rather than the entire subpath. # args_match: regex for valid args # This validation provides a measure of security. # If it is changed, the application perform its own validation. @@ -84,9 +86,9 @@ # root_static = ['favicon.ico', 'robots.txt'], # domains = None, # map_hyphen = False, -# acfe_match = r'\w+$', # legal app/ctlr/fcn/ext -# file_match = r'(\w+[-=./]?)+$', # legal file (path) name -# args_match = r'([\w@ -]+[=.]?)+$', # legal arg in args +# acfe_match = r'\w+$', # legal app/ctlr/fcn/ext +# file_match = r'([-+=@$%\w]+[./]?)+$', # legal static subpath +# args_match = r'([\w@ -]+[=.]?)+$', # legal arg in args # ) # # See rewrite.map_url_in() and rewrite.map_url_out() for implementation details.