fix(agent-runner): align plugin runner runtime boundaries

This commit is contained in:
huanghuoguoguo
2026-06-05 09:35:17 +08:00
parent 36292102f9
commit 121a736e6a
14 changed files with 189 additions and 101 deletions

View File

@@ -1256,7 +1256,13 @@ class TestValidateRunAuthorizationHelper:
mock_ap = MagicMock()
mock_ap.logger = MagicMock()
session, error = await _validate_run_authorization('run_validate_test_helper', 'model', 'model_001', mock_ap)
session, error = await _validate_run_authorization(
'run_validate_test_helper',
'model',
'model_001',
mock_ap,
caller_plugin_identity='test/runner',
)
# Should return session, no error
assert session is not None
@@ -1310,6 +1316,7 @@ class TestValidateRunAuthorizationHelper:
'model',
'model_999', # Not in resources
mock_ap,
caller_plugin_identity='test/runner',
)
# Should return no session, error response
@@ -1342,7 +1349,13 @@ class TestValidateRunAuthorizationHelper:
mock_ap = MagicMock()
mock_ap.logger = MagicMock()
session, error = await _validate_run_authorization('run_tool_test_helper', 'tool', 'web_search', mock_ap)
session, error = await _validate_run_authorization(
'run_tool_test_helper',
'tool',
'web_search',
mock_ap,
caller_plugin_identity='test/runner',
)
assert session is not None
assert error is None
@@ -1371,7 +1384,13 @@ class TestValidateRunAuthorizationHelper:
mock_ap = MagicMock()
mock_ap.logger = MagicMock()
session, error = await _validate_run_authorization('run_kb_test_helper', 'knowledge_base', 'kb_001', mock_ap)
session, error = await _validate_run_authorization(
'run_kb_test_helper',
'knowledge_base',
'kb_001',
mock_ap,
caller_plugin_identity='test/runner',
)
assert session is not None
assert error is None
@@ -1548,7 +1567,13 @@ class TestRealActionHandlerSimulation:
mock_ap.logger = MagicMock()
# Step 1: Validate authorization
session, error = await _validate_run_authorization('run_invoke_llm_flow_sim', 'model', 'model_001', mock_ap)
session, error = await _validate_run_authorization(
'run_invoke_llm_flow_sim',
'model',
'model_001',
mock_ap,
caller_plugin_identity='test/runner',
)
# Should pass authorization
assert session is not None
@@ -1582,7 +1607,13 @@ class TestRealActionHandlerSimulation:
mock_ap.logger.warning = MagicMock()
# Try to access unauthorized model
session, error = await _validate_run_authorization('run_reject_model_sim', 'model', 'model_999', mock_ap)
session, error = await _validate_run_authorization(
'run_reject_model_sim',
'model',
'model_999',
mock_ap,
caller_plugin_identity='test/runner',
)
# Should reject
assert session is None
@@ -1641,7 +1672,13 @@ class TestStoragePermissionValidation:
mock_ap = MagicMock()
mock_ap.logger = MagicMock()
session, error = await _validate_run_authorization('run_plugin_storage_auth', 'storage', 'plugin', mock_ap)
session, error = await _validate_run_authorization(
'run_plugin_storage_auth',
'storage',
'plugin',
mock_ap,
caller_plugin_identity='test/runner',
)
assert session is not None
assert error is None
@@ -1670,7 +1707,13 @@ class TestStoragePermissionValidation:
mock_ap.logger = MagicMock()
mock_ap.logger.warning = MagicMock()
session, error = await _validate_run_authorization('run_plugin_storage_denied', 'storage', 'plugin', mock_ap)
session, error = await _validate_run_authorization(
'run_plugin_storage_denied',
'storage',
'plugin',
mock_ap,
caller_plugin_identity='test/runner',
)
assert session is None
assert error is not None
@@ -1700,7 +1743,11 @@ class TestStoragePermissionValidation:
mock_ap.logger = MagicMock()
session, error = await _validate_run_authorization(
'run_workspace_storage_auth', 'storage', 'workspace', mock_ap
'run_workspace_storage_auth',
'storage',
'workspace',
mock_ap,
caller_plugin_identity='test/runner',
)
assert session is not None
@@ -1731,7 +1778,11 @@ class TestStoragePermissionValidation:
mock_ap.logger.warning = MagicMock()
session, error = await _validate_run_authorization(
'run_workspace_storage_denied', 'storage', 'workspace', mock_ap
'run_workspace_storage_denied',
'storage',
'workspace',
mock_ap,
caller_plugin_identity='test/runner',
)
assert session is None
@@ -1769,7 +1820,13 @@ class TestFilePermissionValidation:
mock_ap = MagicMock()
mock_ap.logger = MagicMock()
session, error = await _validate_run_authorization('run_file_auth', 'file', 'file_001', mock_ap)
session, error = await _validate_run_authorization(
'run_file_auth',
'file',
'file_001',
mock_ap,
caller_plugin_identity='test/runner',
)
assert session is not None
assert error is None
@@ -1803,6 +1860,7 @@ class TestFilePermissionValidation:
'file',
'file_999', # Not in resources
mock_ap,
caller_plugin_identity='test/runner',
)
assert session is None
@@ -1891,9 +1949,8 @@ class TestCallerPluginIdentityValidation:
await registry.unregister('run_identity_mismatch')
@pytest.mark.asyncio
async def test_no_caller_identity_allowed(self):
"""_validate_run_authorization allows when caller_plugin_identity not provided."""
# Unscoped plugin path: if caller_plugin_identity is None, skip identity check
async def test_run_id_requires_caller_identity(self):
"""Run-scoped authorization requires caller_plugin_identity."""
from langbot.pkg.agent.runner.session_registry import get_session_registry
registry = get_session_registry()
@@ -1912,18 +1969,17 @@ class TestCallerPluginIdentityValidation:
mock_ap = MagicMock()
mock_ap.logger = MagicMock()
# caller_plugin_identity not provided (None)
session, error = await _validate_run_authorization(
'run_no_caller_identity',
'model',
'model_001',
mock_ap,
caller_plugin_identity=None, # Not provided
caller_plugin_identity=None,
)
# Should pass (backward compat)
assert session is not None
assert error is None
assert session is None
assert error is not None
assert 'caller_plugin_identity is required' in error.message
await registry.unregister('run_no_caller_identity')

View File

@@ -54,7 +54,8 @@ def test_classify_python_workspace_detects_package_and_requirements():
def test_wrap_python_command_with_env_contains_bootstrap_and_command():
command = wrap_python_command_with_env('python script.py')
assert 'python -m venv "$_LB_VENV_DIR"' in command
assert '_LB_SYSTEM_PYTHON="$(command -v python3 || command -v python || true)"' in command
assert '"$_LB_SYSTEM_PYTHON" -m venv "$_LB_VENV_DIR"' in command
assert 'export VIRTUAL_ENV="$_LB_VENV_DIR"' in command
assert command.rstrip().endswith('python script.py')

View File

@@ -494,6 +494,47 @@ class TestBuildBoxProcessPayload:
assert payload['args'] == ['/opt/other/server.py', '--flag']
# ── Python Workspace Preparation ────────────────────────────────────
class TestPythonWorkspacePreparation:
def test_requirements_workspace_uses_venv_bootstrap(self, mcp_module, tmp_path):
host_path = tmp_path / 'mcp-source'
host_path.mkdir()
(host_path / 'requirements.txt').write_text('mcp==1.26.0\n', encoding='utf-8')
command = mcp_module.BoxStdioSessionRuntime.detect_install_command(
str(host_path),
'/workspace/.mcp/u1/workspace',
)
assert command is not None
assert '_LB_SYSTEM_PYTHON="$(command -v python3 || command -v python || true)"' in command
assert '"$_LB_SYSTEM_PYTHON" -m venv "$_LB_VENV_DIR"' in command
assert 'python -m pip install -r "/workspace/.mcp/u1/workspace/requirements.txt"' in command
assert 'pip install --no-cache-dir -r' not in command
def test_process_payload_can_start_from_prepared_python_env(self, mcp_module):
payload = {
'command': 'python',
'args': ['/workspace/.mcp/u1/workspace/server.py'],
'env': {},
'cwd': '/workspace/.mcp/u1/workspace',
}
wrapped = mcp_module.BoxStdioSessionRuntime._wrap_process_payload_with_python_env(
payload,
'/workspace/.mcp/u1/workspace',
)
assert wrapped['command'] == 'sh'
assert wrapped['args'][0] == '-lc'
assert 'export VIRTUAL_ENV=/workspace/.mcp/u1/workspace/.venv' in wrapped['args'][1]
assert 'export PATH=/workspace/.mcp/u1/workspace/.venv/bin:$PATH' in wrapped['args'][1]
assert 'exec python /workspace/.mcp/u1/workspace/server.py' in wrapped['args'][1]
assert wrapped['cwd'] == '/workspace/.mcp/u1/workspace'
# ── get_runtime_info_dict ───────────────────────────────────────────

View File

@@ -212,7 +212,8 @@ class TestSkillPathHelpers:
command = wrap_skill_command_with_python_env('python scripts/run.py')
assert 'python -m venv "$_LB_VENV_DIR"' in command
assert '_LB_SYSTEM_PYTHON="$(command -v python3 || command -v python || true)"' in command
assert '"$_LB_SYSTEM_PYTHON" -m venv "$_LB_VENV_DIR"' in command
assert 'export VIRTUAL_ENV="$_LB_VENV_DIR"' in command
assert command.rstrip().endswith('python scripts/run.py')