11import asyncio
22import functools
33import pathlib
4+ import sys
45from typing import Optional
56from unittest import mock
67from unittest .mock import MagicMock
1415
1516
1617@pytest .mark .parametrize (
17- "show_index,status,prefix,data" ,
18+ "show_index,status,prefix,request_path, data" ,
1819 [
19- pytest .param (False , 403 , "/" , None , id = "index_forbidden" ),
20+ pytest .param (False , 403 , "/" , "/" , None , id = "index_forbidden" ),
2021 pytest .param (
2122 True ,
2223 200 ,
2324 "/" ,
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>" ,
3029 ),
3130 pytest .param (
3231 True ,
3332 200 ,
3433 "/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>' ,
4038 id = "index_static" ,
4139 ),
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+ ),
4250 ],
4351)
4452async def test_access_root_of_static_handler (
@@ -47,6 +55,7 @@ async def test_access_root_of_static_handler(
4755 show_index : bool ,
4856 status : int ,
4957 prefix : str ,
58+ request_path : str ,
5059 data : Optional [bytes ],
5160) -> None :
5261 # Tests the operation of static file server.
@@ -72,7 +81,94 @@ async def test_access_root_of_static_handler(
7281 client = await aiohttp_client (app )
7382
7483 # 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 :
76172 assert r .status == status
77173
78174 if data :
0 commit comments