1 import os 2 from paste.urlparser import * 3 from paste.fixture import * 4 from pkg_resources import get_distribution 5 6 def relative_path(name): 7 here = os.path.join(os.path.dirname(os.path.abspath(__file__)), 8 'urlparser_data') 9 f = os.path.join('urlparser_data', '..', 'urlparser_data', name) 10 return os.path.join(here, f) 11 12 def path(name): 13 return os.path.join(os.path.dirname(os.path.abspath(__file__)), 14 'urlparser_data', name) 15 16 def make_app(name): 17 app = URLParser({}, path(name), name, index_names=['index', 'Main']) 18 testapp = TestApp(app) 19 return testapp 20 21 def test_find_file(): 22 app = make_app('find_file') 23 res = app.get('/') 24 assert 'index1' in res 25 assert res.header('content-type') == 'text/plain' 26 res = app.get('/index') 27 assert 'index1' in res 28 assert res.header('content-type') == 'text/plain' 29 res = app.get('/index.txt') 30 assert 'index1' in res 31 assert res.header('content-type') == 'text/plain' 32 res = app.get('/test2.html') 33 assert 'test2' in res 34 assert res.header('content-type') == 'text/html' 35 res = app.get('/test 3.html') 36 assert 'test 3' in res 37 assert res.header('content-type') == 'text/html' 38 res = app.get('/test%203.html') 39 assert 'test 3' in res 40 assert res.header('content-type') == 'text/html' 41 res = app.get('/dir with spaces/test 4.html') 42 assert 'test 4' in res 43 assert res.header('content-type') == 'text/html' 44 res = app.get('/dir%20with%20spaces/test%204.html') 45 assert 'test 4' in res 46 assert res.header('content-type') == 'text/html' 47 # Ensure only data under the app's root directory is accessible 48 res = app.get('/../secured.txt', status=404) 49 res = app.get('/dir with spaces/../../secured.txt', status=404) 50 res = app.get('/%2e%2e/secured.txt', status=404) 51 res = app.get('/%2e%2e%3fsecured.txt', status=404) 52 res = app.get('/..%3fsecured.txt', status=404) 53 res = app.get('/dir%20with%20spaces/%2e%2e/%2e%2e/secured.txt', status=404) 54 55 def test_deep(): 56 app = make_app('deep') 57 res = app.get('/') 58 assert 'index2' in res 59 res = app.get('/sub') 60 assert res.status == 301 61 print(res) 62 assert res.header('location') == 'http://localhost/sub/' 63 assert 'http://localhost/sub/' in res 64 res = app.get('/sub/') 65 assert 'index3' in res 66 67 def test_python(): 68 app = make_app('python') 69 res = app.get('/simpleapp') 70 assert 'test1' in res 71 assert res.header('test-header') == 'TEST!' 72 assert res.header('content-type') == 'text/html' 73 res = app.get('/stream') 74 assert 'test2' in res 75 res = app.get('/sub/simpleapp') 76 assert 'subsimple' in res 77 78 def test_hook(): 79 app = make_app('hook') 80 res = app.get('/bob/app') 81 assert 'user: bob' in res 82 res = app.get('/tim/') 83 assert 'index: tim' in res 84 85 def test_not_found_hook(): 86 app = make_app('not_found') 87 res = app.get('/simple/notfound') 88 assert res.status == 200 89 assert 'not found' in res 90 res = app.get('/simple/found') 91 assert 'is found' in res 92 res = app.get('/recur/__notfound', status=404) 93 # @@: It's unfortunate that the original path doesn't actually show up 94 assert '/recur/notfound' in res 95 res = app.get('/recur/__isfound') 96 assert res.status == 200 97 assert 'is found' in res 98 res = app.get('/user/list') 99 assert 'user: None' in res 100 res = app.get('/user/bob/list') 101 assert res.status == 200 102 assert 'user: bob' in res 103 104 def test_relative_path_in_static_parser(): 105 x = relative_path('find_file') 106 app = StaticURLParser(relative_path('find_file')) 107 assert '..' not in app.root_directory 108 109 def test_xss(): 110 app = TestApp(StaticURLParser(relative_path('find_file')), 111 extra_environ={'HTTP_ACCEPT': 'text/html'}) 112 res = app.get("/-->%0D<script>alert('xss')</script>", status=404) 113 assert b'--><script>' not in res.body 114 115 def test_static_parser(): 116 app = StaticURLParser(path('find_file')) 117 testapp = TestApp(app) 118 res = testapp.get('', status=301) 119 res = testapp.get('/', status=404) 120 res = testapp.get('/index.txt') 121 assert res.body.strip() == b'index1' 122 res = testapp.get('/index.txt/foo', status=404) 123 res = testapp.get('/test 3.html') 124 assert res.body.strip() == b'test 3' 125 res = testapp.get('/test%203.html') 126 assert res.body.strip() == b'test 3' 127 res = testapp.get('/dir with spaces/test 4.html') 128 assert res.body.strip() == b'test 4' 129 res = testapp.get('/dir%20with%20spaces/test%204.html') 130 assert res.body.strip() == b'test 4' 131 # Ensure only data under the app's root directory is accessible 132 res = testapp.get('/../secured.txt', status=404) 133 res = testapp.get('/dir with spaces/../../secured.txt', status=404) 134 res = testapp.get('/%2e%2e/secured.txt', status=404) 135 res = testapp.get('/dir%20with%20spaces/%2e%2e/%2e%2e/secured.txt', status=404) 136 res = testapp.get('/dir%20with%20spaces/', status=404) 137 138 def test_egg_parser(): 139 app = PkgResourcesParser('Paste', 'paste') 140 testapp = TestApp(app) 141 res = testapp.get('', status=301) 142 res = testapp.get('/', status=404) 143 res = testapp.get('/flup_session', status=404) 144 res = testapp.get('/util/classinit.py') 145 assert 'ClassInitMeta' in res 146 res = testapp.get('/util/classinit', status=404) 147 res = testapp.get('/util', status=301) 148 res = testapp.get('/util/classinit.py/foo', status=404) 149 150 # Find a readable file in the Paste pkg's root directory (or upwards the 151 # directory tree). Ensure it's not accessible via the URLParser 152 unreachable_test_file = None 153 search_path = pkg_root_path = get_distribution('Paste').location 154 level = 0 155 # We might not find any readable files in the pkg's root directory (this 156 # is likely when Paste is installed as a .egg in site-packages). We 157 # (hopefully) can prevent this by traversing up the directory tree until 158 # a usable file is found 159 while unreachable_test_file is None and \ 160 os.path.normpath(search_path) != os.path.sep: 161 for file in os.listdir(search_path): 162 full_path = os.path.join(search_path, file) 163 if os.path.isfile(full_path) and os.access(full_path, os.R_OK): 164 unreachable_test_file = file 165 break 166 167 search_path = os.path.dirname(search_path) 168 level += 1 169 assert unreachable_test_file is not None, \ 170 'test_egg_parser requires a readable file in a parent dir of the\n' \ 171 'Paste pkg\'s root dir:\n%s' % pkg_root_path 172 173 unreachable_path = '/' + '../'*level + unreachable_test_file 174 unreachable_path_quoted = '/' + '%2e%2e/'*level + unreachable_test_file 175 res = testapp.get(unreachable_path, status=404) 176 res = testapp.get('/util/..' + unreachable_path, status=404) 177 res = testapp.get(unreachable_path_quoted, status=404) 178 res = testapp.get('/util/%2e%2e' + unreachable_path_quoted, status=404) 179