fix(file-manager): direct 执行环境下文件工具不再误拦授权范围外路径

run_command 是否走沙箱只看执行环境,而 read_file/write_file/edit_file 是
进程内文件操作不走 OS 沙箱,唯一防线是 FileManager._ensure_host_access
的授权路径检查,但它不知道执行环境,导致完全访问权限(direct)下仍按
授权列表拦截。现 FileManager 同步 host_execution_mode,direct 时与
run_command 语义对齐直接放行,sandbox 模式检查保持不变。
This commit is contained in:
JOJO 2026-08-12 11:23:27 +08:00
parent 8da4a596eb
commit 011f9e14bc
3 changed files with 14 additions and 0 deletions

View File

@ -259,6 +259,8 @@ class MainTerminal(MainTerminalCommandMixin, MainTerminalContextMixin, MainTermi
self.terminal_manager.set_host_execution_mode(mode)
if getattr(self, "sub_agent_manager", None):
self.sub_agent_manager.set_host_execution_mode(mode)
if getattr(self, "file_manager", None):
self.file_manager.set_host_execution_mode(mode)
def _init_host_network_permission(self):
default_permission = str(HOST_SANDBOX_NETWORK_PERMISSION or "restricted").strip().lower()

View File

@ -54,8 +54,15 @@ class FileManagerBase:
self.container_session: Optional["ContainerHandle"] = None
self._container_proxy: Optional[ContainerFileProxy] = None
self._data_dir: Optional[str] = data_dir
# 宿主机执行环境sandbox / direct由主终端同步
# direct 时与 run_command 对齐:不走路径授权检查(不套沙箱语义)。
self.host_execution_mode: str = "sandbox"
self.set_container_session(container_session)
def set_host_execution_mode(self, mode: str) -> None:
normalized = str(mode or "").strip().lower()
self.host_execution_mode = "direct" if normalized == "direct" else "sandbox"
def _load_personalization_config(self) -> Optional[Dict]:
"""加载个性化配置"""
if not self._data_dir:

View File

@ -175,6 +175,11 @@ class PathMixin:
def _ensure_host_access(self, full_path: Path, access: str) -> Tuple[bool, str]:
if not self._is_host_mode():
return True, ""
# 执行环境为 direct完全访问权限run_command 不套沙箱、可读写任意路径;
# read_file/write_file/edit_file 为进程内文件操作,本就不走 OS 沙箱,此处与
# run_command 语义对齐直接放行。sandbox 模式下保持授权范围检查不变。
if getattr(self, "host_execution_mode", "sandbox") == "direct":
return True, ""
check_target = full_path
if access == "write" and not full_path.exists():
check_target = full_path.parent.resolve()