CVE-2023-6831 (Python)

MLflow's artifact-serving HTTP API is vulnerable to path traversal when attacker-controlled artifact paths are accepted without robust normalization and validation. An attacker can submit artifact path components or query parameters to the mlflow-artifacts endpoints that contain parent-directory traversal sequences, including URL-encoded or repeatedly encoded forms and Windows-style backslash separators such as "..\\file". If these paths are passed to artifact repository operations, the server may read, write, delete, or enumerate files outside the intended artifact root.

Weakness:
CWE-73: name: External Control of File Name or Path
description: The product allows user input to control or influence paths or file names that are used in filesystem operations.
CWE-22: name: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
description: The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.

Repository: https://github.com/mlflow/mlflow

Vulnerable code:
file_path: mlflow/utils/uri.py
snippet: def validate_path_is_safe(path):
    """
    Validates that the specified path is safe to join with a trusted prefix. This is a security
    measure to prevent path traversal attacks.
    A valid path should:
        not contain separators other than '/'
        not contain .. to navigate to parent dir in path
        not be an absolute path
    """
    from mlflow.utils.file_utils import local_file_uri_to_path

    exc = MlflowException(f"Invalid path: {path}", error_code=INVALID_PARAMETER_VALUE)
    if any((s in path) for s in ("#", "%23")):
        raise exc

    if is_file_uri(path):
        path = local_file_uri_to_path(path)
    if (
        any((s in path) for s in _OS_ALT_SEPS)
        or ".." in path.split("/")
        or pathlib.PureWindowsPath(path).is_absolute()
        or pathlib.PurePosixPath(path).is_absolute()
        or (is_windows() and len(path) >= 2 and path[1] == ":")
    ):
        raise exc

Fixed code:
file_path: mlflow/utils/uri.py
snippet: def validate_path_is_safe(path):
    """
    Validates that the specified path is safe to join with a trusted prefix. This is a security
    measure to prevent path traversal attacks.
    A valid path should:
        not contain separators other than '/'
        not contain .. to navigate to parent dir in path
        not be an absolute path
    """
    from mlflow.utils.file_utils import local_file_uri_to_path

    # We must decode URL before validating it
    path = urllib.parse.unquote(path)

    exc = MlflowException("Invalid path", error_code=INVALID_PARAMETER_VALUE)
    if any((s in path) for s in ("#", "%23")):
        raise exc

    if is_file_uri(path):
        path = local_file_uri_to_path(path)
    if (
        any((s in path) for s in _OS_ALT_SEPS)
        or ".." in path.split("/")
        or pathlib.PureWindowsPath(path).is_absolute()
        or pathlib.PurePosixPath(path).is_absolute()
        or (is_windows() and len(path) >= 2 and path[1] == ":")
    ):
        raise exc