From 4ba5b95722a243f66c53e4d5a8445a0d0bd7997c Mon Sep 17 00:00:00 2001 From: tangtaizong666 Date: Thu, 25 Jun 2026 17:31:57 +0800 Subject: [PATCH 1/2] fix: guard workspace umo normalization --- astrbot/core/tools/computer_tools/util.py | 2 ++ tests/unit/test_computer_workspace_util.py | 38 ++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 tests/unit/test_computer_workspace_util.py diff --git a/astrbot/core/tools/computer_tools/util.py b/astrbot/core/tools/computer_tools/util.py index a3930b4c6a..67b48a76c2 100644 --- a/astrbot/core/tools/computer_tools/util.py +++ b/astrbot/core/tools/computer_tools/util.py @@ -8,6 +8,8 @@ def normalize_umo_for_workspace(umo: str) -> str: normalized = re.sub(r"[^A-Za-z0-9._-]+", "_", umo.strip()) + if not normalized.strip("._-"): + return "unknown" return normalized or "unknown" diff --git a/tests/unit/test_computer_workspace_util.py b/tests/unit/test_computer_workspace_util.py new file mode 100644 index 0000000000..e55feada76 --- /dev/null +++ b/tests/unit/test_computer_workspace_util.py @@ -0,0 +1,38 @@ +from pathlib import Path + +from astrbot.core.tools.computer_tools import util as computer_util + + +def test_normalize_umo_for_workspace_preserves_normal_session_names() -> None: + assert ( + computer_util.normalize_umo_for_workspace("qq:GroupMessage:1000") + == "qq_GroupMessage_1000" + ) + assert ( + computer_util.normalize_umo_for_workspace("platform.user-1") + == "platform.user-1" + ) + + +def test_normalize_umo_for_workspace_rejects_dot_only_values() -> None: + assert computer_util.normalize_umo_for_workspace(".") == "unknown" + assert computer_util.normalize_umo_for_workspace("..") == "unknown" + assert computer_util.normalize_umo_for_workspace("...") == "unknown" + assert computer_util.normalize_umo_for_workspace(" _._ ") == "unknown" + + +def test_workspace_root_for_dot_only_umo_stays_under_workspaces( + monkeypatch, + tmp_path: Path, +) -> None: + workspaces_root = tmp_path / "workspaces" + monkeypatch.setattr( + computer_util, + "get_astrbot_workspaces_path", + lambda: str(workspaces_root), + ) + + root = computer_util.workspace_root("..") + + assert root == (workspaces_root / "unknown").resolve(strict=False) + assert root.is_relative_to(workspaces_root.resolve(strict=False)) From 9eb14f08442da30809ad0a4ad20e488ceb422240 Mon Sep 17 00:00:00 2001 From: tangtaizong666 Date: Thu, 25 Jun 2026 17:59:31 +0800 Subject: [PATCH 2/2] test: broaden workspace umo safety coverage --- astrbot/core/tools/computer_tools/util.py | 7 +++- tests/unit/test_computer_workspace_util.py | 43 ++++++++++++++++++---- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/astrbot/core/tools/computer_tools/util.py b/astrbot/core/tools/computer_tools/util.py index 67b48a76c2..bd10951131 100644 --- a/astrbot/core/tools/computer_tools/util.py +++ b/astrbot/core/tools/computer_tools/util.py @@ -5,12 +5,15 @@ from astrbot.core.astr_agent_context import AstrAgentContext from astrbot.core.utils.astrbot_path import get_astrbot_workspaces_path +UNKNOWN_WORKSPACE_NAME = "unknown" + def normalize_umo_for_workspace(umo: str) -> str: + """Convert a UMO into a safe per-session workspace directory name.""" normalized = re.sub(r"[^A-Za-z0-9._-]+", "_", umo.strip()) if not normalized.strip("._-"): - return "unknown" - return normalized or "unknown" + return UNKNOWN_WORKSPACE_NAME + return normalized def workspace_root(umo: str) -> Path: diff --git a/tests/unit/test_computer_workspace_util.py b/tests/unit/test_computer_workspace_util.py index e55feada76..2d558090c5 100644 --- a/tests/unit/test_computer_workspace_util.py +++ b/tests/unit/test_computer_workspace_util.py @@ -1,5 +1,7 @@ from pathlib import Path +import pytest + from astrbot.core.tools.computer_tools import util as computer_util @@ -15,13 +17,38 @@ def test_normalize_umo_for_workspace_preserves_normal_session_names() -> None: def test_normalize_umo_for_workspace_rejects_dot_only_values() -> None: - assert computer_util.normalize_umo_for_workspace(".") == "unknown" - assert computer_util.normalize_umo_for_workspace("..") == "unknown" - assert computer_util.normalize_umo_for_workspace("...") == "unknown" - assert computer_util.normalize_umo_for_workspace(" _._ ") == "unknown" + assert ( + computer_util.normalize_umo_for_workspace(".") + == computer_util.UNKNOWN_WORKSPACE_NAME + ) + assert ( + computer_util.normalize_umo_for_workspace("..") + == computer_util.UNKNOWN_WORKSPACE_NAME + ) + assert ( + computer_util.normalize_umo_for_workspace("...") + == computer_util.UNKNOWN_WORKSPACE_NAME + ) + assert ( + computer_util.normalize_umo_for_workspace(" _._ ") + == computer_util.UNKNOWN_WORKSPACE_NAME + ) -def test_workspace_root_for_dot_only_umo_stays_under_workspaces( +@pytest.mark.parametrize( + "unsafe_umo", + [ + ".", + "..", + "...", + " _._ ", + "/", + "//", + " / / ", + ], +) +def test_workspace_root_for_unsafe_umo_stays_under_workspaces( + unsafe_umo: str, monkeypatch, tmp_path: Path, ) -> None: @@ -32,7 +59,9 @@ def test_workspace_root_for_dot_only_umo_stays_under_workspaces( lambda: str(workspaces_root), ) - root = computer_util.workspace_root("..") + root = computer_util.workspace_root(unsafe_umo) - assert root == (workspaces_root / "unknown").resolve(strict=False) + assert root == (workspaces_root / computer_util.UNKNOWN_WORKSPACE_NAME).resolve( + strict=False + ) assert root.is_relative_to(workspaces_root.resolve(strict=False))