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