From 5d3972791d04d75bf76d6fa41855fd7bbe21f42f Mon Sep 17 00:00:00 2001 From: haochen <3033940900@qq.com> Date: Sun, 27 Sep 2026 10:13:05 +0800 Subject: [PATCH] fix(paths): reject trailing newlines in scoped IDs (#5911) Co-authored-by: Zhangchenhao-111 <285911435+Zhangchenhao-111@users.noreply.github.com> --- .../packages/harness/deerflow/config/paths.py | 6 +++--- backend/tests/test_paths_user_isolation.py | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/backend/packages/harness/deerflow/config/paths.py b/backend/packages/harness/deerflow/config/paths.py index 6f4fcc0ffa..3723f04969 100644 --- a/backend/packages/harness/deerflow/config/paths.py +++ b/backend/packages/harness/deerflow/config/paths.py @@ -31,14 +31,14 @@ def _validate_thread_id(thread_id: str) -> str: def _validate_user_id(user_id: str) -> str: """Validate a user ID before using it in filesystem paths.""" - if not _SAFE_USER_ID_RE.match(user_id): + if not _SAFE_USER_ID_RE.fullmatch(user_id): raise ValueError(f"Invalid user_id {user_id!r}: only alphanumeric characters, hyphens, and underscores are allowed.") return user_id def _validate_integration_id(integration_id: str) -> str: """Validate an integration ID before using it in filesystem paths.""" - if not _SAFE_INTEGRATION_ID_RE.match(integration_id): + if not _SAFE_INTEGRATION_ID_RE.fullmatch(integration_id): raise ValueError(f"Invalid integration_id {integration_id!r}: only alphanumeric characters, dots, hyphens, and underscores are allowed.") # The charset allows dots for names like ``some.integration``; reject the # bare ``.``/``..`` path components so a future caller cannot escape the @@ -50,7 +50,7 @@ def _validate_integration_id(integration_id: str) -> str: def _validate_project_id(project_id: str) -> str: """Validate a project ID before using it in filesystem paths.""" - if not _SAFE_USER_ID_RE.match(project_id): + if not _SAFE_USER_ID_RE.fullmatch(project_id): raise ValueError(f"Invalid project_id {project_id!r}: only alphanumeric characters, hyphens, and underscores are allowed.") return project_id diff --git a/backend/tests/test_paths_user_isolation.py b/backend/tests/test_paths_user_isolation.py index a7197bcec6..9c7a8b1181 100644 --- a/backend/tests/test_paths_user_isolation.py +++ b/backend/tests/test_paths_user_isolation.py @@ -29,6 +29,10 @@ class TestValidateUserId: with pytest.raises(ValueError, match="Invalid user_id"): paths.user_dir("") + def test_rejects_trailing_newline(self, paths: Paths): + with pytest.raises(ValueError, match="Invalid user_id"): + paths.user_dir("alice\n") + class TestMakeSafeUserId: def test_already_safe_id_is_unchanged(self): @@ -80,6 +84,12 @@ class TestValidateIntegrationId: with pytest.raises(ValueError, match="Invalid integration_id"): _validate_integration_id(integration_id) + def test_rejects_trailing_newline(self): + from deerflow.config.paths import _validate_integration_id + + with pytest.raises(ValueError, match="Invalid integration_id"): + _validate_integration_id("lark-cli\n") + @pytest.mark.parametrize("integration_id", [".", ".."]) def test_host_integration_config_dir_rejects_dot_traversal(self, paths: Paths, integration_id): with pytest.raises(ValueError, match="Invalid integration_id"): @@ -91,6 +101,12 @@ class TestValidateIntegrationId: paths.host_user_integration_data_dir("alice", integration_id) +class TestValidateProjectId: + def test_rejects_trailing_newline(self, paths: Paths): + with pytest.raises(ValueError, match="Invalid project_id"): + paths.user_project_dir("alice", "project\n") + + class TestUserDir: def test_user_dir(self, paths: Paths): assert paths.user_dir("alice") == paths.base_dir / "users" / "alice"