"""乾坤沙箱工作区文件读取(百工执行产物预览)。 隔离前缀语义:workspace//(subject=character_id,一角色一持久工作区); subject 经白名单替换杜绝路径注入,沙箱端另有 canonicalize 校验兜底。 """ from __future__ import annotations import re import uuid from app.integrations.sandbox.auth import AuthorizationContext from app.integrations.sandbox.client import SandboxClient from app.integrations.sandbox.models import ( DirEntry, FileListRequest, FileReadRequest, FileReadResponse, ) _ISOLATE_SAFE = re.compile(r"[^A-Za-z0-9_.-]") def isolate_workdir( base: str | None, *, subject_id: str | None, run_id: str | None, trace_id: str | None, ) -> str: """把工作区根目录隔离到主体级子目录,防跨主体信息泄漏。""" root = base or "workspace" key = subject_id or run_id or trace_id or f"adhoc_{uuid.uuid4().hex[:8]}" safe = _ISOLATE_SAFE.sub("_", key).strip("._-") or "adhoc" return f"{root}/{safe}" async def read_sandbox_file( sandbox: SandboxClient, path: str, *, run_id: str | None = None, character_id: str | None = None, trace_id: str | None = None, isolated: bool = True, ) -> FileReadResponse: effective = path if isolated: workdir = isolate_workdir("workspace", subject_id=character_id, run_id=run_id, trace_id=trace_id) effective = f"{workdir}/{path.lstrip('/')}" return await sandbox.file_read( FileReadRequest(path=effective), AuthorizationContext( trace_id=trace_id or str(uuid.uuid4()), risk_level="L0", tool="file_read", character_id=character_id, run_id=run_id, ), ) async def list_sandbox_dir( sandbox: SandboxClient, path: str = ".", *, run_id: str | None = None, character_id: str | None = None, trace_id: str | None = None, isolated: bool = True, ) -> list[DirEntry]: effective = path if isolated: workdir = isolate_workdir("workspace", subject_id=character_id, run_id=run_id, trace_id=trace_id) effective = f"{workdir}/{path.lstrip('/')}" result = await sandbox.file_list( FileListRequest(path=effective), AuthorizationContext( trace_id=trace_id or str(uuid.uuid4()), risk_level="L0", tool="file_list", character_id=character_id, run_id=run_id, ), ) return result.entries