diff --git a/backend/packages/harness/deerflow/client.py b/backend/packages/harness/deerflow/client.py index ece067c452..0203dc965d 100644 --- a/backend/packages/harness/deerflow/client.py +++ b/backend/packages/harness/deerflow/client.py @@ -254,7 +254,7 @@ class DeerFlowClient: self._checkpoint_channel_mode = freeze_checkpoint_channel_mode(self._app_config.database.checkpoint_channel_mode) self._checkpoint_snapshot_frequency = freeze_checkpoint_snapshot_frequency(self._app_config.database.checkpoint_delta.snapshot_frequency) - if agent_name is not None and not AGENT_NAME_PATTERN.match(agent_name): + if agent_name is not None and not AGENT_NAME_PATTERN.fullmatch(agent_name): raise ValueError(f"Invalid agent name '{agent_name}'. Must match pattern: {AGENT_NAME_PATTERN.pattern}") self._checkpointer = checkpointer diff --git a/backend/tests/test_client.py b/backend/tests/test_client.py index b49167c9d6..daccabdbf2 100644 --- a/backend/tests/test_client.py +++ b/backend/tests/test_client.py @@ -121,6 +121,13 @@ class TestClientInit: with pytest.raises(ValueError, match="Invalid agent name"): DeerFlowClient(agent_name="../path/traversal") + def test_agent_name_with_trailing_newline_rejected(self, mock_app_config): + with patch("deerflow.client.get_app_config", return_value=mock_app_config): + # The client's own guard must reject this at construction; the + # memory store's later fullmatch check uses different phrasing. + with pytest.raises(ValueError, match="Must match pattern"): + DeerFlowClient(agent_name="reviewer\n") + def test_custom_config_path(self, mock_app_config): with ( patch("deerflow.client.reload_app_config") as mock_reload,