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}。