fix(isolation): 第6层泄漏——通用会话工作空间总根可枚举所有产线项目

实测发现:工具层隔离生效后,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)
This commit is contained in:
ymq 2026-09-05 15:21:35 +08:00
parent b99fb4cc13
commit 5c28fed040
2 changed files with 35 additions and 0 deletions

View File

@ -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 能力包,

View File

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