python3Packages.sanic: add patch for CVE-2022-35920

Changed files
+145
pkgs
development
+141
pkgs/development/python-modules/sanic/22.3.2-CVE-2022-35920.patch
···
+
Based on upstream 9d415e4ec63d31b3749fd540e2c2ac7c98dedcdd and
+
2fa28f1711a8e59c6f4d3468e9c2f8b6991188a2, adjusted to apply to
+
v22.3.2, raise the same exception as before and not remove any
+
imports.
+
+
diff --git a/sanic/mixins/routes.py b/sanic/mixins/routes.py
+
index 9e2cf96..e0cf86b 100644
+
--- a/sanic/mixins/routes.py
+
+++ b/sanic/mixins/routes.py
+
@@ -3,7 +3,7 @@ from contextlib import suppress
+
from functools import partial, wraps
+
from inspect import getsource, signature
+
from mimetypes import guess_type
+
-from os import path
+
+from os import path, sep
+
from pathlib import PurePath
+
from re import sub
+
from textwrap import dedent
+
@@ -775,23 +775,23 @@ class RouteMixin(metaclass=SanicMeta):
+
content_type=None,
+
__file_uri__=None,
+
):
+
- # Using this to determine if the URL is trying to break out of the path
+
- # served. os.path.realpath seems to be very slow
+
- if __file_uri__ and "../" in __file_uri__:
+
- raise InvalidUsage("Invalid URL")
+
# Merge served directory and requested file if provided
+
- # Strip all / that in the beginning of the URL to help prevent python
+
- # from herping a derp and treating the uri as an absolute path
+
- root_path = file_path = file_or_directory
+
+ root_path = file_path = path.abspath(unquote(file_or_directory))
+
+
+
if __file_uri__:
+
- file_path = path.join(
+
- file_or_directory, sub("^[/]*", "", __file_uri__)
+
- )
+
+ # Strip all / that in the beginning of the URL to help prevent
+
+ # python from herping a derp and treating the uri as an
+
+ # absolute path
+
+ unquoted_file_uri = unquote(__file_uri__).lstrip("/")
+
+
+
+ segments = unquoted_file_uri.split("/")
+
+ if ".." in segments or any(sep in segment for segment in segments):
+
+ raise InvalidUsage("Invalid URL")
+
+
+
+ file_path = path.join(file_or_directory, unquoted_file_uri)
+
+ file_path = path.abspath(file_path)
+
+
- # URL decode the path sent by the browser otherwise we won't be able to
+
- # match filenames which got encoded (filenames with spaces etc)
+
- file_path = path.abspath(unquote(file_path))
+
- if not file_path.startswith(path.abspath(unquote(root_path))):
+
+ if not file_path.startswith(root_path):
+
error_logger.exception(
+
f"File not found: path={file_or_directory}, "
+
f"relative_url={__file_uri__}"
+
diff --git a/tests/test_static.py b/tests/test_static.py
+
index 36a98e1..aeb625b 100644
+
--- a/tests/test_static.py
+
+++ b/tests/test_static.py
+
@@ -1,6 +1,7 @@
+
import inspect
+
import logging
+
import os
+
+import sys
+
+
from collections import Counter
+
from pathlib import Path
+
@@ -8,7 +9,7 @@ from time import gmtime, strftime
+
+
import pytest
+
+
-from sanic import text
+
+from sanic import Sanic, text
+
from sanic.exceptions import FileNotFound
+
+
+
@@ -21,6 +22,22 @@ def static_file_directory():
+
return static_directory
+
+
+
+@pytest.fixture(scope="module")
+
+def double_dotted_directory_file(static_file_directory: str):
+
+ """Generate double dotted directory and its files"""
+
+ if sys.platform == "win32":
+
+ raise Exception("Windows doesn't support double dotted directories")
+
+
+
+ file_path = Path(static_file_directory) / "dotted.." / "dot.txt"
+
+ double_dotted_dir = file_path.parent
+
+ Path.mkdir(double_dotted_dir, exist_ok=True)
+
+ with open(file_path, "w") as f:
+
+ f.write("DOT\n")
+
+ yield file_path
+
+ Path.unlink(file_path)
+
+ Path.rmdir(double_dotted_dir)
+
+
+
+
+
def get_file_path(static_file_directory, file_name):
+
return os.path.join(static_file_directory, file_name)
+
+
@@ -578,3 +595,40 @@ def test_resource_type_dir(app, static_file_directory):
+
def test_resource_type_unknown(app, static_file_directory, caplog):
+
with pytest.raises(ValueError):
+
app.static("/static", static_file_directory, resource_type="unknown")
+
+
+
+
+
+@pytest.mark.skipif(
+
+ sys.platform == "win32",
+
+ reason="Windows does not support double dotted directories",
+
+)
+
+def test_dotted_dir_ok(
+
+ app: Sanic, static_file_directory: str, double_dotted_directory_file: Path
+
+):
+
+ app.static("/foo", static_file_directory)
+
+ dot_relative_path = str(
+
+ double_dotted_directory_file.relative_to(static_file_directory)
+
+ )
+
+ _, response = app.test_client.get("/foo/" + dot_relative_path)
+
+ assert response.status == 200
+
+ assert response.body == b"DOT\n"
+
+
+
+
+
+def test_breakout(app: Sanic, static_file_directory: str):
+
+ app.static("/foo", static_file_directory)
+
+
+
+ _, response = app.test_client.get("/foo/..%2Fstatic/test.file")
+
+ assert response.status == 400
+
+
+
+
+
+@pytest.mark.skipif(
+
+ sys.platform != "win32", reason="Block backslash on Windows only"
+
+)
+
+def test_double_backslash_prohibited_on_win32(
+
+ app: Sanic, static_file_directory: str
+
+):
+
+ app.static("/foo", static_file_directory)
+
+
+
+ _, response = app.test_client.get("/foo/static/..\\static/test.file")
+
+ assert response.status == 400
+
+ _, response = app.test_client.get("/foo/static\\../static/test.file")
+
+ assert response.status == 400
+4
pkgs/development/python-modules/sanic/default.nix
···
hash = "sha256-4zdPp3X22dfZ5YlW3G5/OqeUxrt+NiFO9dk2XjEKXEg=";
};
+
patches = [
+
./22.3.2-CVE-2022-35920.patch
+
];
+
postPatch = ''
# Loosen dependency requirements.
substituteInPlace setup.py \