From 5c28fed04003378559d31ee1f8363dd68ab85e0d Mon Sep 17 00:00:00 2001 From: ymq Date: Sat, 5 Sep 2026 15:21:35 +0800 Subject: [PATCH] =?UTF-8?q?fix(isolation):=20=E7=AC=AC6=E5=B1=82=E6=B3=84?= =?UTF-8?q?=E6=BC=8F=E2=80=94=E2=80=94=E9=80=9A=E7=94=A8=E4=BC=9A=E8=AF=9D?= =?UTF-8?q?=E5=B7=A5=E4=BD=9C=E7=A9=BA=E9=97=B4=E6=80=BB=E6=A0=B9=E5=8F=AF?= =?UTF-8?q?=E6=9E=9A=E4=B8=BE=E6=89=80=E6=9C=89=E4=BA=A7=E7=BA=BF=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 实测发现:工具层隔离生效后,LLM 改用 list_files/run_command 浏览 WORKSPACE_BASE 总根({base}/{org}/{产线}/…全量可见),从目录名 枚举出所有产线的全部项目。 修复: - workspace.py 新增 generic_workspace_dir():通用会话专属目录 _general/{uid} - agent_loop_v2: generic 会话文件根改专属目录(_resolve_ws_path 越界保护圈死) - agent_loop_v2: run_command 对通用会话执行层拒绝(shell 无法圈禁在目录内) - schema 层剔除 shell 类工具(配合 pipeline-core) --- pipeline_service/agent_loop_v2.py | 23 +++++++++++++++++++++++ pipeline_service/workspace.py | 12 ++++++++++++ 2 files changed, 35 insertions(+) diff --git a/pipeline_service/agent_loop_v2.py b/pipeline_service/agent_loop_v2.py index b6f2ff2..09a7d44 100644 --- a/pipeline_service/agent_loop_v2.py +++ b/pipeline_service/agent_loop_v2.py @@ -55,6 +55,12 @@ _PROJECT_TOOL_NAMES = { "pause_project", "resume_project", "delete_project", } +# 产线耦合工具(2026-09-05 第 6 层泄漏修复):run_command 的 shell 无法圈禁 +# 在目录内(cwd 只是起点,命令可 cd/绝对路径访问全工作空间),纯通用会话 +# 一律剔除。read_file/list_files/search_files/write_file 受 _resolve_ws_path +# 越界保护,配合通用会话专属工作目录(_general/{user})天然隔离,保留。 +_GENERIC_DENIED_TOOLS = {"run_command"} + # ── 默认工具定义(在 pipeline-core 未加载时使用)── @@ -457,6 +463,18 @@ class AgentExecutor: # 6. 项目空间键:generic→'general'(通用助手),否则用真实 pipeline_id(产线之间隔离) self.space = GENERAL_SPACE if self.generic else (self.pipeline_id or GENERAL_SPACE) + # 7. 通用会话专属工作目录(2026-09-05 第 6 层泄漏修复):默认文件根是 + # WORKSPACE_BASE 总根({base}/{org}/{产线}/… 全量可见),LLM 用 + # list_files/read_file 就能从目录名枚举所有产线的项目。通用会话改用 + # 用户专属目录 _general/{user_id},_resolve_ws_path 越界保护把它圈死。 + if self.generic: + from .workspace import generic_workspace_dir + self.workspace_dir = generic_workspace_dir(self.user_id) + try: + os.makedirs(self.workspace_dir, exist_ok=True) + except Exception: + pass + async def _resolve_skills_base_dir(self): """技能根目录 = 全局 skills/(单一技能树,所有机构共享读,2026-08-21 重构)。""" try: @@ -809,6 +827,11 @@ class AgentExecutor: if self.generic and tool_name in _PROJECT_TOOL_NAMES: return ("FAIL: 当前是通用助手会话(未挂产线插件),不提供项目管理能力," "无法查看或操作任何产线的项目。") + # 产线耦合工具拦截(2026-09-05 第 6 层):run_command 的 shell 无法圈禁 + # 在工作目录内,通用会话一律拒绝。 + if self.generic and tool_name in _GENERIC_DENIED_TOOLS: + return ("FAIL: 当前是通用助手会话,不提供命令执行能力。" + "如需处理文件,可直接上传文件,我会读取上传内容作答。") # 通用内建工具映射(core 内核,产线无关)。 # 产线专属工具(create_task/diagnose_project 等)已迁移到 sdlc_ability 能力包, diff --git a/pipeline_service/workspace.py b/pipeline_service/workspace.py index a234317..1826469 100644 --- a/pipeline_service/workspace.py +++ b/pipeline_service/workspace.py @@ -15,6 +15,18 @@ WORKSPACE_BASE = '/d/pipeline/workspaces' GENERAL_SPACE = 'general' +def generic_workspace_dir(user_id: str) -> str: + """纯通用会话的专属工作目录(2026-09-05 产线隔离第 6 层修复)。 + + 通用会话没有项目,文件工具的文件根若用 WORKSPACE_BASE 总根, + LLM 可浏览 {base}/{org}/{产线}/… 从目录名枚举所有产线的项目。 + 改为每用户专属目录 _general/{user_id},配合 _resolve_ws_path 的 + 越界保护,把通用会话的文件视野圈死在自己的目录内。 + """ + uid = (user_id or 'anonymous').replace('/', '_').replace('..', '_') or 'anonymous' + return os.path.join(WORKSPACE_BASE, '_general', uid) + + def build_workspace_path(workspace_base, org_id, space, name, project_id=''): """构建项目 workspace 路径:{base}/{org_id}/{space}/{name}。