- Главная
- Плагины
- Безопасность и права доступа
- dsh-plugin-security-review
dsh-plugin-security-review
shanhaifish/dsh-plugin-security-review
Static bundle form of the DSH plugin-install security gate: reviews cordis_define/cordis_run with a fail-safe policy, plus review/audit tools and a browser approval popup (agree / agree+whitelist / reject; agree+whitelist writes the plugin family into tru
Установка
dsh plugin --profile web add github:shanhaifish/dsh-plugin-security-reviewREADME
Plugin Install Security Review(v1.8.0)
English: README.en.md · 简体中文: 本文档
DSH 插件安装安全审查守卫。对 cordis_define / cordis_run 安装或运行的动态插件源码做静态安全审查,并按安全优先策略拦截;同时提供 plugin_security_review / plugin_security_audit 两个审查工具。
v1.5.0 起为静态 bundle 插件,随 profile 层栈自动加载,不再需要每次重启 DSH 后重新 define/run。
仓库内容
| 路径 | 说明 |
|---|---|
package.json + cordis.patch.yml + lib/ | 静态 bundle(推荐):dsh plugin add 安装后开机自启动 |
manifest.json + package-source.js | 动态插件回退形态(v1.4.0):无 bundle 能力的 profile 按 README 恢复流程加载 |
tests/gate.test.mjs | 动态形态行为测试(node tests/gate.test.mjs) |
tests/static.test.mjs | 静态形态行为测试(node tests/static.test.mjs) |
安装(静态 bundle, 推荐)
dsh plugin --profile web add dsh-plugin-security-review
已发布到 npm(
registry.npmjs.org/dsh-plugin-security-review,最新 v1.8.0)。生产环境直接用上述命令从 registry 安装即可;file:本地安装仅用于未发布或离线调试。
本地未发布时用 file: 指向本仓库(注意路径不能含空格):
dsh plugin --profile web add file:/path/to/dsh-plugin-security-review
重启 dsh web 后守卫即自动生效:plugin_security_audit(无参数)应显示「守卫: 运行中 (v1.8.0)」。
安装(动态插件, 回退)
仅用于没有 bundle 能力的 profile。步骤:
- 让 agent 读取
package-source.js与manifest.json。 cordis_define:plugin: { kind:"new", idPrefix:"secur" },name/purpose 取manifest.json,code.host取package-source.js中return { ... }之后的内容。cordis_run激活;plugin_security_audit验证。
动态形态不跨 DSH 进程存续,重启后需重新加载;静态 bundle 形态无此限制。
v1.8.0 变更(相对 v1.7.0)
- 审批弹窗新增「同意+白名单」:弹窗由 [同意][拒绝] 改为 [拒绝] [同意+白名单] [同意]。点「同意+白名单」→ 除把该代码指纹写入
approved外,还把该插件族加入 trusted-local 白名单(new define 按idPrefix写prefixes;existing/run 按pluginId写pluginIds)——同一插件的安装与开发迭代(code 变更导致指纹变化)不再反复弹窗。点「同意」仍仅批准该代码指纹(代码变更即失效)。白名单写入 best-effort:写失败(权限/损坏)只记日志、不影响指纹批准,读取侧 fail-closed 不变。 - 修复:点「同意」后重试消息偶发投到别的会话。旧实现用浏览器端单一
sessionId,从 mux 流任意session/subscribed帧取值;DSh mux 是全会话聚合的,多会话并存时最后一个订阅者覆盖该值,导致session.prompt把重试注入到其它工作对话。现改为守卫在拦截时记录来源会话 id(exec.agent.sessionId,写入每个 pending 条目的agentId),弹窗点击后经connection.api.sessions.prompt注入到来源会话;agentId缺失或目标不可 prompt 时静默退回手动重试。 - 弹窗 hint/按钮文案与 README 同步更新;新增 T19(来源会话 id 暴露)与 T20(同意+白名单写白名单、同族变体不再弹窗)测试。
- 动态回退形态(
package-source.js/manifest.json)同步版本号至 v1.8.0,但不提供弹窗(浏览器 GUI 特性,仅静态 bundle;其 ASK 仍走 seam)。
v1.7.0 变更(相对 v1.6.0)
- 审批弹窗(同意/拒绝, 新增, 静态 bundle):ASK 判定不再走官方审批 seam(在审批禁用部署中 seam 会被策略自动拒绝成
the user rejected tool死路),改为挂起审批 + deny + 浏览器弹窗。用户点击「同意」→ 代码指纹持久化批准 → 注入会话消息让 agent 自动重试 → 安装/运行成功;点「拒绝」→ 记录并放弃。弹窗经守卫自己的 HTTP 路由(/security-gate/approvals/pending、/security-gate/approvals/decide)轮询驱动,渲染在shell.overlay浮层;携带client/client.jsClient 半端(dsh.client声明 +exports["./client"])。 askMode配置:<DSH_HOME>/storages/plugin-security-gate/config.json设{"askMode":"seam"}可回退官方审批 UI;默认(缺失/损坏)popup。- 弹窗样式与主题适配:居中模态 + 半透明遮罩(替代早期侧边浮层, 消除遮挡);全部颜色使用 dsw 设计系统真实令牌(
--dsw-alias-bg-overlay/--dsw-alias-button-primary-fill/--dsw-alias-label-primary-foreground/--dsw-alias-bg-mask-3/--dsw-alias-state-error-primary/--dsw-alias-state-warn-label等),亮/暗主题自动适配,保证字体与窗口可读性。 - audit 状态展示:总览增加
askMode与待审批数量;gateStatus()同步新增字段。 - 动态回退形态版本同步 v1.7.0,但不提供弹窗(浏览器 GUI 特性,仅静态 bundle;其 ASK 仍走 seam),README 明示该差异。
v1.6.0 变更(相对 v1.5.0)
- 操作者白名单 trusted-local(新增):
<DSH_HOME>/storages/plugin-security-gate/trusted-local.json声明本机可信插件(前缀/pluginId/代码指纹),命中时cordis_define/cordis_run直接放行并写入审计历史([trusted-local])。信任锚是有文件权限的操作者,不是 agent 自我声明;文件不存在或损坏时行为与现状完全一致(fail-closed 不变)。cordis_stop/cordis_undefine的守卫自我保护不受白名单影响。 - 修复 audit lossless-JSON 报错:
plugin_security_audit不带includeBundles时返回bundles: undefined,违反 dsh-tools 的 lossless-JSON 输出校验导致工具报错;现默认null。 - BLOCK 拒绝文案修正:BLOCK 没有人工批准通道,文案不再声称"批准可放行",改指向 trusted-local 白名单;组合规则(
HOST_EXFIL_CRED/CLIENT_COOKIE_EXFIL等)标注[不可声明削减],明确不受CAPABILITIES:声明影响。 - 动态回退形态(
package-source.js/manifest.json)同步到 v1.6.0。
v1.5.0 变更(相对 v1.4.0 动态形态)
- 静态 bundle 化:命名导出
name/inject/apply,经ctx.tools.register(defineTool(...))注册工具,经ctx.on('tools/pre-execute')拦截,开机自启动。 - 移除动态自升级豁免:静态形态经
dsh plugin update升级,cordis_define/cordis_run对所有动态插件一律审查,不再有secur前缀/谱系令牌豁免;源码不可检索时一律 fail-closed(ask)。 - 审查引擎、评分模型、能力声明、跨会话批准持久化、库存审计与 v1.4.0 保持一致。
判定策略(安全优先)
| 判定 | 条件 | 行为 |
|---|---|---|
| BLOCK | critical>0 或 high≥2 或 总分≥100 | 拒绝安装/运行,附完整报告(不经弹窗) |
| ASK | high≥1 或 总分≥40 | askMode=popup(默认):挂起审批 + 弹出「同意/拒绝」确认框,同意后指纹批准并自动重试;askMode=seam:走官方审批服务(审批禁用时等效拒绝) |
| WARN | 总分≥10 | 放行,卡片附审查报告 |
| ALLOW | 其余 | 放行 |
风险权重:critical=100, high=40, medium=15, low=4,总分上限 300;能力声明项计分减半(最低 1 分)。
审查覆盖面
- Host 规则:不安全进程执行(exec/execSync/shell:true — critical)、VM 逃逸、Node 内部 API、构造器链逃逸、宿主进程终止(critical);子进程执行能力(spawn/fork — high)、原型污染、动态代码、凭据访问、审批篡改、动态模块加载(high);静态模块加载、进程信号、文件系统、网络、全局设置、沙箱、工具干预、会话读取(medium);普通 env 读取、定时器(low)。
- 凭据形态环境变量:
process.env.X名字形如*_KEY/_TOKEN/_SECRET/_PASSWORD/_CREDENTIAL/_AUTH/_COOKIE/_PRIVATE单独记 high。 - Client 规则:
document.cookie、innerHTMLXSS、浏览器eval、动态模块加载(high);import()/require、存储、网络、跳转、postMessage、Service Worker(medium);host.call、定时器(low)。 - 15 项服务能力面:
ctx.get('shell'/'subprocess'/'credentials'/'approval'/'dynamicCordisRunner'/...)精确检测。 - 组合规则(收敛后):凭据形态数据+网络 = critical;Cookie+网络 = critical;文件读取+网络 = high;动态代码+网络 = high;本地存储+网络 = low(信息性)。
- 混淆检测:高熵长字符串、
\x/\u转义、atob。
能力声明约定(可选)
在 cordis_define 的 purpose 末尾追加:
CAPABILITIES: spawn,network,env,fs
关键字:spawn exec module network env fs credentials approval shell subprocess runner settings sandbox sessions llm process eval vm proto storage cookie dom redirect postmessage serviceworker rpc timer obfs。声明覆盖的规则项计分减半(报告标 [已声明])。
跨会话批准持久化
- 人工批准的 ASK 成功执行后,其代码指纹(host+client 的 sha256)写入
<DSH_HOME>/storages/plugin-security-gate/state.json。 - 后续 define/run 命中相同指纹自动放行;代码变更即失效。历史尾部一并持久化。
本地可信开发:trusted-local 白名单(v1.6.0)
背景:守卫对所有 cordis_define/cordis_run 一律审查,合法的本地开发插件也可能被 BLOCK/ASK 卡死(例如"读取 DEEPSEEK_API_KEY 并调用官方 API"这类插件触发 HOST_EXFIL_CRED critical 组合,BLOCK 无人工批准通道)。不做"本地创建自动放行":DSH 工具调用契约里没有来源/出处字段,"本地编写"与"提示注入伪造"在调用面上不可区分,自动放行等于重新引入 v1.4.0 修复过的豁免伪造漏洞。放行必须绑定 agent 自我声明之外的信任锚——操作者文件白名单。
格式(由有文件权限的操作者创建;文件不存在/损坏 = 无白名单,行为不变):
{
"prefixes": ["dev", "local"],
"pluginIds": ["devtool-1", "legit-llm-client"],
"fingerprints": ["<host+client 代码指纹 sha256, 可用 plugin_security_review 结果中的 sha 字段>"]
}
匹配规则(命中任一即直接放行,并记入审计历史 [trusted-local]):
| 条目 | 匹配对象 |
|---|---|
prefixes | define 的 idPrefix(kind:new)或 pluginId(kind:existing)前缀匹配 |
pluginIds | define 的 pluginId / run 的 pluginId 精确匹配(不依赖源码可检索) |
fingerprints | host+client 联合 sha256 精确匹配(代码变更即失效) |
边界说明:
- 白名单放行同样适用于 BLOCK 级插件——这是"操作者显式信任"的意图所在。
- 把守卫自身 id/前缀加入白名单会解除守卫自我保护(操作者本就有文件权限卸载守卫,风险自担)。
cordis_stop/cordis_undefine对守卫的自我保护不受白名单影响。- 在 full-access 部署中 agent 也能写该文件——这与 agent 已有全部文件权限的信任级别一致,白名单是对抗"未经操作者确认的高危安装",不是对抗 agent 自身的安全边界。
审批弹窗:拒绝 / 同意+白名单 / 同意(v1.8.0, 静态 bundle)
为什么需要:ASK 判定走官方审批 seam 时,在审批禁用部署(策略自动拒绝)中会变成 the user rejected tool "cordis_define" 死路。v1.7.0 起静态 bundle 形态自带弹窗,ASK 不再无出口。
流程:
cordis_define/cordis_run触发 ASK 判定 → 守卫挂起审批记录(持久化于state.json,并记录来源会话 idagentId)并返回 deny,文案提示"已弹出确认框"。- 浏览器端
client/client.js(注册在shell.overlay浮层)轮询GET /security-gate/approvals/pending,弹出卡片:插件名、判定/风险分、风险分布 + [拒绝] [同意+白名单] [同意]。 - 点「同意」→
POST /security-gate/approvals/decide {sha, outcome:'approve'}→ 守卫把该代码指纹写入approved→ 经session.prompt向来源会话注入重试消息 → agent 自动重试 → 同指纹放行。 - 点「同意+白名单」→
POST .../decide {sha, outcome:'approveTrusted'}→ 同第 3 步外,再把该插件族写入 trusted-local(prefixes/pluginIds)→ 同族后续安装/开发(即使代码变更)直接放行,不再弹窗。 - 点「拒绝」→ 记录
human rejected via popup历史并清除挂起;agent 重试同代码仍被拦截。
配置 askMode(<DSH_HOME>/storages/plugin-security-gate/config.json):
{ "askMode": "seam" }
popup(默认,缺失/损坏时回退):守卫自带弹窗;绕过官方审批 UI。seam:走官方approval服务(需要部署启用审批,否则等效拒绝)。
边界:
- 弹窗是浏览器 GUI 特性:仅
webprofile 的静态 bundle 形态提供;CLI/无浏览器会话与动态回退形态的 ASK 仍走 seam(deny 文案附 trusted-local 手动指引兜底)。 - 「同意」仅批准该代码指纹(代码变更即失效),不等于加入 trusted-local;「同意+白名单」才写入 trusted-local。
- 重试注入固定指向来源会话(
entry.agentId,守卫在拦截时从exec.agent记录),与当前浏览哪个会话无关;多会话并存不再投错。 - BLOCK 判定仍硬拒绝,不经弹窗(安全优先不变)。
- 多会话并存时挂起审批为全局注册表(本地单用户部署通常单会话);如需按会话过滤,后续可在路由增加 session 参数。
profile bundle 库存审计
plugin_security_audit includeBundles=true:扫描 <DSH_HOME>/profiles/* 下声明 dsh 的依赖(@deepseek-ai/* 官方包跳过),读取入口产物做同一套静态审查,输出逐包判定。信息性审计:bundle 安装(pnpm/npm)不经过 cordis_define/cordis_run,守卫无法在该路径硬拦截,仅提供装前/装后可见性。
工具
plugin_security_review:对给定 host/client 源码做安装前审查,返回判定、评分、逐条风险与行号;purpose支持CAPABILITIES:声明。plugin_security_audit:指定 pluginId+packageId 出完整报告;无参数出全量总览+拦截/放行历史+守卫状态;includeBundles=true附加 profile 库存审计。
维护与边界
- 升级守卫(静态):
dsh plugin --profile web update dsh-plugin-security-review;本地file:依赖更新后重跑一次dsh plugin add。 - 自我保护边界:静态 bundle 守卫无法通过
cordis_stop/cordis_undefine停用(它不在动态注册表里),但可由有文件权限的操作者经dsh plugin remove卸载——这是静态形态与动态形态的固有差异。 - 已知限制:组合判定是静态 presence 判定,未经数据流确认,可能误报/漏报;深度混淆存在盲区;本守卫是进程内拦截(标准安装路径),不是对抗可停用守卫的恶意 actor 的安全边界,真正的纵深防御仍需宿主级隔离。trusted-local 白名单与审批弹窗都是操作者/用户决策面,不是对抗 agent 自身的安全边界;审批弹窗仅静态 bundle 形态提供。
Похожие плагины
api-relay-audit
toby-bridges/api-relay-audit
dsh-auto-review
perrylink/dsh-auto-review
dsh-claude-ux
eri64/dsh-claude-ux
dsh-remote
xgone/dsh-remote