Skip to content
dsh.fish
Bundle

dsh-trust-check

Static trust audit for DeepSeek Harness plugins: capabilities, injected prompts, token cost, provenance, and a reproducible 0-100 trust score

Source
liuwenji007
License
MIT
Updated
Updated yesterday

Readme

# dsh-trust-check

[English](README.en.md)

DeepSeek Harness 插件静态信任审计:对已安装插件做**能力披露**——权限、注入、来源、安装脚本——代码判定、可复现、零 token。

> 不是"杀毒软件",也不做安全承诺。它只做能证明的事:把插件**真实会碰什么、注入了什么、来源是否可核对**摊开给你看,结论每一条都附证据(文件 + 行号 + 片段),你可以自己复核。

## 安装

**设置页需要 dsh ≥ 0.1.0-rc.8(建议 0.1.1-rc.2)。** Web UI 依赖宿主模块表里的 `@deepseek-ai/dsh-client-store`;市场不会拦不匹配的升级,老宿主上装了设置页也会加载失败(`dsh-client-store` missed the module table)。

**CLI 不依赖 DSH 宿主**,旧 dsh 上仍可用 `npx dsh-trust-check`。

先确认宿主(只影响设置页):

```sh
dsh --version
# 过旧则:npm i -g @deepseek-ai/dsh@latest
```

```sh
dsh plugin --profile web add dsh-trust-check
```

重启 `dsh web`,打开 **设置 → 插件体检**。已安装要升级:市场一键更新,或 `dsh plugin --profile web add dsh-trust-check@latest`,再重启。

同时提供独立 CLI,不依赖 DSH 宿主:

```sh
npx dsh-trust-check                 # 审计默认 profile `web`
npx dsh-trust-check --profile work  # 审计其他 profile
npx dsh-trust-check --json          # 机器可读输出

# 审计任意已解压的包目录(无需 profile、无需 DSH)
npx dsh-trust-check --dir ./path/to/plugin
npx dsh-trust-check --dir ./pkg --spec npm:foo@1.0.0 --json
```

`--dir` 与 `--profile` 互斥。两种模式的 `--json` 输出同为 `AuditResponse` 形状 `{ schemaVersion, profile, dir?, generatedAt, plugins, errors }`;单目录模式下 `profile` 为空字符串,`dir` 为绝对路径。详见 [docs/audit-schema.md](docs/audit-schema.md) / [docs/INTEGRATION.md](docs/INTEGRATION.md)。

| 设置 → 插件体检 | CLI `--dir --json` |
| --- | --- |
| ![设置页插件体检](docs/dsh.png) | ![CLI JSON 输出](docs/cli.png) |

### 排障

| 碰到 | 怎么处理 |
| --- | --- |
| 设置页 `dsh-client-store` missed the module table | 宿主过旧:先升 **dsh ≥ 0.1.0-rc.8**,再重启 Web;审计可先用上面的 CLI |
| 设置里没有「插件体检」 | 确认已 `add`、已重启;或宿主太旧导致客户端未加载 |

## 怎么读报告

设置页与 CLI 采用**决策优先**布局,阅读顺序:

1. **决策**:徽章 + 动作句 +「为什么要小心」(最多 3 条)
2. **扫描**:能力芯片 → 注入摘要(默认折叠)→ 来源
3. **取证**:证据按能力分组,默认折叠

| 裁决 | 含义 | 何时出现 |
|---|---|---|
| **有红线** | 命中硬红线,默认应停用;可「确认风险」后继续 | `redLines` 非空,且未确认当前指纹 |
| **风险已确认** | 已确认当前红线风险 | 有红线,`trust-ack.json` 与本次扫描一致 |
| **需确认** | 未见硬红线,但有特权能力或 patch 改动 | 有能力或 override/disable,无红线 |
| **能力如预期** | 已确认当前能力与去向指纹 | `trust-ack.json` 与本次扫描一致(无红线) |
| **静态未见风险信号** | 未见红线或特权能力;不是安全保证 | 其余 |

`score` / `summary` 仍留在 JSON 里供排序与集成方使用,设置页不展示;注入 token 估算标明为**成本参考**,不是信任依据。

### 形状层(代码判定)

除能力芯片外,报告还会从源码中提取三类字面量事实:

- **字面量去向**:URL / host / IP。同源 HTTP 相对路由(如 `/dsh-market/check`)不算去向。
- **工作区外路径**:绝对路径、家目录、路径穿越等。
- **密钥触摸**:路径、敏感 env 名。

这些只代表「在源码里看到了什么」,不代表「地址/路径安全」;运行时拼接的 URL 看不到。

**白名单**:常见托管/registry 域名(GitHub、npm、npmmirror、腾讯云镜像等)、DSH 精选目录与 GitHub 代理、常见模型厂商 API(DeepSeek、OpenAI、Anthropic、Google Gemini)。命中白名单的条目默认收起并附简短说明;明文 HTTP 永远不会因白名单降级。

**降噪与截断**:

- 跳过**网络对齐**的 CIDR 表行(如 `["10.0.0.0", 8]`、`inRange(a, "10.0.0.0", "10.255.255.255")`);`["8.8.8.8", 32]` 这类主机路由仍记为字面量 IP。不因行内出现 `PRIVATE_RANGES` / `CIDR` 字样、或同行两个 IP 就整行跳过。
- 跳过 RFC 5737 文档例网(`192.0.2.0/24`、`198.51.100.0/24`、`203.0.113.0/24`),以及 `0.0.0.0` / `255.255.255.255`(绑定/广播,非单播出站)。
- 跳过 `http://local` / `http://dsh.invalid` 一类占位 base、RFC 2606 的 `.example` / `.invalid` / `.test`、cmd 开关(`/c`)等误判噪音。
- 注释在扫描前被抹掉,JSDoc 里的示例 URL 不算去向(打包产物通常保留注释)。
- `xmlns="http://www.w3.org/2000/svg"` 一类命名空间标识按主机名精确排除——攻击者注册不到这些域名,这条豁免无法被借用。
- 超出上限时按风险高低截断,明文 HTTP 与字面量 IP 不会被无害地址挤掉。
- 密钥路径只认**无空白的路径形引号串**(`"~/.ssh/config"`、`"/Users/x/.ssh/config"`、`'.ssh/config'`、`"~/.aws/credentials"`)或 `/id_rsa`、`~/.netrc` 等路径形态;UI 文案(`"Uses … ~/.ssh/config when empty"`)、deny-list 正则、`startsWith('id_rsa')`、裸 `'.ssh'` 不算凭据访问。

### 记为预期

在设置页确认「这些能力符合我装它的目的」后,指纹写入 `~/.dsh/profiles/<profile>/trust-ack.json`。升级后能力 / 去向 / 工作区外路径 / 密钥触摸 / 注入(含技能文本大小)任一变化都会回到「需确认」。确认红线走单独的「确认风险」按钮,普通的记为预期请求无法顶替。

**AI 解释**:可选按钮,通过 DSH 已配置的模型解释报告摘要,**不改裁决**;未配置模型时不可用。

### 红线(有则默认应挡住)

1. 声明 install / postinstall / preinstall 安装脚本(**不含** `prepare`:`prepare` 只在 pack/git 安装时跑,记为扣分,不是红线);
2. `cordis.patch.yml` override / disable 了 `@deepseek-ai/*` 核心 bundle(匹配 `id` **或** `name`);
3. 读取凭据/密钥材料(keychain / keytar / dotenv / 路径形 `.ssh` / `.aws/credentials` 等)**且**有网络访问;
4. 非 localhost 的明文 `http://` 外连(字面量)**且**有 network;
5. 非 loopback、非文档例网、非绑定/广播的字面量 IP 外连 **且**有 network。

命中红线时数值分封顶 49(避免「100 分 + 高风险」的误导)。**裁决只看 `redLines`,不看分数**:分数低(如 9 分)可能只是 shell + 网络 + 未锁版本叠加,应显示「需确认」而非「有红线」;JSON 里的 `band` 仍可能为 `red`(分数低于 50),但 UI/CLI 用 `verdict()` 呈现,二者不要混读。

## 审计什么

| 维度 | 读什么 | 判定 |
|---|---|---|
| **能力面** | `package.json` 依赖 scope + 静态扫 `lib/`、`dist/`、`bin/`、`scripts/`、技能目录,以及 `main` / `exports` / `bin` 入口文件 | shell / 文件读写 / 网络 / 凭据 / 子代理 / LLM 调用 / 环境变量 |
| **注入面** | `cordis.patch.yml` + `systemPrompt` / `ctx.skills.register` / `system-prompt/assemble` + 技能文本 | override / disable 了谁(`id` 或 `name`)、注入了什么 |
| **成本** | 技能文本 + system-prompt 行内字面量字节数 | 估算每请求注入 token(字节 / 4,仅估算) |
| **来源** | `package.json` 的 `repository`(缺失回退到 git 安装源)+ 安装 spec | 是否锁版本/锁 commit |
| **更新风险** | 安装脚本(install/postinstall/preinstall;`prepare` 仅扣分) | 是否在安装时执行任意代码 |

## 给集成方(如 dsh-market)

装前怎么接闸门:**[docs/INTEGRATION.md](docs/INTEGRATION.md)**(英文)。  
JSON 字段契约:**[docs/audit-schema.md](docs/audit-schema.md)**(`schemaVersion`、稳定五字段、易变字段)。

本包导出稳定 API,供安装前确认弹窗或 CI 闸门使用:

```ts
import { auditPlugin, collectPlugin, verdict } from 'dsh-trust-check'

const report = auditPlugin(collectPlugin(extractedDir, spec))

// 闸门语义(写死,可直接抄进 market 确认弹窗):
// verdict(report) === 'red'   → 默认挡住,允许用户确认后继续
// verdict(report) === 'review'  → 展示能力清单,建议用户确认
// verdict(report) === 'clear'   → 可静默通过
```

CLI 等价调用(market 也可 spawn,无需 DSH):

```sh
npx dsh-trust-check --dir "$EXTRACTED_DIR" --spec "$INSTALL_SPEC" --json
```

解析 `--json` 时统一读 `plugins[0]`(单目录)或 `plugins` 数组(profile 模式);`errors` 非空表示目录不可读——**按扫描失败处理,不是 `clear`**。空目录 / 损坏解压(无可读 `package.json` 且无源码)会进 `errors`(fail closed)。`--json` 顶层含 **`schemaVersion`**(当前为 `1`):只在输出**形状**破坏性变更时递增,检测规则改动不会 bump。细节见上两份文档;仓库内 Path A 冒烟样例:`scripts/market-gate-demo.mjs`。

**本期不做**:远程 tarball 下载(拉包是 market 的职责)。独立验证姿势:先把包解到临时目录,再 `--dir`。

## 判定原则

- **代码判定,不是 LLM 打分**:判断全在代码里,不烧 token、结果可复现。
- **只证"有",不证"无"**:静态分析只下"检测到了某能力"的结论,从不说"保证没有某能力"。
- **证据可复核**:每个能力命中都带 `文件:行号` 和原文片段。
- **seam 表可热更**:能力判定规则是一张数据表(`src/core/seams.ts`),DSH 接口变了改表不改引擎。

## 已知局限

- **装后体检**:profile 模式审计的是**已经安装**的插件;install/postinstall/prepare 在你第一次扫描前就可能已经跑过。`--dir` 模式可在安装前对解压目录扫描(但安装脚本本身仍可能在 market 解包/安装阶段已执行)。
- 静态扫描有漏判/误判(运行时才加载的能力看不到;动态 `import('node:' + …)`、字符串拼接、混淆后的 `eval`/`Function` 仍可能绕过规则表)。
- **不扫 `node_modules`**:依赖里的行为不在审计范围内。
- 客户端 `fetch('/api')` 等同源调用仍记为 network;芯片会标「同源」或「外连」,但没有字面量外连不等于不出网,评分不变。
- 注入 token 是字节 / 4 的粗估,不是精确计费。
- `link:` / `file:` 本地安装的插件无法从 spec 推断来源,若其 `package.json` 未声明 `repository`,会显示"未声明仓库"。
- `repository` 字段是插件自述,不与 npm 包名交叉验证;非 `http(s)` 协议不会渲染成可点击链接。

## 开发

```sh
pnpm install
pnpm build       # tsdown:node half → lib/index.js,client half → lib/client.js
pnpm test        # vitest,覆盖 core 引擎
pnpm typecheck   # tsc --noEmit
```

规则表、跳过规则与白名单的贡献流程见 [CONTRIBUTING.md](CONTRIBUTING.md)。**扩大跳过 / 白名单与加规则不同权**——二者都可能削弱检测;明文 HTTP 永远不会因白名单降级。修误报时必须带 fail-open 探针。规则打磨对照的攻击面与静态分析极限见 [THREAT-MODEL.md](THREAT-MODEL.md)(英文)。

## 路线图

- v1(当前):已装插件体检 + CLI `--dir` + Web 分项报告
- v2:向 dsh-market 提安装确认 PR(本包已提供 `--dir` / `auditPlugin` 契约)
- v3:作为 Agent CI 的数据层——"插件升级后行为是否漂移"的回归断言

## License

MIT

Install

dsh plugin --profile web add github:liuwenji007/dsh-trust-check#5b81d38954dfbf7b74170ba14e5def41cb40791a

Profile: web

  • This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
Source