CVE-2023-1176: Absolute Path Traversal (CWE-36)
value: Absolute Path Traversal in GitHub repository mlflow/mlflow prior to 2.2.2.
CWE description: The product uses external input to construct a pathname that should be within a restricted directory, but it does not properly neutralize absolute path sequences such as "/abs/path" that can resolve to a location that is outside of that directory.
CVSS v3 base score: 3.3
Published: 2023-03-24T15:15Z
Repository: https://github.com/mlflow/mlflow
Language: Python
Files changed: mlflow/store/model_registry/file_store.py, mlflow/utils/file_utils.py, tests/store/model_registry/test_file_store.py
Commit date: 2023-02-25 14:22:33 +0900
Commit message: Prevent registered model name from containing path separator (#7892)

Signed-off-by: harupy <hkawamura0130@gmail.com>
Diff: diff --git a/mlflow/store/model_registry/file_store.py b/mlflow/store/model_registry/file_store.py
index 32244f8a..77b4ffaf 100644
--- a/mlflow/store/model_registry/file_store.py
+++ b/mlflow/store/model_registry/file_store.py
@@ -35,11 +35,11 @@ from mlflow.store.model_registry import (
 from mlflow.utils.search_utils import SearchUtils, SearchModelUtils, SearchModelVersionUtils
 from mlflow.utils.string_utils import is_string_type
 from mlflow.utils.validation import (
     _validate_registered_model_tag,
     _validate_model_version_tag,
-    _validate_model_name,
+    _validate_model_name as _original_validate_model_name,
     _validate_model_version,
     _validate_tag_name,
 )
 from mlflow.utils.env import get_env
 from mlflow.utils.file_utils import (
@@ -54,10 +54,11 @@ from mlflow.utils.file_utils import (
     read_file,
     write_to,
     make_containing_dirs,
     list_all,
     local_file_uri_to_path,
+    contains_path_separator,
 )
 from mlflow.utils.time_utils import get_current_time_millis
 
 
 _REGISTRY_DIR_ENV_VAR = "MLFLOW_REGISTRY_DIR"
@@ -65,10 +66,19 @@ _REGISTRY_DIR_ENV_VAR = "MLFLOW_REGISTRY_DIR"
 
 def _default_root_dir():
     return get_env(_REGISTRY_DIR_ENV_VAR) or os.path.abspath(DEFAULT_LOCAL_FILE_AND_ARTIFACT_PATH)
 
 
+def _validate_model_name(name):
+    _original_validate_model_name(name)
+    if contains_path_separator(name):
+        raise MlflowException(
+            f"Invalid name: '{name}'. Registered model name cannot contain path separator",
+            INVALID_PARAMETER_VALUE,
+        )
+
+
 class FileStore(AbstractStore):
     MODELS_FOLDER_NAME = "models"
     META_DATA_FILE_NAME = "meta.yaml"
     TAGS_FOLDER_NAME = "tags"
     MODEL_VERSION_TAGS_FOLDER_NAME = "tags"
diff --git a/mlflow/utils/file_utils.py b/mlflow/utils/file_utils.py
index e996e9af..282354f2 100644
--- a/mlflow/utils/file_utils.py
+++ b/mlflow/utils/file_utils.py
@@ -709,5 +709,12 @@ def shutil_copytree_without_file_permissions(src_dir, dst_dir):
             # the appropriate location in the destination directory
             file_path = os.path.join(dirpath, filename)
             relative_file_path = os.path.relpath(file_path, src_dir)
             abs_file_path = os.path.join(dst_dir, relative_file_path)
             shutil.copyfile(file_path, abs_file_path)
+
+
+def contains_path_separator(path):
+    """
+    Returns True if a path contains a path separator, False otherwise.
+    """
+    return any((sep in path) for sep in (os.path.sep, os.path.altsep) if sep is not None)
diff --git a/tests/store/model_registry/test_file_store.py b/tests/store/model_registry/test_file_store.py
index 1d984e7e..ea5a5882 100644
--- a/tests/store/model_registry/test_file_store.py
+++ b/tests/store/model_registry/test_file_store.py
@@ -60,10 +60,18 @@ def test_create_registered_model(store):
     assert model.latest_versions == []
     assert model.creation_timestamp == model.last_updated_timestamp
     assert model.tags == {}
 
 
+def test_create_registered_model_with_name_that_looks_like_path(store, tmp_path):
+    name = str(tmp_path.joinpath("test"))
+    with pytest.raises(
+        MlflowException, match=r"Registered model name cannot contain path separator"
+    ):
+        store.get_registered_model(name)
+
+
 def _verify_registered_model(fs, name, rm_data):
     rm = fs.get_registered_model(name)
     assert rm.name == name
     assert rm.creation_timestamp == rm_data[name]["creation_timestamp"]
     assert rm.last_updated_timestamp == rm_data[name]["last_updated_timestamp"]

Vulnerable code:     _validate_model_name,
Fixed code:     _validate_model_name as _original_validate_model_name,
    contains_path_separator,
def _validate_model_name(name):
    _original_validate_model_name(name)
    if contains_path_separator(name):
        raise MlflowException(
            f"Invalid name: '{name}'. Registered model name cannot contain path separator",
            INVALID_PARAMETER_VALUE,
        )




def contains_path_separator(path):
    """
    Returns True if a path contains a path separator, False otherwise.
    """
    return any((sep in path) for sep in (os.path.sep, os.path.altsep) if sep is not None)
def test_create_registered_model_with_name_that_looks_like_path(store, tmp_path):
    name = str(tmp_path.joinpath("test"))
    with pytest.raises(
        MlflowException, match=r"Registered model name cannot contain path separator"
    ):
        store.get_registered_model(name)