1
1
import asyncio
2
2
import functools
3
3
import pathlib
4
+ import sys
4
5
from typing import Optional
5
6
from unittest import mock
6
7
from unittest .mock import MagicMock
14
15
15
16
16
17
@pytest .mark .parametrize (
17
- "show_index,status,prefix,data" ,
18
+ "show_index,status,prefix,request_path, data" ,
18
19
[
19
- pytest .param (False , 403 , "/" , None , id = "index_forbidden" ),
20
+ pytest .param (False , 403 , "/" , "/" , None , id = "index_forbidden" ),
20
21
pytest .param (
21
22
True ,
22
23
200 ,
23
24
"/" ,
24
- b"<html>\n <head>\n <title>Index of /.</title>\n "
25
- b"</head>\n <body>\n <h1>Index of /.</h1>\n <ul>\n "
26
- b'<li><a href="/my_dir">my_dir/</a></li>\n '
27
- b'<li><a href="/my_file">my_file</a></li>\n '
28
- b"</ul>\n </body>\n </html>" ,
29
- id = "index_root" ,
25
+ "/" ,
26
+ b"<html>\n <head>\n <title>Index of /.</title>\n </head>\n <body>\n <h1>Index of"
27
+ b' /.</h1>\n <ul>\n <li><a href="/my_dir">my_dir/</a></li>\n <li><a href="/my_file">'
28
+ b"my_file</a></li>\n </ul>\n </body>\n </html>" ,
30
29
),
31
30
pytest .param (
32
31
True ,
33
32
200 ,
34
33
"/static" ,
35
- b"<html>\n <head>\n <title>Index of /.</title>\n "
36
- b"</head>\n <body>\n <h1>Index of /.</h1>\n <ul>\n "
37
- b'<li><a href="/static/my_dir">my_dir/</a></li>\n '
38
- b'<li><a href="/static/my_file">my_file</a></li>\n '
39
- b"</ul>\n </body>\n </html>" ,
34
+ "/static" ,
35
+ b"<html>\n <head>\n <title>Index of /.</title>\n </head>\n <body>\n <h1>Index of"
36
+ b' /.</h1>\n <ul>\n <li><a href="/static/my_dir">my_dir/</a></li>\n <li><a href="'
37
+ b'/static/my_file">my_file</a></li>\n </ul>\n </body>\n </html>' ,
40
38
id = "index_static" ,
41
39
),
40
+ pytest .param (
41
+ True ,
42
+ 200 ,
43
+ "/static" ,
44
+ "/static/my_dir" ,
45
+ b"<html>\n <head>\n <title>Index of /my_dir</title>\n </head>\n <body>\n <h1>"
46
+ b'Index of /my_dir</h1>\n <ul>\n <li><a href="/static/my_dir/my_file_in_dir">'
47
+ b"my_file_in_dir</a></li>\n </ul>\n </body>\n </html>" ,
48
+ id = "index_subdir" ,
49
+ ),
42
50
],
43
51
)
44
52
async def test_access_root_of_static_handler (
@@ -47,6 +55,7 @@ async def test_access_root_of_static_handler(
47
55
show_index : bool ,
48
56
status : int ,
49
57
prefix : str ,
58
+ request_path : str ,
50
59
data : Optional [bytes ],
51
60
) -> None :
52
61
# Tests the operation of static file server.
@@ -71,7 +80,94 @@ async def test_access_root_of_static_handler(
71
80
client = await aiohttp_client (app )
72
81
73
82
# Request the root of the static directory.
74
- async with await client .get (prefix ) as r :
83
+ async with await client .get (request_path ) as r :
84
+ assert r .status == status
85
+
86
+ if data :
87
+ assert r .headers ["Content-Type" ] == "text/html; charset=utf-8"
88
+ read_ = await r .read ()
89
+ assert read_ == data
90
+
91
+
92
+ @pytest .mark .internal # Dependent on filesystem
93
+ @pytest .mark .skipif (
94
+ not sys .platform .startswith ("linux" ),
95
+ reason = "Invalid filenames on some filesystems (like Windows)" ,
96
+ )
97
+ @pytest .mark .parametrize (
98
+ "show_index,status,prefix,request_path,data" ,
99
+ [
100
+ pytest .param (False , 403 , "/" , "/" , None , id = "index_forbidden" ),
101
+ pytest .param (
102
+ True ,
103
+ 200 ,
104
+ "/" ,
105
+ "/" ,
106
+ b"<html>\n <head>\n <title>Index of /.</title>\n </head>\n <body>\n <h1>Index of"
107
+ b' /.</h1>\n <ul>\n <li><a href="/%3Cimg%20src=0%20onerror=alert(1)%3E.dir">&l'
108
+ b't;img src=0 onerror=alert(1)>.dir/</a></li>\n <li><a href="/%3Cimg%20sr'
109
+ b'c=0%20onerror=alert(1)%3E.txt"><img src=0 onerror=alert(1)>.txt</a></l'
110
+ b"i>\n </ul>\n </body>\n </html>" ,
111
+ ),
112
+ pytest .param (
113
+ True ,
114
+ 200 ,
115
+ "/static" ,
116
+ "/static" ,
117
+ b"<html>\n <head>\n <title>Index of /.</title>\n </head>\n <body>\n <h1>Index of"
118
+ b' /.</h1>\n <ul>\n <li><a href="/static/%3Cimg%20src=0%20onerror=alert(1)%3E.'
119
+ b'dir"><img src=0 onerror=alert(1)>.dir/</a></li>\n <li><a href="/stat'
120
+ b'ic/%3Cimg%20src=0%20onerror=alert(1)%3E.txt"><img src=0 onerror=alert(1)&'
121
+ b"gt;.txt</a></li>\n </ul>\n </body>\n </html>" ,
122
+ id = "index_static" ,
123
+ ),
124
+ pytest .param (
125
+ True ,
126
+ 200 ,
127
+ "/static" ,
128
+ "/static/<img src=0 onerror=alert(1)>.dir" ,
129
+ b"<html>\n <head>\n <title>Index of /<img src=0 onerror=alert(1)>.dir</t"
130
+ b"itle>\n </head>\n <body>\n <h1>Index of /<img src=0 onerror=alert(1)>.di"
131
+ b'r</h1>\n <ul>\n <li><a href="/static/%3Cimg%20src=0%20onerror=alert(1)%3E.di'
132
+ b'r/my_file_in_dir">my_file_in_dir</a></li>\n </ul>\n </body>\n </html>' ,
133
+ id = "index_subdir" ,
134
+ ),
135
+ ],
136
+ )
137
+ async def test_access_root_of_static_handler_xss (
138
+ tmp_path : pathlib .Path ,
139
+ aiohttp_client : AiohttpClient ,
140
+ show_index : bool ,
141
+ status : int ,
142
+ prefix : str ,
143
+ request_path : str ,
144
+ data : Optional [bytes ],
145
+ ) -> None :
146
+ # Tests the operation of static file server.
147
+ # Try to access the root of static file server, and make
148
+ # sure that correct HTTP statuses are returned depending if we directory
149
+ # index should be shown or not.
150
+ # Ensure that html in file names is escaped.
151
+ # Ensure that links are url quoted.
152
+ my_file = tmp_path / "<img src=0 onerror=alert(1)>.txt"
153
+ my_dir = tmp_path / "<img src=0 onerror=alert(1)>.dir"
154
+ my_dir .mkdir ()
155
+ my_file_in_dir = my_dir / "my_file_in_dir"
156
+
157
+ with my_file .open ("w" ) as fw :
158
+ fw .write ("hello" )
159
+
160
+ with my_file_in_dir .open ("w" ) as fw :
161
+ fw .write ("world" )
162
+
163
+ app = web .Application ()
164
+
165
+ # Register global static route:
166
+ app .router .add_static (prefix , str (tmp_path ), show_index = show_index )
167
+ client = await aiohttp_client (app )
168
+
169
+ # Request the root of the static directory.
170
+ async with await client .get (request_path ) as r :
75
171
assert r .status == status
76
172
77
173
if data :
0 commit comments