diff --git a/backend/packages/harness/deerflow/persistence/agents/file.py b/backend/packages/harness/deerflow/persistence/agents/file.py index 123c37036d..146c1b4666 100644 --- a/backend/packages/harness/deerflow/persistence/agents/file.py +++ b/backend/packages/harness/deerflow/persistence/agents/file.py @@ -64,6 +64,9 @@ class FileAgentStore(AgentStore): return paths.user_agent_dir(effective_user, name).exists() or paths.agent_dir(name).exists() def get_soul(self, name: str, *, user_id: str | None = None) -> str | None: + # Every other read/write method validates the name; without this, a + # ".." segment reaches resolve_agent_dir and escapes the user's tree. + name = validate_agent_name(name) agent_dir = resolve_agent_dir(name, user_id=user_id) soul_path = agent_dir / SOUL_FILENAME # resolve_agent_dir requires config.yaml; SOUL.md loading does not, so diff --git a/backend/tests/test_agent_storage_backend.py b/backend/tests/test_agent_storage_backend.py index 14db9bcb13..9f46e36945 100644 --- a/backend/tests/test_agent_storage_backend.py +++ b/backend/tests/test_agent_storage_backend.py @@ -426,3 +426,10 @@ def test_get_agent_store_does_not_fallback_when_extensions_config_is_missing(tmp finally: reset_app_config() reset_extensions_config() + + +def test_file_get_soul_rejects_path_traversal(file_home): + store = FileAgentStore() + + with pytest.raises(ValueError, match="Invalid agent name"): + store.get_soul("../../u2/agents/planner", user_id="u1")