DeepSeek Harness 插件

zhang8019/dsh-permission-matrix

Star 数 ★ 0 分类 安全与权限 收录于 2026-09-09

把 DSH 权限拆成 9 个可选预设(3 种沙箱 × 4 种审批),规则 + LLM 双通道四档风险分级,高风险操作需输入批准密码才放行,附 JSONL 审计日志。

安装

# Release 预构建包

dsh plugin --profile web add "https://github.com/zhang8019/dsh-permission-matrix/releases/latest/download/dsh-permission-matrix.tgz"

# GitHub 源码(首次需按提示配置 allowBuilds 构建授权后重试)

dsh plugin --profile web add github:zhang8019/dsh-permission-matrix

装任何插件都等于在你的机器上跑第三方代码,权限和你本人一样大——能读你的文件、用你的凭据、访问网络,工具审批管不到它。GitHub 来源的插件还会在安装时执行构建脚本——pnpm 默认拦截,所以安装可能停在 ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWEDERR_PNPM_IGNORED_BUILDS;dsh 会打印出需要添加的确切键名,把它加进该 profile 的 pnpm-workspace.yamlallowBuilds 下,重跑一次即可装上。放行构建本身就是一次信任判断:请只安装可信来源,并尽量锁定 commit(github:owner/repo#sha)。

README

把 DSH 的权限从「几个固定档位」拆成两个独立维度并重新组合:

  • 沙箱(读写边界):工作区只读 / 工作区读写 / 全电脑读写
  • 审批(操作批准方式):人工审批 / 自动同意 / 自动风险审批 / 自动拒绝

两者组合出 9 个可切换的执行类型,外加全局默认预设LLM 机器人默认预设两个维度——社交渠道(QQ / 微信 / 企微 / Telegram…)的会话可以用与 Web 会话不同的权限档位。

取代 dsh-auto-classifier 的单点 auto 预设:本插件保留它的核心思路(规则 + LLM 裁判 + 硬拒绝),但把预设表拆成完整的组合矩阵。


9 个执行类型

# 预设 id 显示名 沙箱 审批 行为
1 ro-human 工作区只读权限 + 人工审批 read-only ask 一切写入被沙箱拒绝,越权操作弹人工确认
2 ro-deny 工作区只读权限 + 自动拒绝 read-only never 写入一律拒绝,无人审批环节(真正只读)
3 ww-human 工作区读写权限 + 人工审批 workspace-write ask 工作区内可写;越界操作弹人工确认(默认)
4 ww-classify 工作区读写权限 + 自动风险审批 workspace-write ask 工作区内可写;越界与高风险操作按风险分级裁决
5 ww-deny 工作区读写权限 + 自动拒绝 workspace-write never 工作区内可写;越界操作一律拒绝
6 fa-human 全电脑读写权限 + 人工审批 danger-full-access ask 文件全放行;需批准的操作交给人工
7 fa-deny 全电脑读写权限 + 自动拒绝 danger-full-access never 文件全放行;需批准的操作一律拒绝
8 fa-auto 全电脑读写权限 + 自动同意 danger-full-access ask 文件全放行;需批准的操作自动放行(硬拒绝保护)
9 fa-classify 全电脑读写权限 + 自动风险审批 danger-full-access ask 文件全放行;操作按风险分级裁决

为什么是 9 个而不是 12 个

3 沙箱 × 4 审批 = 12,但有三组功能重复:

  • 自动同意在三种沙箱下等效:只读 + 自动同意 里写操作被沙箱拒 → 自动批准 → 照样写成功,最终权限与 全电脑 + 自动同意 相同,只是每次多一次往返。故只保留一个。
  • 自动风险审批在只读下与工作区读写下等效:分类器对工作区内写入确定性放行,两者结果一致。故只保留工作区读写档。

关键设计:审批档只有两个合法值

@deepseek-ai/dsh-user-approvalAPPROVAL_POLICIES 只有 ask / never。因此:

审批档 预设表里写的值 实际由谁实现
人工审批 ask 浏览器应答者(插件不干预)
自动同意 ask 本插件approval/request 返回 allowed-once
自动风险审批 ask 本插件tools/pre-execute + approval/request 分级裁决
自动拒绝 never DSH 原生确定性拒绝(插件不干预)

插件通过 takeover 表按当前会话的预设 id 分流,未列出的预设全程 next(),行为与 DSH 原生一致。

风险分级与判定顺序

  1. 硬拒绝(HARD) → 默认直接拒绝,人工也不能批准(riskPolicies.hard = 'deny');配为 ask / password 时改为密码批准通道(见下节),但不提供「放行」选项;
  2. 高风险(HIGH) → 按 riskPolicies.high 分派;非盘根递归/批量删除、普通 git push 走此档,工作区内路径豁免;
  3. 工作区内结构放行(write/edit/read 且目标在会话工作区内)→ 放行(受保护目标除外:~/.dsh、凭据、.git/config|hooks);只读工具读取系统目录/配置同样放行——读不是写;
  4. 允许规则(常规 git、包管理器查询、语言运行时、PowerShell cmdlet 等)→ 放行;
  5. LLM 裁判(可开关)→ 语义裁决;
  6. 四档风险策略(低 / 中 / 高 / 极高各自可选):
风险级别 来源 默认策略 可选值
允许规则命中 / 工作区内操作 / 裁判判定安全 allow allow / deny / ask / password
规则未命中 / 裁判不确定 deny allow / deny / ask / password
裁判判定危险 / 非盘根递归删除 / git push deny allow / deny / ask / password
极高 硬拒绝清单(24 条) deny deny / ask / password(不提供放行)
  • ask(转人工)在完全权限下同样可用,经 tools/pre-execute 返回 {kind:'ask'} → approval seam(浏览器审批弹窗,点同意即放行);
  • password(密码批准)= 挂起等密码,由本插件自己的密码弹窗接管(见下节),与模型行为无关。

执行面识别(避免"提及即拦截")

HARD/HIGH 规则只匹配真正会被执行的片段:executionSurface() 会剥离注释、字符串字面量、变量赋值右侧与文件名。因此 $m = "shutdown /s"(仅赋值)、# shutdown /s(注释)、Test-Path pm-sudo-notes.txt(路径含关键词)、Write-Host "..."(输出文本)都不会再被误拦;真实执行 shutdown /s 依旧拒绝。

同理,只读形态与写入形态被区分开:schtasks /querynet user(列用户)、icacls <路径>(查 ACL)、bcdedit /enumdiskpart /? 等只读查询与帮助文本放行,对应的写/变更形态仍然拒绝。

「密码批准」——与放行 / 拒绝 / 转人工并列的第四种方式

低 / 中 / 高 / 极高四档都可以选 password:该档操作会被挂起(不失败),由本插件要求输入批准密码;输对了当场执行,输错或超时按拒绝处理。

  1. 挂起时登记一条待批准请求(工具名 + 投影目标 + 指纹 + 风险档,审计 outcome=password-required);
  2. 输入入口有三处(等价):
    • 密码弹窗:注册进 shell.overlay 槽位,浮在整个应用之上——出现即输入密码点「批准并立即放行」,被挂起的那次调用当场执行,不需要模型重试;点「忽略」按拒绝处理;
    • 设置 → 权限矩阵 → 批准密码:逐条「批准(放行一次)」,或「授权下一次需密码的操作」(通配);
    • 浏览器打开 <Web 地址>/dsh-permission-matrix/approve(纯表单页,无需前端脚本);
  3. 超时(approvalPasswordTtlMs,默认 300 秒)未批准 → 该次调用按拒绝处理,但请求仍留在列表里:此时批准会签一张绑定该指纹的一次性令牌,模型重试即放行;
  4. 令牌/交付都只用一次:指纹不同、会话不同或过期均不放行。

passwordask 的区别:ask(转人工)交给浏览器审批弹窗,点一下「同意」就放行; password 必须知道口令,所以不会被"多点几次同意"绕过。极高风险档的 askpassword 等价(都走密码批准)。

极高风险默认仍为 deny(直接拒绝);设置页对极高风险档只提供「拒绝」与「需批准密码」,配置 allow 会在加载期 fail-closed 拒绝。 未设置批准密码 ⇒ 所有选了「需批准密码」的档一律拒绝(弹窗也会明示"无法放行")。

密码学:口令以 scrypt(N=16384, r=8, p=1, 随机盐)哈希存储,明文既不落盘也不回传浏览器(/status 只回一个 approvalPasswordSet 布尔);approvalPasswordHash 不在设置页的可写键白名单里,只能经专用动作路由写入。待批准请求与令牌都在内存中,DSH 重启即失效。

硬拒绝清单在设置页只读,不可编辑——防止通过配置绕过硬边界。

全局默认 + LLM 机器人默认

选择器 作用域 存储位置
全局默认预设 新建的 Web / 常规会话 DSH 原生 permission 命名空间的 defaultPreset(与 Settings → 权限 同源)
LLM 机器人默认预设 社交渠道会话(按工作区匹配) 本插件的 permission-matrix 命名空间

机器人识别不感知任何具体机器人插件:只按会话工作区(cwd)匹配 robotWorkspaces 列表。机器人插件卸载后本插件照常运行,只是不再产生落在这些工作区的新会话。

安装

dsh plugin --profile <profile> add dsh-permission-matrix

本插件的 cordis.patch.yml 会:

  1. permission 行 restate 为上述 9 个预设;
  2. 插入 permission-matrix 插件行。

若同时装有 dsh-auto-classifier,请把它停用(两者争抢同一个 permission 行):

# <profile>/cordis.patch.yml
- id: auto-classifier
  name: dsh-auto-classifier
  disabled: true
  config: { enabled: false, presetName: auto }

注意 patch 条目必须同时带 name 字段,否则 disabled 不生效。

配置

设置 → 权限矩阵 页面(或 <profile>/cordis.patch.ymlpermission-matrix 行):

默认 说明
enabled true 总开关
takeover 见上表 预设 id → 审批档(auto-allow / classify)
riskPolicies {low:allow, medium:deny, high:deny, hard:deny} 四档风险策略;极高风险只接受 deny / ask
approvalPasswordHash 批准密码的 scrypt 哈希(设置页写入,明文不落盘;不在可写白名单,仅专用路由可写)
approvalPasswordTtlMs 300000 密码批准窗口(毫秒):挂起等待时长 / 令牌有效期
hardApprovalPasswordHash / hardApprovalTtlMs 已废弃(v0.3.0 旧键):仅兼容读取,写入一律用新键;已设口令不会因改名丢失
llmJudge true LLM 裁判开关
judgeProvider / judgeModel 留空 = 跟随当前会话模型
judgeStages both both / fast / thinking
autoAllowHardGuard true 自动同意档的硬拒绝保护
robotDefaultPreset fa-auto 机器人会话默认预设
robotWorkspaces [] 机器人会话工作区(绝对路径,含子目录)
gitSnapshot true 放行前 git 快照
gitSnapshotIntervalMs 30000 快照节流
auditLog true 审计开关
auditFile ~/.dsh/permission-matrix/audit.jsonl 审计文件

配置非法(未知预设 id、非法档位、极高风险档配 allow)会在加载期响亮失败,不会静默降级。

审计

每次裁决写一行 JSONL:

{"time":"2026-09-09T10:52:00.000Z","sessionId":"…","preset":"ww-classify","tool":"write","target":"D:\\out\\a.txt","risk":"medium","rule":"mid-policy:deny","source":"mid","decision":"deny","outcome":"blocked"}

outcome 取值含 passed / blocked / allowed-once / escalated-to-human / password-required(挂起等密码) / password-approved(密码批准后放行) / password-timeout / password-declined / password-cancelled

只写文件与进程日志,不进入模型 transcript

架构

src/
├── index.js           装配 + 三个钩子(tools/pre-execute、approval/request、session/created)+ 密码批准闸门
├── presets.js         9 预设的单一真源
├── preset-router.js   当前会话预设 → 是否接管、以哪档接管
├── decide.js          决策核心(纯函数:HARD → HIGH → 区内放行 → 允许规则 → null)
├── rules.js           硬拒绝 / 高风险 / 允许规则表 + 执行面提取 + 字段投影
├── password-approval.js  「密码批准」通道(scrypt 哈希 + 待批准请求 + 挂起等待 + 一次性令牌)
├── judge.js           LLM 裁判(两阶段 + 跟随会话模型 + 失败降级)
├── snapshot.js        Git 快照
├── audit.js           JSONL 审计
├── robot-presets.js   机器人工作区 → 默认预设
├── settings.js        settings 命名空间 + webServer 同源路由(含批准页)
└── client/index.js    设置页 + 极高风险密码弹窗(手写 __ModuleLoader__ bundle,注册 settings.section 与 shell.overlay)

测试

node --test tests/decide.test.js tests/password-approval.test.js tests/hooks.test.js

69 条用例,覆盖:硬拒绝(删根目录 / 格式化 / 提权 / 强推 / git reset --hard / 系统级包安装)、高风险分派(非盘根递归删除 / git push / 动态执行变量)、工作区内递归删除豁免、受保护目标(路径类 + 命令类)、拼接执行绕过(拆片段 / iex 动态执行 → 硬拒绝;纯输出文案不误伤)、误拦回归(只读查询 / 注释与字符串里的关键词 / _rsa / 只读读系统目录 / 读 settings.yaml)、四档风险策略(含 password 值)、密码批准(哈希校验 / 挂起与超时 / dismiss 与 abort / 一次性令牌 / 指纹与会话绑定 / fail-closed / 「弹窗输密码 → 挂起调用立即放行」端到端 / 中风险档同样生效)、fail-closed 校验、防回环、机器人预设切换、总开关。

实测与修复记录(v0.2.0,2026-09-10)

fa-classify(全电脑读写)与 ww-classify(工作区读写)两档预设下做了真实工具调用实测(400+ 条审计记录),完整报告见 docs/test-audit-2026-09-09.zh.md。首轮实测暴露的问题已在 v0.2.0 全部修复:

✅ 已修复的漏拦截(应拒却放行)

  • git reset --hard 完全无规则 → 新增 HARD 规则 hard:git-reset-hard;
  • 系统级包安装(apt-get / pip / winget install)无规则 → 新增 HARD 规则 hard:pkg-install-system;
  • cmd 风格递归删除绕过(rd / rmdir / del /s /q 不匹配 PowerShell 风格正则,实测能真删目录) → 新增 HARD 规则 hard:rm-root-win-cmd + 高风险规则;
  • 非盘根递归删除被放行 → 新增高风险(HIGH)档,按 riskPolicies.high 转人工,工作区内路径豁免;
  • 命令类工具写 .dsh 绕过保护 → 新增硬拒绝分支 hard:protected-target;
  • 普通 git push 被放行 → 新增高风险规则 high:git-push(--force 变体仍为 HARD);
  • rm -rf ~/子目录 不命中 → 高风险规则 high:rm-recursive-unix 覆盖。

✅ 已修复的误拦截(应放行却被拒)

  • 只读查询与帮助文本误伤(schtasks /querynet usericacls <路径>shutdown /?Get-Help Stop-Computerbcdedit /enumdiskpart /?) → HARD 正则收窄为写/变更形态;
  • 命令文本/注释/文件名/字符串里的关键词误伤(shutdownsudo) → 执行面识别 executionSurface();
  • _rsa 宽泛后缀误伤(my_rsa.txt) → 凭据规则收窄为明确的私钥/凭据路径;
  • read 工具读系统目录被拦(pwsh 读同一文件却放行) → 新增只读工具豁免(READ_PATH_TOOLS);
  • 读取 settings.yaml 被当"篡改"拒 → 只读形态豁免,写入仍硬拒绝。

✅ 拼接/动态执行绕过(2026-09-10 修复)

  • 已知限制所列的命令文本拼接绕过($p1='Remove-'; $p2='Item -Recurse -Force C:\'; & $p1$p2)已修复:实测还有更多同族漏拦($c='diskpart'; & $c$x='shutdown /s'; iex $x$cmd='net user hacker P@ss /add'; Invoke-Expression $cmd),一并堵上。
  • 做法:检测到动态执行形态(& $var / . $var / iex $var / $a$b 相邻变量)时,把引号内的字符串当真实命令再跑一遍 HARD 规则,并识别被拆开的 cmdlet 动词片段('Remove-' 这类「动词 + 连字符结尾」,记为 hard:concat-fragment);
  • 关键取舍:该检查只在出现动态执行形态时触发,所以 Write-Host 'rm -rf /' 这类纯文案不会被牵连——顺带修掉了此前"Write-Host 里提到危险词就被判 hard"的误拦(纯输出 cmdlet 现在一律按执行面匹配);
  • 单独出现 & $someExe --version 这种正常动态调用不硬拒绝,落到高风险档 high:dyn-call(默认 deny,可配 ask / password);
  • 残余风险:变量经多层间接(数组/哈希表/子表达式 $(...) 拼接、[scriptblock]::Create)仍可能绕过静态分析,依赖沙箱层与 LLM 裁判兜底。

其它实测结论

  • ✅ HARD 清单本体可靠:全部硬拒绝规则按预期触发(含大小写 / 引号 / 嵌套变体);
  • ww 与 fa 差异只在沙箱层:插件规则与 LLM 裁决逐项一致;ww 下工作区外写入由文件沙箱兜底拦截(插件放行但实际写不进),命令类写工作区外同样被沙箱拒;只读不受限;
  • 沙箱升级通道放开后,规则漏洞仍可穿透执行——因此漏拦截修复在 ww 模式同样必要;
  • v0.2.0 验证:单元测试 51 条全过;仓库 src/ 与已安装 profile 的 src/ SHA256 逐一一致(实机生效)。

v0.3.0(2026-09-10):双重确认 → 批准密码

失效根因(实测 + 审计证据):v0.2 的「极高风险双重人工确认」要求"模型在窗口内原样重发同一条命令"才能完成第 2 次放行。实际行为是:

  • 第 1 次调用被拒后模型不会原样重发,而是改写命令(加 Write-Host、换变量、换工具)或改走别的路径 → 指纹(工具名 + 投影目标)随之变化,hard-confirm-2 分支永不命中;
  • 审计日志 ~/.dsh/permission-matrix/audit.jsonl 1064 条记录里 hard-confirm-1 出现 3 次、hard-confirm-2 出现 0 次——即极高风险操作事实上无法被人工放行;
  • 用户实测现象一致:只弹一次窗,第 1 次批准被立刻当成拒绝,之后不再有第 2 次。

v0.3.0 替换方案:极高风险不再依赖模型行为,改为批准密码——拦截后登记待批准请求,用户在设置页(或 /dsh-permission-matrix/approve 批准页)输入密码才签发一次性令牌放行;没有密码就永远拒绝。设置页新增「极高风险批准密码」区块(设置 / 修改 / 清除密码、待批准请求列表、授权下一次、批准有效期)。

破坏性变更:hardConfirmWindowMsapprovalPasswordTtlMs 取代;极高风险 ask 档不再产生人工审批弹窗(改走密码闸门);审计 outcomepassword-required / password-approved 取代 hard-ask-1 / hard-confirm-2

v0.3.1:密码批准从「极高风险专属」升级为四档通用方式

  • 「需批准密码」不再是极高风险专属:低 / 中 / 高 / 极高任一档都能选 password;
  • 入口从「去设置页批准」升级为插件自己的密码弹窗(注册进 shell.overlay,浮在整个应用之上):挂起的调用当场放行,不再需要模型重试;设置页区块与 /dsh-permission-matrix/approve 批准页保留为等价入口;
  • 命名与配置键去「hard」化:hardApproval.jspassword-approval.jshardApprovalPasswordHashapprovalPasswordHashhardApprovalTtlMsapprovalPasswordTtlMs;旧键保留兼容读取并会在下次设置密码时自动迁移,已设口令不受影响;
  • askpassword 明确区分:ask 仍是浏览器审批弹窗(点同意即放行),password 必须知道口令;极高风险档两者等价。

参考与致谢

本插件的设计参考了以下三个社区插件(思路借鉴 + 取舍改进,代码为独立实现):

1. dsh-auto-classifier — 主要参考

  • 借鉴:单点 auto 预设的定位(低风险放行 / 危险拦截 / 沙箱升级自动裁决)、tools/pre-execute + approval/request 双钩子 + {prepend:true} 抢占、HARD/SOFT 风险分级、Tool(pattern) 规则语法、字段投影(命令类只扫 command/code、路径类只扫 file_path)、工作区内结构放行、LLM 裁判两阶段(快速过滤 + 思考复审)、拒绝日志与 denial 上限、Git 快照。
  • 改进:把「单点 auto」升级为 3 沙箱 × 4 审批 = 9 个可组合预设;风险分级从 2 档细化为低/中/高 + 极高,且四档各自可配(极高档只允许拒绝 / 转人工(需批准密码));新增执行面识别以消除"提及即拦截";新增全局默认 + LLM 机器人默认两个维度。
  • 修正:该插件用 permissionPresets.current(session.events)(旧签名),在 DSH 0.1.2-rc.1 下会被 try/catch 静默吞掉而失效;本插件改用正确签名 current(session) 并回退到投影读取。

2. dsh-auto-approval-plugin — 审批应答者参考

  • 借鉴:注册 approval/request 监听器返回 allowed-once 实现「自动批准」、从会话日志按 callId真实工具参数(不信任模型写的理由)、路径经 realpath 解析后判定区域归属、失败方向永远 defer(绝不误拒)。
  • 改进:本插件的「自动同意」档同样带硬拒绝保护(极高风险不自动放行),而非无条件放行。

3. dsh-yolo-mode — 分级裁决参考

  • 借鉴:用大模型裁决沙箱升权申请allow / judge / delegate / deny 四值决策、fail-closed(超时/非法输出/模型不可用一律拒绝或转人工)、审计 JSONL 每行一次裁决。
  • 改进:本插件把「转人工」做成中/高三档的可选策略,并解决了「完全权限下无审批通道」的限制——利用 tools/pre-execute 返回 {kind:'ask'} 经 approval seam 转人工(见设计说明书 §0.1)。

其它参考

  • DSH 官方文档:docs/subsystems/approval.zh.md(审批 seam 语义)、docs/cookbook/extension-cookbook.zh.md(permission-gate 范例)、docs/cookbook/adding-a-settings-card.zh.md(设置页两个半侧)。
  • 硬拒绝清单的条目集合合并自上述三个插件的默认 deny/dangerous 规则表。

License

MIT

内容来自项目 README(GitHub)↗

链接

同类插件

查看整个分类 →

社区评论

评论公开保存在 GitHub Discussions。加载评论会连接 GitHub 和 Giscus;发表内容需要 GitHub 账号。