Files
xerrors--yuxi/backend/test/unit/services/test_viewer_filesystem_service.py
wehub-resource-sync 1443d3fdf9
Ruff Format Check / Ruff Format & Lint (push) Failing after 7m39s
Deploy VitePress site to Pages / build (push) Failing after 9m11s
Deploy VitePress site to Pages / Deploy (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:32:26 +08:00

85 lines
3.0 KiB
Python

from __future__ import annotations
from pathlib import Path
from types import SimpleNamespace
import pytest
from fastapi import HTTPException
from yuxi.agents.backends.sandbox import paths as sandbox_paths
from yuxi.services import viewer_filesystem_service as svc
from yuxi.services import workspace_service
def test_resolve_local_user_data_path_blocks_upload_symlink_escape(tmp_path: Path, monkeypatch) -> None:
monkeypatch.setattr(sandbox_paths.conf, "save_dir", str(tmp_path))
thread_id = "thread-1"
uid = "user-1"
sandbox_paths.ensure_thread_dirs(thread_id, uid)
outside_file = tmp_path / "outside.txt"
outside_file.write_text("outside", encoding="utf-8")
escape_link = sandbox_paths.sandbox_uploads_dir(thread_id) / "escape.txt"
escape_link.symlink_to(outside_file)
with pytest.raises(HTTPException) as exc_info:
svc._resolve_local_user_data_path(thread_id, uid, "/home/gem/user-data/uploads/escape.txt")
assert exc_info.value.status_code == 403
def test_list_local_entries_skips_symlink_escape(tmp_path: Path, monkeypatch) -> None:
monkeypatch.setattr(sandbox_paths.conf, "save_dir", str(tmp_path))
thread_id = "thread-1"
uid = "user-1"
sandbox_paths.ensure_thread_dirs(thread_id, uid)
uploads_dir = sandbox_paths.sandbox_uploads_dir(thread_id)
(uploads_dir / "safe.txt").write_text("safe", encoding="utf-8")
outside_file = tmp_path / "outside.txt"
outside_file.write_text("outside", encoding="utf-8")
(uploads_dir / "escape.txt").symlink_to(outside_file)
entries = svc._list_local_entries(thread_id, uid, uploads_dir)
assert {entry["path"] for entry in entries} == {"/home/gem/user-data/uploads/safe.txt"}
@pytest.mark.asyncio
async def test_read_viewer_workspace_office_file_returns_pdf_preview(
tmp_path: Path,
monkeypatch,
) -> None:
monkeypatch.setattr(sandbox_paths.conf, "save_dir", str(tmp_path))
thread_id = "thread-1"
uid = "user-1"
user = SimpleNamespace(uid=uid)
sandbox_paths.ensure_thread_dirs(thread_id, uid)
target = sandbox_paths.sandbox_workspace_dir(thread_id, uid) / "slides.pptx"
target.write_bytes(b"presentation")
async def fake_resolve_viewer_state(**kwargs):
return None, None, []
async def fake_convert(filename: str, content: bytes) -> bytes:
assert filename == "slides.pptx"
assert content == b"presentation"
return b"%PDF-1.4\npreview"
monkeypatch.setattr(svc, "_resolve_viewer_state", fake_resolve_viewer_state)
monkeypatch.setattr(workspace_service, "convert_office_to_pdf", fake_convert)
response = await svc.read_viewer_file_content(
thread_id=thread_id,
path="/home/gem/user-data/workspace/slides.pptx",
current_user=user,
db=None,
)
body = b""
async for chunk in response.body_iterator:
body += chunk
assert response.media_type == "application/pdf"
assert response.headers["x-yuxi-preview-type"] == "pdf"
assert body == b"%PDF-1.4\npreview"