Compare commits

..

1 Commits

Author SHA1 Message Date
huanghuoguoguo
aa4b5d6732 fix(plugin): validate plugin id format 2026-05-16 10:36:58 +08:00
3 changed files with 29 additions and 39 deletions

View File

@@ -35,10 +35,6 @@ from ..core import taskmgr
from ..entity.persistence import plugin as persistence_plugin from ..entity.persistence import plugin as persistence_plugin
class PluginRuntimeNotConnectedError(RuntimeError):
"""Raised when plugin runtime operations are requested before connection."""
class PluginRuntimeConnector: class PluginRuntimeConnector:
"""Plugin runtime connector""" """Plugin runtime connector"""
@@ -196,7 +192,7 @@ class PluginRuntimeConnector:
async def ping_plugin_runtime(self): async def ping_plugin_runtime(self):
if not hasattr(self, 'handler'): if not hasattr(self, 'handler'):
raise PluginRuntimeNotConnectedError('Plugin runtime is not connected') raise Exception('Plugin runtime is not connected')
return await self.handler.ping() return await self.handler.ping()
@@ -637,11 +633,12 @@ class PluginRuntimeConnector:
Raises: Raises:
ValueError: If plugin_id is not in the expected 'author/name' format. ValueError: If plugin_id is not in the expected 'author/name' format.
""" """
if '/' not in plugin_id: segments = plugin_id.split('/')
if len(segments) != 2 or not all(segments):
raise ValueError( raise ValueError(
f"Invalid plugin_id format: '{plugin_id}'. Expected 'author/name' format (e.g. 'langbot/rag-engine')." f"Invalid plugin_id format: '{plugin_id}'. Expected 'author/name' format (e.g. 'langbot/rag-engine')."
) )
return plugin_id.split('/', 1) return segments[0], segments[1]
async def call_rag_ingest(self, plugin_id: str, context_data: dict[str, Any]) -> dict[str, Any]: async def call_rag_ingest(self, plugin_id: str, context_data: dict[str, Any]) -> dict[str, Any]:
"""Call plugin to ingest document. """Call plugin to ingest document.

View File

@@ -1,32 +0,0 @@
from __future__ import annotations
from types import SimpleNamespace
from unittest.mock import AsyncMock
import pytest
from langbot.pkg.plugin.connector import PluginRuntimeConnector, PluginRuntimeNotConnectedError
def make_connector() -> PluginRuntimeConnector:
app = SimpleNamespace(instance_config=SimpleNamespace(data={'plugin': {'enable': True}}))
return PluginRuntimeConnector(app, AsyncMock())
@pytest.mark.asyncio
async def test_ping_plugin_runtime_raises_specific_error_when_not_connected():
connector = make_connector()
with pytest.raises(PluginRuntimeNotConnectedError, match='Plugin runtime is not connected'):
await connector.ping_plugin_runtime()
@pytest.mark.asyncio
async def test_ping_plugin_runtime_delegates_to_connected_handler():
connector = make_connector()
connector.handler = SimpleNamespace(ping=AsyncMock(return_value='pong'))
result = await connector.ping_plugin_runtime()
assert result == 'pong'
connector.handler.ping.assert_awaited_once()

View File

@@ -0,0 +1,25 @@
"""Test plugin ID parsing validation."""
import pytest
from src.langbot.pkg.plugin.connector import PluginRuntimeConnector
def test_parse_plugin_id_accepts_author_name():
assert PluginRuntimeConnector._parse_plugin_id('langbot/rag-engine') == ('langbot', 'rag-engine')
@pytest.mark.parametrize(
'plugin_id',
[
'',
'author',
'author/',
'/name',
'author/name/extra',
'/',
],
)
def test_parse_plugin_id_rejects_malformed_ids(plugin_id):
with pytest.raises(ValueError, match='Expected'):
PluginRuntimeConnector._parse_plugin_id(plugin_id)