diff --git a/astrbot/core/tools/computer_tools/util.py b/astrbot/core/tools/computer_tools/util.py index a3930b4c6a..bd10951131 100644 --- a/astrbot/core/tools/computer_tools/util.py +++ b/astrbot/core/tools/computer_tools/util.py @@ -5,10 +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()) - return normalized or "unknown" + if not normalized.strip("._-"): + 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 new file mode 100644 index 0000000000..2d558090c5 --- /dev/null +++ b/tests/unit/test_computer_workspace_util.py @@ -0,0 +1,67 @@ +from pathlib import Path + +import pytest + +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(".") + == 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 + ) + + +@pytest.mark.parametrize( + "unsafe_umo", + [ + ".", + "..", + "...", + " _._ ", + "/", + "//", + " / / ", + ], +) +def test_workspace_root_for_unsafe_umo_stays_under_workspaces( + unsafe_umo: str, + 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(unsafe_umo) + + assert root == (workspaces_root / computer_util.UNKNOWN_WORKSPACE_NAME).resolve( + strict=False + ) + assert root.is_relative_to(workspaces_root.resolve(strict=False))