25576b0be6
Checks (magui2.0) / python-lint (push) Failing after 1s
Checks (magui2.0) / python-test (push) Failing after 0s
Checks (magui2.0) / frontend-lint (push) Failing after 0s
CodeQL Advanced / Analyze (actions) (push) Failing after 1s
Checks (magui2.0) / python-format (push) Failing after 1s
CodeQL Advanced / Analyze (python) (push) Failing after 0s
Checks (magui2.0) / python-pyright (push) Failing after 1s
Checks (magui2.0) / frontend-format (push) Failing after 1s
Checks (magui2.0) / frontend-typecheck (push) Failing after 0s
Checks (magui2.0) / frontend-test (push) Failing after 2s
CodeQL Advanced / Analyze (javascript-typescript) (push) Failing after 1s
102 lines
3.3 KiB
Python
102 lines
3.3 KiB
Python
"""``_save_message`` must drop the per-slot VNC password before persisting.
|
|
|
|
The wire ``browser_address`` frame carries the live RFB password so noVNC
|
|
can authenticate silently. The persisted copy must not — the frontend
|
|
never reads it from history (the parser ignores ``metadata.password``),
|
|
and stored credentials at rest would be reachable via
|
|
``GET /runs/<id>/messages``.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
from unittest.mock import AsyncMock, MagicMock
|
|
|
|
import pytest
|
|
|
|
from magentic_ui.backend.web.managers.connection import WebSocketManager
|
|
|
|
|
|
def _make_ws_manager() -> tuple[WebSocketManager, MagicMock]:
|
|
"""Build a WebSocketManager with stubbed I/O. Returns (manager, db_manager)."""
|
|
mgr = WebSocketManager.__new__(WebSocketManager)
|
|
fake_run = MagicMock()
|
|
fake_run.session_id = 7
|
|
fake_run.user_id = "u"
|
|
mgr._get_run = AsyncMock(return_value=fake_run)
|
|
db_manager = MagicMock()
|
|
db_manager.upsert = MagicMock()
|
|
mgr.db_manager = db_manager
|
|
return mgr, db_manager
|
|
|
|
|
|
def _saved_config(db_manager: MagicMock) -> dict[str, Any]:
|
|
"""Return the ``config`` dict passed to the most recent ``db.upsert`` call."""
|
|
assert db_manager.upsert.call_count == 1
|
|
db_message = db_manager.upsert.call_args.args[0]
|
|
return db_message.config
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_save_message_strips_browser_address_password() -> None:
|
|
mgr, db = _make_ws_manager()
|
|
message = {
|
|
"source": "web_surfer",
|
|
"content": [],
|
|
"metadata": {
|
|
"type": "browser_address",
|
|
"source": "web_surfer",
|
|
"novnc_port": "6080",
|
|
"playwright_port": "9222",
|
|
"password": "secret-token",
|
|
},
|
|
}
|
|
await mgr._save_message(1, message)
|
|
saved = _saved_config(db)
|
|
assert "password" not in saved["metadata"]
|
|
# Other browser_address fields survive.
|
|
assert saved["metadata"]["novnc_port"] == "6080"
|
|
assert saved["metadata"]["playwright_port"] == "9222"
|
|
# The input dict must not be mutated.
|
|
assert message["metadata"]["password"] == "secret-token"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_save_message_browser_address_without_password_unchanged() -> None:
|
|
mgr, db = _make_ws_manager()
|
|
message = {
|
|
"source": "web_surfer",
|
|
"content": [],
|
|
"metadata": {
|
|
"type": "browser_address",
|
|
"source": "web_surfer",
|
|
"novnc_port": "6080",
|
|
"playwright_port": "9222",
|
|
},
|
|
}
|
|
await mgr._save_message(2, message)
|
|
saved = _saved_config(db)
|
|
assert "password" not in saved["metadata"]
|
|
assert saved["metadata"]["novnc_port"] == "6080"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_save_message_other_types_pass_through() -> None:
|
|
"""Non-browser_address messages are persisted verbatim — even fields
|
|
that happen to be named ``password`` on other message types stay
|
|
untouched. The scrub is targeted, not blanket."""
|
|
mgr, db = _make_ws_manager()
|
|
message = {
|
|
"source": "system",
|
|
"content": [{"type": "text", "text": "ok"}],
|
|
"metadata": {
|
|
"type": "system",
|
|
"source": "system",
|
|
"status": "complete",
|
|
"password": "this-is-not-a-vnc-password",
|
|
},
|
|
}
|
|
await mgr._save_message(3, message)
|
|
saved = _saved_config(db)
|
|
assert saved["metadata"]["password"] == "this-is-not-a-vnc-password"
|