DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

bbo777 /

bbo777/dsh-baseline

Verified

A fail-closed security baseline plugin for DeepSeek Harness (DSH): policy-gated network egress, a monotonic subagent permission ceiling, and one hash-chained audit log of every tool decision and result.

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHubProject homepage
READMESource: main@87ce4c79

dsh-baseline

CI npm license

DSH 的安全基线闸门:网络出口独立成层,子代理权限只能收窄不能放宽,每一次判定与结果写进同一条可校验的哈希链。

版本 0.1.0 · DSH peer >=0.2.1-rc.2 <0.3.0(真机验证环境:0.2.1-rc.2 CLI Web、0.2.1-rc.3 desktop,范围见验证状态) · Node.js >= 22.5(node:sqlite;22.13 起无需额外 flag)

Host 半区与客户端均已实现;真机验证范围见验证状态与待验证项:桌面版验证服务端加载、审计、链与 HTTP 路由,CLI Web 验证客户端渲染、主题与 API。

快速开始

dsh plugin --profile web add dsh-baseline

装好后重启 DSH,左侧栏出现「基线审计」即生效。桌面版请在 Plugin Manager 里安装 dsh-baseline。

本包安装即启用。若想先审查再启用,见安装与启用第 3 条。


首屏:五条不能含糊的话

1. 不魔改 DSH 源码

只使用官方公开的扩展点和事件:tools/pre-execute、tools/result、ctx.tools.guard()、ctx.approval、ctx.agents、webServer.register()、客户端 slots / locale。不依赖未文档化的内部 API,不替换、不补丁、不包装 DSH 自身的运行时文件。

2. fail-closed

任何不确定的判定、审批缺失、审批超时、子代理 lineage 断裂、审计库不可用,一律拒绝,并给出明确原因。宁可让一次调用失败,也不让它在无法验证时继续执行。

3. 与相邻产品互补,可共存

  • 与 dsh-permgate:互补。它做 8 类权限拦截;本插件不替代权限分类,只守网络、子代理上限、凭据与破坏性操作这几条底线。
  • 与 auditrail:互补。它做 SQLite 哈希链审计;本插件的链只记录自身的判定与结果,不接管全局审计。
  • 与 dsh-plugin-product-subagents:互补。它提供子代理上限;本插件在 ctx.tools.guard() 里再保一次单调上限,任何后注册的 listener 或 guard 都不能把子代理放宽。
  • 与 dsh-plugin-vet:互补。它做运行时观察;本插件做阻断、审批与可校验审计。

4. 能力边界:明确不覆盖什么

  • 不覆盖“恶意插件直接 require('child_process')”的逃逸。纯插件层看不到绕过 ToolRuntime 的原生进程调用,这需要进程级隔离、容器或操作系统策略。
  • 不覆盖 DSH Code Mode run_code 的 worker thread 逃逸。worker 内执行的代码不经过可拦截的工具调用边界,同样需要进程级隔离。
  • 不解析 DNS。公网域名解析到 127.0.0.1 或发生 DNS rebinding 时,本插件只能看到主机名字符串。
  • 不是防火墙,不是沙箱,不是杀毒软件,也不是完整的能力权限系统。完整清单见能力边界。

5. “baseline”的含义:有些底线,任何配置都不能推翻

启用插件后,以下两类规则无条件、优先于所有自定义规则执行,配置文件和面板都无法关闭:

  1. 破坏性系统命令(根目录递归删除、格式化磁盘、写块设备、fork bomb 等);
  2. 对设备或内核接口的原始写入(/dev/sd*、/proc/self/mem、/sys/*、\\.\PhysicalDrive*)。

网络底线分两部分:

  • 不可关闭:始终阻断的内部主机名与后缀(云元数据、.internal、.svc、.local 等);
  • 默认开启、可运营调整:私网、回环、链路本地、CGNAT 的 IP 段由 blockedCIDRs 定义。默认列表覆盖这些范围;删除条目会明确削弱该范围的阻断,这是操作者的显式选择,README 不把它伪装成不可推翻。

凭据读取/外发拒绝与子代理权限上限默认开启,只能通过配置文件中的 guard 开关显式关闭,面板不能关闭。

“默认开启”指策略在安装后即生效;本包安装即启用(见安装与启用)。如需先审查再启用,见该节第 3 条。


它解决什么问题

大多数 Agent 安全产品在“工具名”层面做权限控制。但真实的逃逸往往发生在三个地方:网络出口、子代理继承、以及“谁在什么时候基于什么理由允许了什么”。dsh-baseline 把这三处做成同一套可验证的机制:

层 机制 不可绕过的原因
网络出口 私网/元数据默认阻断、可选主机白名单、按会话出口预算、严格模式逐个审批 网络命令与 URL 在 pre-execute 中提取,硬规则在 guard 中二次复核
子代理上限 沿 session.header.parentSession 回溯祖先,重放同一次调用的策略 在 ctx.tools.guard() 中执行;guard 只有“拒绝/不干预”两种结果,没有 allow,后注册者无法放宽。fork 会话不计入
审计 判定与结果写入同一条 SHA-256 哈希链,客户端只读查询 写入前脱敏;verifyChain() 返回第一条断链记录;客户端不直接接触 SQLite

它不做的:

  • 不替代 dsh-permgate 的权限类别;
  • 不把每条审计记录上传到远端;
  • 不读取业务数据,不保存文件内容、patch、diff、图片或消息体;
  • 不提供自定义审批 UI,审批一律复用 DSH 标准 ctx.approval;
  • 不在客户端保存 token,不使用 localStorage。

安装与启用

前置条件

  • DSH >=0.2.1-rc.2 <0.3.0(peerDependencies 与 engines.dsh 的区间;见下方兼容矩阵)
  • Node.js >= 22.5,且 Node 带 node:sqlite(本包使用 DatabaseSync)。22.13 起无需额外 flag;22.5–22.12 需要以 --experimental-sqlite 启动 DSH
  • DSH 以 Web 模式运行(只有 Web 模式会挂载 webServer;面板与 HTTP API 依赖它)

兼容矩阵

DSH Cordis 状态
0.2.1-rc.3 4.x 已验证(桌面版:加载 / deny / 审批拒绝 / 审计链 / HTTP 路由)
0.2.1-rc.2 4.0.5 已验证(CLI Web:客户端渲染 / API 全路由)+ 服务端真实 ToolRuntime 集成测试
其它 0.2.x 4.x 未验证(peer 与 engines.dsh 允许,但未跑过)

package.json 的 dsh.compatibility.dsh 记录首个完成真机验证的版本(0.2.1-rc.2),不代表只支持该版本;实际允许范围由 peerDependencies['@deepseek-ai/dsh-tools'] 与 engines.dsh 的区间决定。

构建

npm install
npm run build

产物:

  • lib/index.js 与 lib/types/:Host 半区
  • lib/client.js:客户端 bundle(约 60 KB)
  • locale/zh.json、locale/en.json:面板与 Plugin Manager 的显示元数据
  • icon.svg:插件图标

安装

  1. 在 DSH 的 Plugin Manager 中安装 dsh-baseline(从 npm),或使用 install_bundle 安装本目录 / npm pack 后的 tarball。不要手工复制 node_modules 或改写 Loader 配置来替代安装步骤。

    注:已在 DSH 0.2.1-rc.3 desktop 上通过 Plugin Manager 从 npm 安装并验证(侧栏入口、面板、导出、熔断解除均通过)。

  2. 安装后先看 bundle 详情与插件行:标题应为“安全基线”/Security baseline,描述与图标来自 locale/*.json 与 icon.svg。

  3. 本包安装即启用。 随包发布的 cordis.patch.yml 只做一次 insert,不带 disabled 键:

    - insert:
        - id: dsh-baseline
          name: dsh-baseline
          config: {}
    

    原因:Plugin Manager 的安装开关只会把包写进 dsh.profile.bundles,不会翻转条目的 disabled。早期版本把 disabled: true 写死在包里,结果是「装上了、列表里有、重启后什么都没发生」——插件被合成进树里,却从未实例化。

    想先审查再启用,不要改包内文件,而是在 profile 的 cordis.patch.yml 末尾加一行覆盖:

    - id: dsh-baseline
      disabled: true
    

    审查完改成 false 或删掉该行,再重启 DSH。

  4. 替换已安装的包后需要重启 DSH,才能加载新的 JavaScript 模块;仅安装新 bundle 可能通过 HMR 生效。

  5. 重启后打开 Web UI:左侧栏出现“基线审计”图标,主面板 key 为 baseline。面板需要 Host 半区已启用且 webserver/index-inject 已注入启动 token。

配置覆盖语义

DSH Loader 中,匹配到同一插件的 config 会整体替换原有配置,而不是逐字段合并。Schemastery 会为缺失字段补默认值,但不会保留你上一次写的值。覆盖配置时,请把需要偏离默认值的字段一次写全。

推荐的最小启用配置:

- insert:
    - id: dsh-baseline
      name: dsh-baseline
      disabled: false
      config:
        defaultMode: standard

策略引擎

三种模式

行为 loose standard(默认) strict
硬规则(含凭据/网络) 执行 执行 执行
自定义规则 执行 执行 执行
内置审批规则 不触发 触发 触发
严格模式兜底 不启用 不启用 启用
无规则命中时的默认 允许 允许 只读放行;网络或可能修改状态的调用需审批
熔断 / 出口预算 / 审计降级 fail-closed fail-closed fail-closed
子代理上限(启用时) 执行 执行 执行

standard 是低误报默认:只在有明确信号时弹审批(下载管道执行、破坏性 Git、破坏性 SQL、持久化、监听端口、读 .env、写系统目录、证据不完整,以及配置了白名单后的白名单外目标)。

硬规则(优先于自定义规则)

这些规则在自定义规则之前评估,effect 固定为 deny,risk 为 critical(出口预算的 guard 拒绝为 high)。

guard-private-network 的规则本身不能被配置或面板关闭,但其 IP 段集合来自 blockedCIDRs,删除某个 CIDR 会使其不再命中;始终阻断的内部主机名不受列表影响。凭据类规则受 guard.enforceCredentialDeny 控制。

规则 ID 触发条件
guard-destructive-command 递归删除根目录/home(操作数先做 POSIX 规范化,/tmp/..、/./、// 视为根);删除盘符根;format 磁盘;dd 写块设备;mkfs* / wipefs / blkdiscard;对设备 shred;在根目标递归 chmod/chown;在根目标 find -delete/-exec;fork bomb
guard-root-write 任意命令或工具的写入目标规范化后等于文件系统根(/、C:\、//)或当前用户主目录(~、$HOME);覆盖 mv x /、> / 等非 rm 变体
guard-device-write 写 /dev/sd*、/dev/nvme*、/proc/self/mem、/sys/*、\\.\PhysicalDrive* 等设备或内核接口
guard-credential-read 读真正的凭据/密钥:~/.ssh/ 下的 id_rsa/id_dsa/id_ecdsa/id_ed25519 与 *.pem/*.key/*.p12/*.pfx/*.jks/*.keystore,以及 .npmrc、.netrc、.pgpass、~/.aws/credentials、~/.azure/、~/.kube/config、~/.docker/config.json、~/.git-credentials、~/.gnupg/、gcloud 凭据、~/.password-store/。~/.ssh/config 仅审批(见 builtin-credential-config-read);known_hosts 与 *.pub 不拒绝
guard-credential-write 写入任意 ~/.ssh/authorized_keys(持久化后门)
guard-credential-exfiltration 同一次调用既读凭据又访问网络或写私钥;或读取 .env/敏感配置同时访问网络(即使 loose 模式也拒绝)
guard-private-network 目标命中 blockedCIDRs,或命中始终阻断的内部主机名(见网络策略)

凭据三条规则只在 guard.enforceCredentialDeny: true(默认)时执行。其余四条不可关闭。

内置审批规则

规则 ID 触发条件 standard strict
builtin-pipe-to-shell 下载内容直接管道给 sh/bash/python/node 等解释器;PowerShell irm | iex 审批 审批
builtin-destructive-git git push --force、reset --hard、clean -f/-x、checkout/restore .、branch -d、stash drop/clear、filter-branch/filter-repo 审批 审批
builtin-destructive-database DROP TABLE/DATABASE、TRUNCATE TABLE、ALTER TABLE ... DROP、无 WHERE 的 DELETE FROM 审批 审批
builtin-persistence crontab 写入、systemctl enable、launchctl load、schtasks /create、注册表 Run 自启动、chkconfig --add、update-rc.d,以及写入 shell rc/systemd/cron 等持久化位置;authorized_keys 写入由 guard-credential-write 直接拒绝 审批 审批
builtin-listener nc -l、socat ... listen、ssh -R、python -m http.server、php -S、ngrok/cloudflared/chisel/frpc 审批 审批
builtin-sensitive-read 读取 .env / .env.*(.example、.sample、.template、.dist、.md 除外) 审批 审批
builtin-credential-config-read 读取 ~/.ssh/config(可能包含 ProxyCommand);known_hosts 与 *.pub 不触发 审批 审批
builtin-protected-write 写 /etc、/usr、/bin、/sbin、/boot、/dev、/proc、/sys、/var/lib、/var/spool、/Library、/System、C:\Windows、C:\Program Files 等受保护位置(/dev/null、/dev/stderr、nul 等无害设备目标除外) 审批 审批
builtin-unverifiable-evidence 参数遍历触顶(truncated)且涉及网络或写入 审批 拒绝
builtin-network-unverifiable 配置了 allowedHosts、调用涉及网络、但无法确定目标主机 审批 拒绝
builtin-network-not-allowed 配置了 allowedHosts、目标不在白名单中 审批 拒绝
builtin-egress-budget 仅严格模式:maxEgressBytes > 0 且检测到上传标志 — 审批
builtin-workspace-escape 仅严格模式:写入工作区之外的绝对路径 — 审批

严格模式兜底(没有规则命中时):

  • 网络调用且所有目标都在显式白名单内 → 允许(default-strict-allow);
  • 其他网络调用 → 审批(builtin-strict-network);
  • 非只读调用 → 审批(builtin-strict-mutation);
  • 只读调用 → 允许(default-allow)。

自定义规则

rules:
  - id: allow-npm-registry
    effect: allow
    priority: 100
    risk: low
    reason: Package installs may reach the npm registry
    tools: [bash]
    commands: ["npm *", "pnpm *"]
    hosts: ["registry.npmjs.org"]

匹配语义:

  • 不同维度之间是 AND:写了 tools 和 hosts,就必须同时满足。
  • 同一维度内部是 OR:hosts 中任意一个命中即可。
  • 未写的维度视为不限制;但至少要写 tools/paths/commands/hosts/agents 之一。
  • tools、agents:大小写不敏感的 glob。agents 匹配 agent.id(会话 ID)。
  • paths:同时按路径 glob 与普通 glob 匹配,覆盖真实路径、词法路径和工作区相对路径;路径模式中 * 不跨 /。
  • commands:默认是“包含”匹配;模式中出现 *、?、[ 时按 glob 匹配。会匹配完整命令与每个解析后的命令段。
  • hosts:主机名 glob 或 CIDR。
  • 排序:priority 降序;相同优先级按声明顺序;第一条命中的规则生效。
  • effect 可为 allow / deny / ask,risk 为 low/medium/high/critical。
  • 硬规则优先于自定义规则;自定义 allow 不能放行破坏性命令、设备写入、命中当前 blockedCIDRs 的网段或已启用的凭据拒绝。
  • 自定义 allow 仍会经过 guard 的子代理上限检查。
  • 规则 ID 必须唯一,长度 ≤128,不能使用 builtin-、guard-、network-、__ 前缀;每个维度最多 64 个模式。

单调 guard:子代理权限上限

ctx.tools.guard() 的契约是“只返回拒绝原因或不干预”,没有 allow。因此它是唯一能保证“后注册的 listener 不能放宽前人的拒绝”的位置。本插件的 guard 按以下顺序检查:

  1. 审计库不可用 → 拒绝(guard-audit-unavailable);
  2. 会话已熔断 → 拒绝(guard-fuse);
  3. 出口预算已超限且本次涉及网络 → 拒绝(guard-egress-budget);
  4. 重新执行全部硬规则(此时已经过审批;审批不能放宽硬规则);
  5. 找不到本次调用的 pre-execute 记录 → 拒绝(guard-policy-bypass,防止绕过判定阶段);
  6. 子代理上限未启用,或本次是根代理 → 放过;
  7. lineage 无法验证 → 拒绝(guard-subagent-ceiling);
  8. 对每个祖先,按祖先身份重放同一次调用的策略;若祖先的判定等级高于子代理本次判定(deny > ask > allow),拒绝(guard-subagent-ceiling)。

lineage 的来源:

  • 从当前 agent 的 session.header.parentSession 向上走;
  • header.origin === 'subagent' 但没有 parentSession → lineage 断裂;
  • ctx.agents.get(parentSession) 找不到父会话 → 断裂;
  • 出现环、或深度超过 32 层 → 断裂;
  • 断裂一律拒绝,不放宽。

这是“对同一次调用重放祖先策略”的单调上限,不是完整的能力集合格。它保证:如果祖先在同一调用上会被更严格地对待,子代理就不能凭自己的配置获得更宽的结果。若 ctx.agents 不可用,所有子代理调用都会被拒绝(fail-closed)。

审批

  • 只使用 DSH 标准 ctx.approval.request(),不渲染自定义阻塞 UI。
  • 只有 allowed-once 视为通过;rejected、cancelled、unavailable 全部拒绝。
  • approvalTimeoutMs(默认 30,000 ms)到期即拒绝,并标记 timedOut;审计结果记为 timeout。
  • apply 时 ctx.approval 不存在只记 warning;真正需要审批而服务仍缺失时,显式拒绝并写明“approval is required but ctx.approval is unavailable”,不会静默等待到超时。
  • 审批通过后 guard 会重新检查硬规则与子代理上限;一次审批只对本次调用有效。

网络策略

networkPolicy 是独立的策略层,不依赖工具名:

networkPolicy:
  allowedHosts: []          # 空 = 允许公网主机;非空 = 白名单
  blockedCIDRs: [ ... ]     # 默认覆盖常见私网、回环、链路本地、元数据段
  maxEgressBytes: 0         # 0 = 不启用出口预算

默认 blockedCIDRs:

10.0.0.0/8        172.16.0.0/12     192.168.0.0/16
127.0.0.0/8       169.254.0.0/16    0.0.0.0/8
100.64.0.0/10     ::1/128           ::/128
fc00::/7          fe80::/10

判定规则:

  • IP 字面量:命中 blockedCIDRs 即阻断。默认列表覆盖私网、回环、链路本地、CGNAT、未指定地址与 IPv6 ULA/链路本地。
  • 始终阻断的内部主机名:metadata.google.internal、metadata.goog、instance-data、instance-data.ec2.internal、host.docker.internal、gateway.docker.internal、kubernetes.default.svc。
  • 始终阻断的内部后缀:.local、.internal、.lan、.home.arpa、.intranet、.corp、.private、.svc、.cluster.local、.nip.io、.sslip.io、.xip.io、.lvh.me。
  • 回环主机名:localhost、*.localhost 等只有在回环仍在 blockedCIDRs 中时才阻断;删除 127.0.0.0/8 与 ::1/128 可放行本地开发。
  • 空主机名视为阻断。
  • 主机名比较统一小写、去掉末尾点和端口;支持 [::1] 形式。
  • allowedHosts 为空时,公网主机不做额外限制;非空时,白名单外的目标在 standard 下审批、在 strict 下拒绝。
  • 白名单支持主机 glob(如 *.npmjs.org)与 CIDR 条目。
  • 不做 DNS 解析,不观察真实 socket。这是刻意的:pre-execute 不能把每次工具调用变成一次 DNS 查询,也挡不住 DNS rebinding。

出口预算

maxEgressBytes 是事后估算,不是实时流量计:

  • 工具执行完成后,用 JSON.stringify(exec.arguments) 的 UTF-8 字节数估算本次出口量;
  • 按会话累计;累计超过预算后,该会话后续网络调用被拒绝 60 秒,然后清零重新累计;
  • 当前这次已执行的调用不会被追回;
  • strict 模式下,若预算开启且检测到上传标志(curl -d/-F/-T、wget --post-*、scp/rsync/sftp 远端目标),会先要求审批;
  • 设为 0 关闭预算。

熔断

  • 同一会话在 60 秒内累计达到 denyBurstThreshold(默认 10)次拒绝 → 熔断;
  • 熔断状态只在内存中,重启即清空;触发熔断会写一条 __fuse__ 审计控制记录,解除操作只复位内存、不写链;
  • 面板可解除;解除同时重置该会话的出口预算窗口;
  • denyBurstThreshold: 0 关闭熔断。

审计

写入模型

  • pre-execute 只把 pending 记录放在内存(WeakMap<ToolExecution, DecisionRecord>),不写库;
  • 结果已知时,在 tools/result 只写一次;
  • 被拒绝、取消、超时的调用同样有结果行;
  • result 取值:ok / error / denied / timeout;
  • effect 取值:allow / deny / ask / fuse(fuse 只用于熔断与出口预算的控制记录,不是审批判定)。

数据库默认位置:

  • dataDir 显式配置 → 该目录;
  • 否则 $DSH_HOME/dsh-baseline;
  • 否则 ~/.dsh/dsh-baseline。

文件:audit.sqlite(SQLite,WAL 模式)与 feedback.jsonl。

哈希链

每条记录包含:ts、session_id、agent_id、tool_name、params_json、effect、risk、reason、rule_id、result、prev_hash、hash。

hash = SHA-256(canonical JSON of {
  ts, sessionId, agentId, toolName, paramsJson,
  effect, risk, reason, ruleId, result, prevHash
})
  • 第一条的 prev_hash 是 64 个 0;
  • 写链与插入在同一个 BEGIN IMMEDIATE 事务里,避免两个写入者扩展同一个前驱;
  • verifyChain() 逐条校验 prev_hash 与重算的 hash,返回第一条断链的 id;
  • 审计库打不开或校验过程出错时返回 { ok: false }(没有 brokenAt);面板显示“审计链不可用”,不会伪装成完整;
  • 保留策略清理旧记录后,会把链基线更新为最后一条被清理记录的 hash,因此清理不会造成误报断链;
  • /status 不会每次重扫全链:激活时做一次全量校验,之后返回缓存结果,并附带 checkedAt(上次全量校验时间)、checkedThroughId(已校验到的最大 id)与 stale(校验后又有写入或清理)。调用 /verify 或重启进程会重新全量校验并清除 stale。node:sqlite 是同步 API,这样轮询状态不会阻塞工具调用路径。

保留与导出

  • auditRetentionDays 默认 30 天;启动时清理一次,之后每 6 小时清理一次;0 关闭清理;
  • 清理按 id 切掉一段连续前缀,而不是按 ts 逐条删除:ts 是 pre-execute 时间,并发下可能乱序,逐条删会把中间行挖掉、导致其后所有 prev_hash 失配。清理后 chain.baseHash 更新为被清理的最后一条记录(按 id 排序的最新过期行)的 hash;
  • 查询按 id 降序,单页最多 500 条;sessionId、agentId、toolName 是转义后的子串匹配(%、_、\ 按字面量处理),effect、from、to 是精确条件;
  • 导出按 id 升序,以 1,000 条为一批、按 id 游标分批读取并流式写入响应,最多 50,000 条;超过时响应 x-dsh-baseline-truncated: true 头。面板当前只完成下载,不展示该头;大库导出请直接调用 API。
  • 导出格式:JSON 或 CSV(CSV 单元格做了公式注入转义;JSON 输出为数组,CSV 先写表头)。

保留期边界:清理按 id 切连续前缀。由于 ts 在并发调用下可能乱序,边界附近少量“时间戳未过期但 id 较小”的记录可能被一并清理;影响范围限于乱序跨度(通常为秒级)。

反馈

面板可对单条记录写反馈:追加到 dataDir/feedback.jsonl,不修改既有审计记录;note 最长 2,000 字符,写入前脱敏。新建文件时以 0600 创建。

脱敏:写入之前,而不是之后

  • 私钥 PEM 块、sk-...、sk-ant-...、AIza...、GitHub token、Slack token、AWS AKIA/ASIA、JWT、Bearer/Basic、TOKEN=...、URL 用户信息、查询参数里的 token、--password 类 CLI 参数、Cookie/Authorization 头都会被替换为 [REDACTED];
  • 键名命中 token/secret/password/api_key/credential 等语义的字段整体替换为 [REDACTED](max_tokens 这类计量字段除外);
  • 内容型字段(content、new_string、body、patch、diff、base64、image 等)整体替换为 [omitted],从不解析、从不保存;
  • 参数 JSON 最多 65,536 字符,reason 最多 2,000 字符,递归深度最多 12;超限写 [TRUNCATED] 或截断标记;
  • 脱敏发生在哈希之前,因此库里存的就是被哈希的文本。

注意:脱敏是模式匹配,不是数据分类器。未知格式的密钥、写在命令字符串里的自定义凭据仍可能残留;导出前请按组织要求复核。

数据最小化与自身透明

  • 插件自身不发起任何外部网络请求;客户端只请求同源的本地 API;
  • 不读取、不转发业务数据;文件内容、patch、图片、消息体不进入审计库;
  • 数据库与反馈文件都在本地 dataDir;请用文件系统权限保护该目录;
  • 客户端不直接连接 SQLite,所有查询通过 Host 半区的只读 HTTP 接口;
  • 源码随 npm 包分发(files 含 src/),许可证 MIT。公开仓库地址待补,暂未提供在线源码浏览入口。

审计面板与 HTTP API

面板

  • 左侧栏入口:sidebar.panellist,id 为 baseline,标签“基线审计”;
  • 主面板:main keyed slot,key 为 baseline;
  • “审计记录”页:按判定/工具/会话/每页数量筛选,展开行可看脱敏参数、规则、判定原因、前后哈希,并可提交反馈;支持 JSON/CSV 导出、分页、链校验;
  • “策略与运行状态”页:编辑 allowedHosts 与 blockedCIDRs(即时生效,持久化到审计库 meta 表),查看只读的出口预算、熔断会话并解除熔断;
  • 未保存的策略草稿在切换页签时保留;离开面板或重载后以已保存值为准;
  • 找不到 Host 注入的 token 时显示明确的“面板尚不可用”,不会静默空白。

API

Host 半区在 web 模式下注册 prefix 路由 /dsh-baseline/api,并通过 webserver/index-inject 注入:

window.__DSH_BASELINE__ = { token, apiBase: '/dsh-baseline/api' }
方法 路径 作用
GET /status 记录数、保留天数、时间范围、链状态、降级原因、熔断列表、策略快照
GET / POST /verify 校验整条哈希链
POST /query 分页查询,body { filter: {...} }
POST /export 导出 JSON/CSV,body { filter, format }
POST /feedback 写反馈,body { auditId, note }
POST /fuse/reset 解除指定会话熔断,body { sessionId }
GET / POST /policy 读取或更新 allowedHosts/blockedCIDRs

鉴权与边界:

  • 每个 Host 激活周期生成一个 32 字节随机 token,前端从注入的全局对象读取;不落盘、不进 localStorage;
  • 请求头:x-dsh-baseline-token;比较使用 timingSafeEqual;
  • 带 Origin 的请求必须与 webServer.getOrigin() 完全一致,否则 403;
  • 响应统一 cache-control: no-store、x-content-type-options: nosniff;
  • JSON body 上限 64 KiB;查询单页 1–500 条;导出 50,000 条;
  • /query 的 sessionId、agentId、toolName 为子串匹配(LIKE,%/_/\ 已转义为字面量);effect、from、to 为精确条件;
  • /export 按 id 游标分批读取并流式输出,不会把整次导出结果先堆在内存里;
  • /status 的 chain 是缓存结果(含 checkedAt / checkedThroughId / stale),只有 /verify 或进程重启才重新全量校验;
  • 变更策略只能改这两个列表;guard 开关、maxEgressBytes、defaultMode 等只能通过配置文件修改。

配置参考

所有字段都有默认值,由 Schemastery 校验;apply 中不再写硬编码兜底。

字段 默认值 说明
defaultMode standard loose / standard / strict
approvalTimeoutMs 30000 审批超时,1,000–600,000 ms
auditRetentionDays 30 审计保留天数,0–3,650;0 关闭清理
dataDir '' 空则解析为 $DSH_HOME/dsh-baseline 或 ~/.dsh/dsh-baseline
networkPolicy.allowedHosts [] 空表示允许公网主机;最多 200 项
networkPolicy.blockedCIDRs 见上表 默认覆盖私网/回环/链路本地/元数据段;最多 200 项
networkPolicy.maxEgressBytes 0 0 关闭;否则按会话累计估算
denyBurstThreshold 10 60 秒内拒绝次数达到即熔断;0 关闭
guard.enforceCredentialDeny true 凭据读取/外发拒绝;只能配置文件修改
guard.enforceSubagentCeiling true 子代理权限上限;只能配置文件修改
rules [] 自定义规则,见上节

配置校验会拒绝:重复规则 ID、保留前缀、空维度、空模式、重复项超限、非法 CIDR、URL 形式的 allowedHost、无效枚举值等。

完整示例

- insert:
    - id: dsh-baseline
      name: dsh-baseline
      disabled: false
      config:
        defaultMode: standard
        approvalTimeoutMs: 30000
        auditRetentionDays: 30
        networkPolicy:
          allowedHosts:
            - registry.npmjs.org
            - "*.githubusercontent.com"
          blockedCIDRs:
            - 10.0.0.0/8
            - 172.16.0.0/12
            - 192.168.0.0/16
            - 127.0.0.0/8
            - 169.254.0.0/16
            - 0.0.0.0/8
            - 100.64.0.0/10
            - ::1/128
            - ::/128
            - fc00::/7
            - fe80::/10
          maxEgressBytes: 0
        denyBurstThreshold: 10
        guard:
          enforceCredentialDeny: true
          enforceSubagentCeiling: true
        rules:
          - id: allow-npm-registry
            effect: allow
            priority: 100
            risk: low
            reason: Package installs may reach the npm registry
            tools: [bash]
            commands: ["npm *", "pnpm *"]
            hosts: ["registry.npmjs.org"]

再次强调:匹配到同一插件时,config 整体替换。上例把默认值显式写全,避免覆盖时意外丢失设置。

运行时覆盖

只有 allowedHosts 与 blockedCIDRs 可以面板修改,保存在 audit.sqlite 的 audit_meta 表中,重启后仍然生效。配置文件里的值作为“重置基线”:审计库损坏或持久值非法时回退到配置值。


契约标注

本 README 与代码按三类标注依据。所有签名已对照本机安装的 0.2.1-rc.2 包类型定义或官方文档核对;运行期集成仍有待验证项。

公共契约

这些是包公开文档/类型中承诺的接口,本插件按契约使用:

契约 依据包
Cordis 插件导出 name / inject / Config / apply,ctx.effect / ctx.on / ctx.get / ctx.inject @deepseek-ai/cordis@4.0.5
tools/pre-execute waterfall、PreToolDecision(allow/deny/cancel/ask)、tools/result、ctx.tools.guard() 单调且只返回拒绝原因 @deepseek-ai/dsh-tools@0.2.1-rc.2
审批 request({ agent, toolName, callId, reason, displayReason, signal }),结果 allowed-once / rejected / cancelled / unavailable @deepseek-ai/dsh-user-approval@0.2.1-rc.2
ctx.agents.get(sessionId)、agent.session.header 的 id / cwd / parentSession / origin / delegationDepth @deepseek-ai/dsh-agent@0.2.1-rc.2、@deepseek-ai/dsh-session@0.2.1-rc.2
webServer.register({ kind: 'prefix', path, handler })、getOrigin()、webserver/index-inject @deepseek-ai/dsh-host-webserver@0.2.1-rc.2
ctx.slots.inject / ctx.slots.register,主面板 main keyed slot 与 sidebar.panellist list;ctx.locale.register / bind @deepseek-ai/dsh-client-ui-layout、@deepseek-ai/dsh-client-ui-sidebar、@deepseek-ai/dsh-client-locale
Schemastery Config 校验与默认值 @deepseek-ai/schemastery@3.18.5

仓库惯例

这些是官方开发文档要求的做法,不是类型级强制:

惯例 说明
bundle patch 安装即启用 cordis.patch.yml 不带 disabled 键;如需停用,用 profile 的 cordis.patch.yml 覆盖
通过 Plugin Manager install_bundle 安装 不手工复制依赖或绕过 bundle 选择
同 ID 的 config 整体替换 覆盖配置时必须写全非默认字段
包根 package.json 的 icon 优先于 ./icon 图标 ≤256 KiB,路径必须在包内;显示元数据来自 locale/*.json 的 meta.title / meta.description
客户端构建包装成 window.__ModuleLoader__.load(...) 客户端 require 仅允许 react、cordis、@deepseek-ai/dsh-client-*;不直接 require 具体 client 包
主题使用 --dsw-alias-* token 不在客户端写死品牌色;不向 document.body 追加节点
客户端不直连 SQLite Host 暴露只读 HTTP 接口,客户端通过同源 fetch 访问

当前实现

这些是 dsh-baseline@0.1.0-rc.1 自己的选择,可能随版本调整,不构成对外契约:

内容 当前值
网络证据提取 键驱动的参数遍历 + 小型 shell 分词器;支持常见 shell、PowerShell、sudo/env/timeout 等包装器;嵌套 sh -c、$(...)、反引号递归解析(最多 4 层)
证据上限 5,000 节点、1,000 字符串、2,000 路径、深度 12;越过即 truncated
出口预算窗口 60 秒;按会话累计 arguments JSON 字节;超出后拒绝后续网络调用
熔断窗口 60 秒;默认阈值 10
审计 schema audit + audit_meta,schema 版本 1;id 自增;索引落在 ts、session_id、agent_id、effect
保留清理 启动时 + 每 6 小时;按 id 切连续前缀;清理后更新 chain.baseHash
链状态缓存 激活与 /verify 时全量校验;/status 返回缓存;写入或清理后 stale: true
导出上限 50,000 条;按 id 游标 1,000 条/批流式输出;查询 500 条/页,文本字段子串匹配
API 路径与 token 注入 /dsh-baseline/api、__DSH_BASELINE__
规则保留前缀 builtin-、guard-、network-、__
特殊内部主机名/后缀 见网络策略

待验证项

以下是仍未验证的部分;已验证项的准确范围见验证状态。不要把它们当作已经获得运行期证明:

待验证内容 需要验证的包/环境
桌面应用窗口内的面板视觉效果(CLI Web 已渲染,但未在 Electron 窗口截图核对) Electron 桌面版 + 真实 DSH Web profile
“导出 JSON / CSV”按钮的浏览器下载行为 真实 DSH Web/Electron UI
熔断“解除”按钮、验链按钮的 UI 点击路径 真实 DSH Web/Electron UI
审批弹窗的中英文 displayReason 视觉与文案(拒绝结果已进入桌面版审计) @deepseek-ai/dsh-user-approval、真实审批 UI
Plugin Manager 中标题、描述、图标的实际渲染 @deepseek-ai/dsh-client-ui-plugin-manager、@deepseek-ai/dsh-client-locale
通过官方 install_bundle 安装(本轮为手工 pnpm add + manifest 装配) 本机 DSH 安装与 Plugin Manager
headless 环境下的真实任务(无法复用桌面账号登录,见运行环境限制) 带 DEEPSEEK_API_KEY 的 headless profile
Electron / file:// 模式下面板行为 按设计不提供面板(依赖 webServer);未测试
多个 Host 进程共享同一 dataDir 未验证;设计假设单写入者

人工验收步骤见 docs/dsh-ui-checklist.md。其中仍有未验项:审批弹窗文案、导出下载、熔断解除按钮、验链按钮、桌面窗口视觉、官方安装通道;服务端的 deny/ask/审计/熔断已有自动化测试与真实 ToolRuntime.execute() 覆盖。


验证状态

自动化

  • npm test:130 个测试,15 个 suite 全部通过(node:test + node:assert/strict,通过 tsx 运行;SSR 用例使用 tsconfig.client.json 以启用 JSX 转换);
  • 覆盖 engine、extractEvidence、guard(含 fork 不按子代理处理、真实子代理 lineage、subagent 上限、bypass 拒绝审计)、audit.verifyChain(含 100 条记录篡改测试、缓存状态、按 id 前缀清理、模糊查询、分批导出)、真实 ToolRuntime 集成、客户端 API、破坏性命令与凭据规则回归、无害设备重定向回归;
  • fork bomb 硬规则:test/forkbomb.test.ts 覆盖 3 种写法、bash -c 嵌套、字符串打印的已知误报,以及 rm -rf /tmp/cache、ls -la、echo 'hello world' 不被误伤;
  • npm run typecheck:服务端与客户端两个 tsconfig 均通过;
  • npm run build:服务端编译 + 客户端 bundle 成功(约 60 KB);
  • 中英文字典键集一致:test/client.test.ts 的 zh and en dictionaries keep the same key set;
  • 面板 SSR:test/ssr.test.ts 用 react-dom/server 的 renderToStaticMarkup 真实渲染 AuditPanel 三种初始状态(无 boot token、英文、中文),并断言具体文案(如 The audit panel is not available yet、Security baseline、安全基线、共 0 条记录)。这是 Node SSR 冒烟,不是真实 DSH Web UI 验证;
  • grep -rn "trace(" src/server/index.ts 无输出。

真机验证

Verified on DSH 0.2.1-rc.3 desktop (Windows): bundle load, deny/ask audit, hash chain.

  • DSH 0.2.1-rc.3 desktop(Electron,Windows):插件被正确合成并加载,apply() 实际执行,dataDir 生效,audit.sqlite 自动创建;26 条审计记录,result 全部回填,pending = 0;pwsh cat .env → deny / builtin-sensitive-read / denied,理由 approval timed out after 30000 ms;grep / read 工作区内的 .env → deny / denied,理由含 (approval rejected);其余 23 条 allow + result=ok;插件自身 verifyChain() 返回 {"ok":true},checkedThroughId=26;宿主端口上 /dsh-baseline/api/status 无令牌返回 401。
  • DSH 0.2.1-rc.2 CLI Web(独立测试 profile):合并资源中含 id: "dsh-baseline" 与面板文案;「基线审计」出现在 sidebar.panellist,可点开;面板渲染标题「安全基线」、审计链徽标、筛选器、导出按钮、策略页 11 条 CIDR;深浅主题切换后颜色跟随,无写死品牌色;0 条 console 错误、0 页面异常、0 失败请求;/status /verify /query /policy 带令牌正常。
  • DSH 0.2.1-rc.3 desktop 从 npm 安装(dsh-baseline@0.1.0-rc.1,经 Plugin Manager):包被拉取并写入 profile,apply() 执行、默认 dataDir 下 audit.sqlite 自动创建、/dsh-baseline/api/status 无令牌返回 401;侧栏「基线审计」出现;面板在 Electron 窗口内正常渲染并跟随明暗主题;「导出 JSON / CSV」触发浏览器下载;熔断生效后「解除」可恢复。该次验证在 rc.1 上进行,当时需手工补 disabled: false;0.1.0 起 bundle patch 不再带 disabled,安装即启用。

没有做:

  • 桌面应用窗口内的面板视觉效果(只验证了 CLI Web 运行时下的渲染,未在 Electron 窗口里截图核对);
  • 「导出 JSON / CSV」按钮的浏览器下载行为;
  • 熔断「解除」按钮、验链按钮的 UI 点击路径;
  • 通过应用内 Plugin Manager 的 install_bundle 安装(本轮是手工 pnpm add + manifest 装配,不等于官方安装通道);
  • headless 环境下的真实任务(无法复用桌面账号登录,见运行环境限制);
  • Electron file://、多 Host 进程共享同一 dataDir。

因此,Host 半区的策略行为有单元/集成测试与桌面版真机证据支撑;客户端已在 CLI Web 验证渲染与 API,但 UI 交互与官方安装链路仍有未验项,见待验证项。人工验收步骤见 docs/dsh-ui-checklist.md。

  • 发布元数据:本轮不提供 repository / homepage / bugs 字段;源码通过 npm 包的 files 分发(含 src/),许可证 MIT。

能力边界

明确不覆盖

  1. 恶意插件直接 require('child_process'):绕过 ToolRuntime 的原生进程调用不在纯插件层可见范围内。需要进程隔离、容器、seccomp/AppArmor 或操作系统策略。
  2. Code Mode run_code 的 worker thread 逃逸:worker 内代码不经过可拦截的工具调用边界。需要 worker 级隔离或禁用该模式。
  3. DNS 解析与 DNS rebinding:只检查词法目标。公网域名指向私网地址、DNS 重绑定、代理转发都不在本层能力内。
  4. 真实 socket 流量:不观察实际连接、不计算真实字节、不解析 TLS/HTTP。出口预算是参数大小的估算。
  5. 完整 shell/语言语义:分词器是启发式的;高度混淆的命令、编码载荷、自定义解释器、二进制程序内部的网络行为可能漏检。
  6. 非 ToolRuntime 的副作用:插件自身的代码、其他插件的后台任务、模型直接调用的外部 API 不在拦截路径上。
  7. 操作系统级破坏:硬规则只覆盖高置信度的模式;不是防病毒,也不是系统完整性保护。

运行环境限制

  • headless 无法复用桌面账号登录:dsh --profile headless 默认走 deepseek-official API Key 路由;切到 deepseek-account 后需要 credentials + authorization 服务,而 headless 里 authorization 默认关闭,开启后仍 pending (waiting for service: credentials),最终报 ACCOUNT_SIGN_IN_REQUIRED。根因:账号 provider 的 desktopPlatform 仅在 profile 名为 desktop 且运行于 Electron(win32/darwin)时启用。headless 部署需要自备 DEEPSEEK_API_KEY,否则任务起不来。
  • 审批超时即拒绝:approval 服务 30 秒无响应 → 本次调用按 deny 处理并落审计,理由写作 approval timed out after 30000 ms。这是 fail-closed 的设计取舍:宁可卡住,也不放行。长耗时人工确认场景请调高 approvalTimeoutMs(默认 30000,范围 1,000–600,000 ms)。

已知近似与权衡

  • maxEgressBytes 是事后估算:当前调用执行后才计量,无法阻止本次超额;只对后续网络调用生效。
  • 清空 blockedCIDRs 会削弱 IP 字面量拦截:这是面板允许的显式操作;.internal/.svc/元数据主机名仍然阻断,但私网 IP 与 localhost 的阻断取决于列表内容。默认值不建议删改。
  • 哈希链不是 WORM:能发现单条记录的修改、删除、插入和乱序,但拥有数据库写权限的人可以重算整条链。若需要防整链重写,请定期把链头锚定到外部系统。
  • 审计降级即全拒:SQLite 打不开时,所有经 ToolRuntime 的调用都会被拒绝。这是 fail-closed 的可用性代价。
  • 崩溃可能丢失在途记录:pending 在内存中,进程在 pre-execute 与 tools/result 之间崩溃时,该次调用可能没有审计行;已完成的结果行是持久的。
  • 单写入者假设:SQLite 的写锁能串行化写入,但熔断、出口预算与策略缓存是进程内的;未设计/未验证多 Host 进程共享同一 dataDir。
  • 脱敏是模式匹配:未知格式的密钥可能残留;命令字符串与路径本身也可能包含敏感信息。导出前请复核。
  • 子代理上限是“同一次调用的判定等级”比较:不是完整的能力集合包含关系;lineage 或 ctx.agents 不可用时拒绝所有子代理调用。
  • 面板只在 Web 模式可用:Electron/file:// 没有 webServer,面板与 API 不注册;Host 半区的拦截仍可在插件加载时工作。
  • 严格模式可能更吵:strict 会对未显式放行的网络调用和非只读调用逐次审批;把 defaultMode 设为 strict 前请先准备白名单与规则。standard 是低误报默认。
  • fork bomb 按整条命令的文本子串判定:为覆盖 :(){ :|:& };: 这类包含 |、;、& 的语法,检测发生在分词之前。因此 echo ':(){ :|:& };:' 这种把该字符串当普通参数打印的命令也会被拒绝。这是 fail-closed 的已知误报取舍,锁定用例见 test/forkbomb.test.ts。

未实现的可选项

阶段四的 AST 静态扫描(plugin-risk.json)未实现;dsh-plugin-vet 已提供运行时 watch,本插件不重复该能力。本插件不在安装或启动时解析插件源码、不生成静态风险画像,只对经过 ToolRuntime 的调用做运行时策略判定与审计。


开发

目录

src/server/
  types.ts          结构化依赖类型(不 import DSH 运行时值)
  config.ts         Schemastery schema + 跨字段校验
  engine.ts         硬规则、内置审批、严格兜底、自定义规则
  shell.ts          命令分词、路径/主机/写入提取、破坏性判定
  network.ts        IP/CIDR、主机名策略、白名单
  evidence.ts       从工具参数提取路径/命令/主机
  guard.ts          lineage 与单调 guard
  state.ts          WeakMap pending 记录
  fuse.ts           熔断与出口预算
  policy-state.ts   面板可改的运行时覆盖
  audit.ts          SQLite 哈希链、查询、导出、清理
  api.ts            本地 HTTP API
  redact.ts         写入前脱敏
  index.ts          Cordis 插件装配
src/client/
  index.tsx         面板与侧栏注册
  AuditPanel.tsx    审计/策略界面
  api.ts            同源 REST 客户端
  locales.ts        中英文字典
  client-types.ts   客户端结构化契约
scripts/build-client.mjs  esbuild 包装与 require 白名单
test/               单元与集成测试

命令

npm test          # node:test 全量测试
npm run typecheck # 服务端 + 客户端类型检查
npm run build     # tsc + 客户端 bundle

本机如果 PATH 中的 Node 低于 22.5,请显式使用满足版本要求的 Node 运行 npm,否则 node:sqlite 与 npm 自身都可能不可用。

发布前检查

  • package.json 的 dsh.bundle.patch 指向真实存在的 cordis.patch.yml
  • cordis.patch.yml 不带 disabled 键,安装即启用
  • dsh.client.inject 只包含 @deepseek-ai/dsh-client-* 包
  • lib/client.js 的 require 只有 react / react/jsx-runtime
  • locale/*.json 键集一致,meta.title / meta.description 非模板文案
  • icon.svg 有效、≤256 KiB、路径在包内
  • files 包含 lib/、src/、cordis.patch.yml、locale/、icon.svg、README.md、CHANGELOG.md、docs/、LICENSE
  • npm test、npm run typecheck、npm run build 全绿
  • 在真实 DSH Web/Electron profile 中完成 docs/dsh-ui-checklist.md 里仍标记为“待人工”的项:官方 install_bundle、导出下载、熔断解除、验链按钮、桌面窗口视觉(当前未完成)

License

MIT。见 LICENSE。

—/ 5

No ratings yet

Verified DSH bundle

Commit 87ce4c79b25d

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout