fix: code review — critical and important fixes [v0.2.1]
- inject_message now uses session's event_bus (BOSS_TURN reaches WebSocket/SSE) - asyncio.get_running_loop() replaces deprecated get_event_loop() - API keys masked in /api/providers response - ServerSession (renamed from Session) with cleanup() for handler teardown - Path traversal protection in _resolve_scenario_path - XSS fix: escapeHtml(d.name) in web UI - Duplicate imports removed, os/json moved to module level - WebSocket handler restructured for reliable cleanup - New tests: cleanup, provider masking, path traversal Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -5,10 +5,11 @@ from __future__ import annotations
|
||||
import asyncio
|
||||
|
||||
import pytest
|
||||
from fastapi import HTTPException
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from meeting_room.events import AsyncEventBridge, EventEmitter
|
||||
from meeting_room.server import Session, SessionStore, app, store
|
||||
from meeting_room.server import ServerSession, SessionStore, app, store
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
@@ -113,13 +114,53 @@ class TestRolesAndProviders:
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestSession:
|
||||
class TestServerSession:
|
||||
def test_build_engine(self) -> None:
|
||||
config = _minimal_config()
|
||||
session = Session("test-id", config, _minimal_discussion_config())
|
||||
session = ServerSession("test-id", config, _minimal_discussion_config())
|
||||
engine = session.build_engine()
|
||||
assert engine is not None
|
||||
|
||||
def test_cleanup_removes_collect_handler(self) -> None:
|
||||
from meeting_room.events import AGENT_MESSAGE, BOSS_TURN, AGENT_ERROR
|
||||
config = _minimal_config()
|
||||
session = ServerSession("test-id", config, _minimal_discussion_config())
|
||||
# Simulate what start_discussion does
|
||||
def collect_message(event):
|
||||
pass
|
||||
session._collect_handler = collect_message
|
||||
session.event_bus.on(AGENT_MESSAGE, collect_message)
|
||||
session.event_bus.on(BOSS_TURN, collect_message)
|
||||
session.event_bus.on(AGENT_ERROR, collect_message)
|
||||
assert len(session.event_bus.handlers(AGENT_MESSAGE)) == 1
|
||||
session.cleanup()
|
||||
assert len(session.event_bus.handlers(AGENT_MESSAGE)) == 0
|
||||
|
||||
|
||||
class TestProviderMasking:
|
||||
def test_api_keys_masked(self) -> None:
|
||||
from meeting_room.server import _mask_api_keys
|
||||
providers = {
|
||||
"routerai": {"base_url": "https://api.example.com", "api_key": "sk-1234567890abcdef"},
|
||||
}
|
||||
masked = _mask_api_keys(providers)
|
||||
assert masked["routerai"]["api_key"] == "sk-1****"
|
||||
assert masked["routerai"]["base_url"] == "https://api.example.com"
|
||||
|
||||
def test_empty_key(self) -> None:
|
||||
from meeting_room.server import _mask_api_keys
|
||||
providers = {"test": {"base_url": "https://x.com", "api_key": ""}}
|
||||
masked = _mask_api_keys(providers)
|
||||
assert masked["test"]["api_key"] == ""
|
||||
|
||||
|
||||
class TestPathTraversal:
|
||||
def test_reject_parent_dir(self) -> None:
|
||||
from meeting_room.server import _resolve_scenario_path
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
_resolve_scenario_path("../../etc/passwd")
|
||||
assert exc_info.value.status_code == 400
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Helper
|
||||
|
||||
Reference in New Issue
Block a user