From d05042f1a35ec0adb797c056024d457ac1fd7088 Mon Sep 17 00:00:00 2001
From: Sam Bull <git@sambull.org>
Date: Thu, 11 Apr 2024 15:54:45 +0100
Subject: [PATCH] Escape filenames and paths in HTML when generating index
 pages (#8317) (#8319)

Upstream-Status: Backport
[https://github.com/aio-libs/aiohttp/commit/28335525d1eac015a7e7584137678cbb6ff19397]

CVE: CVE-2024-27306

Co-authored-by: J. Nick Koston <nick@koston.org>
(cherry picked from commit ffbc43233209df302863712b511a11bdb6001b0f)
Signed-off-by: Jiaying Song <jiaying.song.cn@windriver.com>
---
 CHANGES/8317.bugfix.rst      |  1 +
 aiohttp/web_urldispatcher.py | 11 ++++++-----
 2 files changed, 7 insertions(+), 5 deletions(-)
 create mode 100644 CHANGES/8317.bugfix.rst

diff --git a/CHANGES/8317.bugfix.rst b/CHANGES/8317.bugfix.rst
new file mode 100644
index 0000000..b24ef2a
--- /dev/null
+++ b/CHANGES/8317.bugfix.rst
@@ -0,0 +1 @@
+Escaped filenames in static view -- by :user:`bdraco`.
diff --git a/aiohttp/web_urldispatcher.py b/aiohttp/web_urldispatcher.py
index e8a8023..791ab94 100644
--- a/aiohttp/web_urldispatcher.py
+++ b/aiohttp/web_urldispatcher.py
@@ -1,7 +1,9 @@
 import abc
 import asyncio
 import base64
+import functools
 import hashlib
+import html
 import inspect
 import keyword
 import os
@@ -87,6 +89,7 @@ PATH_SEP: Final[str] = re.escape("/")
 _ExpectHandler = Callable[[Request], Awaitable[None]]
 _Resolve = Tuple[Optional["UrlMappingMatchInfo"], Set[str]]
 
+html_escape = functools.partial(html.escape, quote=True)
 
 class _InfoDict(TypedDict, total=False):
     path: str
@@ -706,7 +709,7 @@ class StaticResource(PrefixResource):
         assert filepath.is_dir()
 
         relative_path_to_dir = filepath.relative_to(self._directory).as_posix()
-        index_of = f"Index of /{relative_path_to_dir}"
+        index_of = f"Index of /{html_escape(relative_path_to_dir)}"
         h1 = f"<h1>{index_of}</h1>"
 
         index_list = []
@@ -714,7 +717,7 @@ class StaticResource(PrefixResource):
         for _file in sorted(dir_index):
             # show file url as relative to static path
             rel_path = _file.relative_to(self._directory).as_posix()
-            file_url = self._prefix + "/" + rel_path
+            quoted_file_url = _quote_path(f"{self._prefix}/{rel_path}")
 
             # if file is a directory, add '/' to the end of the name
             if _file.is_dir():
@@ -723,9 +726,7 @@ class StaticResource(PrefixResource):
                 file_name = _file.name
 
             index_list.append(
-                '<li><a href="{url}">{name}</a></li>'.format(
-                    url=file_url, name=file_name
-                )
+                f'<li><a href="{quoted_file_url}">{html_escape(file_name)}</a></li>'
             )
         ul = "<ul>\n{}\n</ul>".format("\n".join(index_list))
         body = f"<body>\n{h1}\n{ul}\n</body>"
-- 
2.25.1

