← Back to home@bbaz123

dsh-operation-security

DSH 操作安全插件:工具 pre-execute 分类、单调 guard、插件生命周期 DPIP 门禁与审计留痕。

Stars
0
Language
JavaScript
Created
Oct 22, 2026
Updated
Oct 22, 2026

Introduction

dsh-operation-security

桌面版(Desktop / app.asar 0.2.1-rc.2)的操作安全层移植。npm-global CLI 安装树里有两样桌面版看不到的东西,这个插件把它们带过来,并补上桌面版唯一可行的插件生命周期门禁位置。

它做的三件事

能力来源在桌面版怎么工作
工具调用分级与策略执行@deepseek-ai/dsh-operation-security@0.2.0-rc.2 的构建产物(vendor/opsec/,逐字移植)注册 tools/pre-execute + 单调 ctx.tools.guard():READ / DOWNLOAD / EXECUTE / PLUGIN_* / EXPORT / PUBLISH;EXPORT/PUBLISH 额外做凭据扫描;拒绝不可被后续监听器翻回允许
插件生命周期 DPIP 门禁CLI 里被插入 lib/plugin-*.js 的 gatePluginInvocation包装 pluginManager.installBundle(以及启用路径的硬停检查)。CLI 的 dsh plugin 在桌面版无法接管:desktop profile 由 Electron 独占,且 GUI / Agent / 自安装页的写入都走 pluginManager 的 Gateway 方法,不经过工具流水线
审计 / 状态 / 恢复边界同上包写入 $DSH_HOME/operation-security/,与 CLI 门禁同一份 audit.jsonl 与 plugin-state.json

门禁语义(与 CLI 一致)

装/升级(installBundle)
  ├─ 该插件记录为 BLOCKED / QUARANTINED / ROLLBACK_REQUIRED → 拒绝(确认也不能放行)
  ├─ 记录为 ACTIVE                                          → 放行(coverage = active)
  ├─ 有有效确认(ack 文件,或 DSH_DPIP_ACK=1)               → 放行(consumed = 一次)
  └─ 其余                                                     → 拒绝,并给出可执行指令

启用(setBundleEnabled / setPluginEnabled,仅 enabled = true)
  └─ 只有硬停状态会被拒绝;未完成的协议记录不拦(CLI 也不拦启用)

拒绝会抛出一个 code = 'DSH_DPIP_REFUSED' 的错误,消息里带三样东西:缺什么(协议五步)、怎么确认(脚本绝对路径与命令)、硬停无法用确认绕过。

给未经审查的第三方插件开一次确认

node "<工作区>/dsh-operation-security/scripts/ack.mjs" @scope/name --note "看过 changelog,无 postinstall"
  • 确认写入 $DSH_HOME/operation-security/dpip-ack.json,默认 24 小时有效;名称会按门禁的规则归一化(pkg@1.2.3 与 pkg 是同一个键);
  • --ttl 0 表示「不设记录级上限」,实际仍受 gate.ackTtlHours 这个上限约束;只有把上限配成 0 才真的不过期;
  • 一次成功安装后自动作废;
  • 只有操作员(你)能写:Agent 和插件自己都不会调用这个脚本。

协议状态可以直接改(脚本不做协议迁移合法性校验,它是操作员入口而不是验证器):

node scripts/state.mjs list
node scripts/state.mjs set @scope/name ACTIVE        # 视为协议已完成
node scripts/state.mjs set @scope/name BLOCKED       # 硬停,确认也无效
node scripts/state.mjs remove @scope/name

配置

行配置在 cordis.patch.yml 里,全部字段见该文件注释。两个总开关:

  • enabled: false → 两层都关闭,profile 回到装插件之前的状态;
  • gate.enabled: false → 只保留工具层策略,不拦插件生命周期写入。

其余是上游 schema 的同名字段(requireApprovalForExecute / requireApprovalForOutput / scanRoots / scanMaxFiles / scanMaxFileBytes / enforcedOperations)。

外发扫描根:EXPORT/PUBLISH 的扫描根按「调用声明的 workdir(向上找 .git)→ 会话的沙箱工作区 → scanRoots」顺序解析。桌面宿主的进程 cwd 是 profile 目录,所以默认会跟随会话工作区;一旦显式配置了 scanRoots,就以你的配置为准。

目录

lib/index.js          插件入口:装工具层,再装服务门禁
lib/config.js         无 schemastery 的字段校验与默认值
lib/paths.js          $DSH_HOME / profile 目录解析(只用 node 内建)
lib/scan-roots.js     按调用解析外发扫描根(会话工作区)
lib/gate/spec.js      安装 spec → 包名(状态键)
lib/gate/decision.js  纯决策:ACTIVE / 确认 / 硬停 / 拒绝文案
lib/gate/ack.js       dpip-ack.json 的原子读写
lib/gate/audit.js     审计记录(复用上游 buildAuditRecord)
lib/gate/manager.js   在 pluginManager 原始实例上定义/回收方法
vendor/opsec/         逐字移植的上游构建(改动见 docs/PATCH-NOTES.md)
scripts/ack.mjs       操作员确认
scripts/state.mjs     协议状态查看/设置

验收

cd dsh-operation-security
node --test "test/*.test.mjs"
node --check lib/index.js && node --check vendor/opsec/index.js

安装后一次真实验收:在一个没有协议记录的包上调用插件安装,应得到 DSH_DPIP_REFUSED,并新增一条 finalStatus: BLOCKED 的审计行。

已知边界

  1. 只管运行中的宿主。 桌面版 CLI(runtime/cli/bin/dsh.cmd)对非 desktop profile 的 dsh plugin 仍然没有门禁——那需要改 app.asar 里的 CLI 代码,升级即失效,本插件不做。
  2. 桌面 profile 不能被 CLI 管理,所以先有插件、后有门禁:本插件的安装这次是门禁生效前的最后一次无约束安装。
  3. enable 路径按 best effort 解析包名(listPlugins() 查不到时跳过额外检查),与 CLI 一致——CLI 完全不检查启用。
  4. plugin_manager 工具本身没有加进 detector:它的写入已经由服务门禁覆盖,再加一层会在门禁拒绝之前先弹一次审批。
  5. 恢复边界只覆盖 profile 的 package.json / pnpm-lock.yaml / pnpm-workspace.yaml / cordis.patch.yml,不含进程、服务与插件写到 profile 之外的文件(审计记录里写明了这条 limits)。
  6. 状态键是「按 spec 归一化出来的名字」:仓库名与本地目录能还原成包名,但 git / tarball URL 只能用 URL 本身作键。也就是说,一个已记录 BLOCKED 的包若改从 tarball 地址安装,不会复用按包名记的硬停,需要执行者显式确认(确认后仍会留下审计)。这是 CLI 门禁「按原始参数取值」的继承限制。
  7. 安装失败时审计沿用上游语义:finalStatus: FAILED,但 securityResult 仍是 PASS(上游用它表示「没有安全门禁失败」)。判断成败看 finalStatus。
  8. 每次放行安装都会在 recovery/ 留一份 profile 四文件副本,没有自动清理;需要时手工删旧目录。
  9. 相对 auditDir 按宿主进程 cwd 解析(当前是 profile 目录),建议只写绝对路径或留空。
  10. 外发扫描口径较严:工作区里只要有文件名命中凭据规则(例如任何 .npmrc),EXPORT/PUBLISH 都会判 CRITICAL。要缩小范围,给调用传 workdir,或在行配置里显式设 scanRoots。