|
3 | 3 | from functools import partial, wraps |
4 | 4 | from inspect import getsource, signature |
5 | 5 | from mimetypes import guess_type |
6 | | -from os import path, sep |
7 | | -from pathlib import PurePath |
| 6 | +from os import path |
| 7 | +from pathlib import Path, PurePath |
8 | 8 | from textwrap import dedent |
9 | 9 | from time import gmtime, strftime |
10 | 10 | from typing import Any, Callable, Iterable, List, Optional, Set, Tuple, Union |
|
16 | 16 | from sanic.compat import stat_async |
17 | 17 | from sanic.constants import DEFAULT_HTTP_CONTENT_TYPE, HTTP_METHODS |
18 | 18 | from sanic.errorpages import RESPONSE_MAPPING |
19 | | -from sanic.exceptions import ( |
20 | | - ContentRangeError, |
21 | | - FileNotFound, |
22 | | - HeaderNotFound, |
23 | | - InvalidUsage, |
24 | | -) |
| 19 | +from sanic.exceptions import FileNotFound, HeaderNotFound, RangeNotSatisfiable |
25 | 20 | from sanic.handlers import ContentRangeHandler |
26 | 21 | from sanic.log import deprecation, error_logger |
27 | 22 | from sanic.models.futures import FutureRoute, FutureStatic |
@@ -774,39 +769,40 @@ async def _static_request_handler( |
774 | 769 | content_type=None, |
775 | 770 | __file_uri__=None, |
776 | 771 | ): |
777 | | -<<<<<<< HEAD |
778 | | - # Using this to determine if the URL is trying to break out of the path |
779 | | - # served. os.path.realpath seems to be very slow |
780 | | - if __file_uri__ and "../" in __file_uri__: |
781 | | - raise InvalidUsage("Invalid URL") |
782 | | -======= |
783 | | ->>>>>>> 9d415e4 (Prevent directory traversion with static files (#2495)) |
784 | 772 | # Merge served directory and requested file if provided |
785 | | - root_path = file_path = path.abspath(unquote(file_or_directory)) |
| 773 | + file_path_raw = Path(unquote(file_or_directory)) |
| 774 | + root_path = file_path = file_path_raw.resolve() |
| 775 | + not_found = FileNotFound( |
| 776 | + "File not found", |
| 777 | + path=file_or_directory, |
| 778 | + relative_url=__file_uri__, |
| 779 | + ) |
786 | 780 |
|
787 | 781 | if __file_uri__: |
788 | 782 | # Strip all / that in the beginning of the URL to help prevent |
789 | 783 | # python from herping a derp and treating the uri as an |
790 | 784 | # absolute path |
791 | 785 | unquoted_file_uri = unquote(__file_uri__).lstrip("/") |
| 786 | + file_path_raw = Path(file_or_directory, unquoted_file_uri) |
| 787 | + file_path = file_path_raw.resolve() |
| 788 | + if ( |
| 789 | + file_path < root_path and not file_path_raw.is_symlink() |
| 790 | + ) or file_path_raw.match("../**/*"): |
| 791 | + error_logger.exception( |
| 792 | + f"File not found: path={file_or_directory}, " |
| 793 | + f"relative_url={__file_uri__}" |
| 794 | + ) |
| 795 | + raise not_found |
792 | 796 |
|
793 | | - segments = unquoted_file_uri.split("/") |
794 | | - if ".." in segments or any(sep in segment for segment in segments): |
795 | | - raise BadRequest("Invalid URL") |
796 | | - |
797 | | - file_path = path.join(file_or_directory, unquoted_file_uri) |
798 | | - file_path = path.abspath(file_path) |
799 | | - |
800 | | - if not file_path.startswith(root_path): |
801 | | - error_logger.exception( |
802 | | - f"File not found: path={file_or_directory}, " |
803 | | - f"relative_url={__file_uri__}" |
804 | | - ) |
805 | | - raise FileNotFound( |
806 | | - "File not found", |
807 | | - path=file_or_directory, |
808 | | - relative_url=__file_uri__, |
809 | | - ) |
| 797 | + try: |
| 798 | + file_path.relative_to(root_path) |
| 799 | + except ValueError: |
| 800 | + if not file_path_raw.is_symlink(): |
| 801 | + error_logger.exception( |
| 802 | + f"File not found: path={file_or_directory}, " |
| 803 | + f"relative_url={__file_uri__}" |
| 804 | + ) |
| 805 | + raise not_found |
810 | 806 | try: |
811 | 807 | headers = {} |
812 | 808 | # Check if the client has been sent this file before |
@@ -874,11 +870,7 @@ async def _static_request_handler( |
874 | 870 | except ContentRangeError: |
875 | 871 | raise |
876 | 872 | except FileNotFoundError: |
877 | | - raise FileNotFound( |
878 | | - "File not found", |
879 | | - path=file_or_directory, |
880 | | - relative_url=__file_uri__, |
881 | | - ) |
| 873 | + raise not_found |
882 | 874 | except Exception: |
883 | 875 | error_logger.exception( |
884 | 876 | f"Exception in static request handler: " |
|
0 commit comments