Files
hermes-webui/tests/test_session_import_workspace_validation.py
T
2026-05-11 10:46:17 +08:00

106 lines
3.0 KiB
Python

import io
import json
from pathlib import Path
from urllib.parse import urlparse
from api.config import DEFAULT_WORKSPACE, SESSION_DIR
from api.models import get_session
from api.routes import _handle_file_read, _handle_session_import
from api.workspace import resolve_trusted_workspace
class _DummyHandler:
def __init__(self):
self.status = None
self.response_headers = []
self.headers = {}
self.wfile = io.BytesIO()
self.command = "GET"
self.path = "/"
def send_response(self, status):
self.status = status
def send_header(self, key, value):
self.response_headers.append((key, value))
def end_headers(self):
pass
def json_body(self):
return json.loads(self.wfile.getvalue().decode("utf-8"))
def test_session_import_rejects_blocked_root_workspace():
handler = _DummyHandler()
_handle_session_import(
handler,
{
"title": "blocked import",
"workspace": "/",
"model": "test",
"messages": [],
},
)
assert handler.status == 400
assert "system directory" in handler.json_body()["error"]
def test_session_import_rejects_non_path_workspace_value():
handler = _DummyHandler()
_handle_session_import(
handler,
{
"title": "invalid import",
"workspace": {"not": "a path"},
"model": "test",
"messages": [],
},
)
assert handler.status == 400
assert handler.json_body()["error"]
def test_imported_session_file_read_stays_under_validated_workspace():
SESSION_DIR.mkdir(parents=True, exist_ok=True)
workspace = Path(DEFAULT_WORKSPACE)
workspace.mkdir(parents=True, exist_ok=True)
(workspace / "allowed.txt").write_text("allowed", encoding="utf-8")
import_handler = _DummyHandler()
_handle_session_import(
import_handler,
{
"title": "valid import",
"workspace": str(workspace),
"model": "test",
"messages": [],
},
)
assert import_handler.status == 200
sid = import_handler.json_body()["session"]["session_id"]
assert get_session(sid).workspace == str(resolve_trusted_workspace(workspace))
read_handler = _DummyHandler()
_handle_file_read(read_handler, urlparse(f"/api/file?session_id={sid}&path=allowed.txt"))
assert read_handler.status == 200
assert read_handler.json_body()["content"] == "allowed"
def test_resolver_would_reject_imported_root_before_file_read():
# Regression guard for the original issue shape: '/' must be rejected at
# import time rather than becoming a session workspace that makes
# Path('/')-relative reads like etc/hosts reachable through /api/file.
try:
resolve_trusted_workspace(Path("/"))
except ValueError as exc:
assert "system directory" in str(exc)
else: # pragma: no cover - this would weaken the security invariant
raise AssertionError("root workspace unexpectedly accepted")