Passer au contenu principal
L

dsh-trust-check

liuwenji007/dsh-trust-check

Divulgation statique des capacités pour les plugins DeepSeek Harness : une page de paramètres et une CLI qui listent les capacités des plugins installés, les destinations littérales, les scripts d'installation et les formes d'injection (enregistrements de prompts, texte de skills embarqué, patch de bundle), chacun avec une preuve file:line. Déterminé par le code, reproductible, zéro token. Divulgation uniquement — pas un verdict de sécurité, jamais une affirmation de sûreté.

Installer

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

README

dsh-trust-check

Awesome DSH Plugin npm version npm downloads

English

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。

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

dsh --version
# 过旧则:npm i -g @deepseek-ai/dsh@latest
dsh plugin --profile web add dsh-trust-check

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

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

npx dsh-trust-check                 # 审计默认 profile `web`(默认紧凑中文)
npx dsh-trust-check --profile work  # 审计其他 profile
npx dsh-trust-check --verbose       # 展开每个插件的全量细节
npx dsh-trust-check --lang en       # 英文人读输出(LANG=en* 时也会默认英文)
npx dsh-trust-check --no-color      # 关闭 ANSI(管道默认无色;TTY 可用 --color 强制)
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

# 用退出码做脚本 / CI 闸门(可选)
npx dsh-trust-check --dir ./pkg --exit-code

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

默认退出码始终为 0。加 --exit-code 后:0 本次静态扫描未检出(profile 模式含已确认),1 需确认,2 有红线,3 扫描失败;profile 模式取所有插件中最严重的一项。未检出不是安全背书。 参数写错(未知参数、--dir 等缺值)会直接报错并以 1 退出。

设置 → 插件体检展开复核
设置页总览展开复核与证据
问题反馈AI 解读
反馈检测问题AI 解读
CLI --dir --json
CLI JSON 输出

排障

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

怎么读报告

设置页为单列卡片,同一时间展开一张。

  1. 顶部:一句话定位 + 总览(如「10 个插件 · 3 需确认」)。带颜色的总览条目可点,跳到该类第一张卡。
  2. 收起态:名称、版本、状态徽章、来源;能力细节点开后再看。
  3. 展开后:发现与动作说明 → AI 解读(若已生成)→ 能力 / 去向 / 路径 / 密钥 / 注入 / 来源 → 证据 → 扫描限制 → 底部确认条。
  4. 与上次确认不一致时:右上角显示「需重新确认」;能力有增减时,标题下出现 +能力 / −能力 小标签。
  5. 确认:在卡片底部,把当前指纹写入本机确认记录,供下次扫描对比。

CLI 打印同一套事实,版式独立于设置页。默认紧凑:需处理项用圆角卡片展开(软换行,宽度跟终端走),并在「为什么要小心」下附证据摘录;expected / clear 一行带过。TTY 下带颜色,--no-color / NO_COLOR 可关。加 --verbose 看全量。机器集成(市场目录、CI)请用 --json,不要解析人读输出。

裁决含义何时出现
有红线命中硬红线。先复核具体事实;了解后可「确认风险」继续redLines 非空,且未确认当前指纹
风险已确认已确认当前红线风险有红线,trust-ack.json 与本次扫描一致
需确认未见硬红线,但有特权能力或 patch 改动有能力或 override/disable,无红线
需重新确认以前确认过,但本次扫描与上次不一致有确认记录,指纹已变(可能来自升级、结果变化或规则更新)
能力如预期已确认当前能力与去向指纹trust-ack.json 与本次扫描一致(无红线)
未检出项目未见红线或特权能力其余

score / summary 留在 JSON 供排序与集成;设置页不展示。注入 token 估算只在「注入」折叠标题旁作成本参考(有值才显示)。

形状层(代码判定)

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

  • 字面量去向:URL / host / IP。同源 HTTP 相对路由(如 /dsh-market/check)不算去向。每条去向带用法标签(请求参数、仅比较、XML 命名空间、链接、赋值/配置、用途未知)和 文件:行号。标签只帮助判断,不改变能力、红线或分数。
  • 工作区外路径:绝对路径、家目录、路径穿越等。
  • 密钥触摸:路径、敏感 env 名。

表示源码里出现了该字面量;运行时拼接的 URL 看不到(模板 URL 的 ${…} 若在主机名之后,或紧跟在完整的带点主机名后面(`https://api.github.com${path}`),主机名仍会记为去向;${…} 出现在主机名写完之前则看不到)。字面量本身不做安全推断。

白名单:常见托管/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"、pdf.js 的 http://www.xfa.org/schema/…、http://ns.adobe.com/xdp/ 一类命名空间标识按主机名精确排除——攻击者注册不到这些域名,这条豁免无法被借用。只有带路径的字面量才豁免:不带路径的 "http://www.w3.org" 后面拼上任何内容都会变成别的主机,所以仍记为去向。
  • 打包进来的格式库里,少数完整 URL 只是标识而非请求(Apple plist 的 DTD http://www.apple.com/DTDs/PropertyList-1.0.dtd、ID3 的 http://musicbrainz.org),按整串字面量精确排除;同主机的其他路径、子域名,或字面量后面紧跟 + / .concat( 拼接,仍记为去向。不带路径的 http://musicbrainz.org 只在作为 == / === / != / !== 的比较对象时豁免。
  • 超出上限时按风险高低截断,明文 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' 不算凭据访问。

记为预期

在设置页确认「这些能力符合我装它的目的」后,指纹写入当前 profile 目录下的 trust-ack.json(通常是 ~/.dsh/profiles/<profile>/;DSH 桌面版由宿主决定目录)。升级后能力、去向、工作区外路径、密钥触摸、红线或技能正文(按内容哈希,而不是字节数)任一变化,都会要求重新确认。确认请求必须带上当前报告的 ackFingerprint;内容已经变化时返回 409,并提示重新确认。没有 digest 的旧确认一律失效。确认红线走单独的「确认风险」按钮,而且只在指纹一致之后才接受。

AI 解读:阅读辅助,可从确认条打开。用 DSH 已配置的模型解释报告摘要;未配置模型时不可用。生成结果保存在本机;插件版本、检测结果或界面语言变化后需重新生成。

反馈检测问题:每条证据可以生成一份草稿;插件详情里另有入口,用来报漏报或说明不清楚。草稿留在页面内存。误报和说明问题会打开检测反馈的 Markdown Issue 编辑器,并带上你改过的标题和正文,由你提交。漏报默认打开 SECURITY.md 里的私密报告页;标明为普通规则缺口、且不含可利用绕过时,才出现公开 Issue。

红线(有则默认应挡住)

  1. 声明 install / postinstall / preinstall 安装脚本(不含 prepare:prepare 只在 pack/git 安装时跑,记为扣分,不是红线);
  2. cordis.patch.yml override / disable 了 @deepseek-ai/* 核心 bundle(匹配 id 或 name);
  3. 读到凭据/密钥的值(credentials.resolve / read / readRecord / get*,含经接缝别名 / 解构改名的调用、keytar/keychain 的 getPassword 等——含 import * as / default as 改名——或读文件调用同行的密钥路径)且有网络访问——只拿到句柄(const c = ctx.credentials、ctx.get('credentials'))或只读元数据(describe)记为 chip 披露,不算红线;
  4. 非 localhost、非 RFC 1918 内网 IP 的明文 http:// 外连(字面量)且有 network;
  5. 非 loopback、非文档例网、非绑定/广播、非 RFC 1918 内网(10/8、172.16/12、192.168/16)的字面量 IP 外连 且有 network。内网 IP 仍列在去向里并标「内网/私人」,只是不判红线——远程攻击者收不到发往内网的数据;169.254/16(含云元数据地址)不在此列,照常判红线。

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

审计什么

  • 能力面 — 读:package.json 依赖 scope + 静态扫 lib/、dist/(都没有则回退包根)、bin/、scripts/、skills/ / prompts/,以及 main / exports / bin / 字符串 browser 入口文件;files 能精确解析时,npm 不会发布的开发文件不计入。判定: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 仅扣分)。判定:是否声明了安装时执行的脚本

给集成方(如插件目录、市场、CI)

装前怎么接闸门:docs/INTEGRATION.md(英文)。
JSON 字段契约:docs/audit-schema.md(schemaVersion、稳定五字段、capabilities 取值与红线文案契约及建议中文、易变字段)。
版本变更:CHANGELOG.md,每个版本先列「Affects catalog results」,锁定版本的集成方升级前看这一节即可。
真实接入参考:awesome-dsh-plugin 在目录构建期调用本工具,适配层见 scripts/lib/capabilities.mjs(第三方代码,由对方维护)。

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

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

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

// 三态含义(是否拦截、是否弹窗由集成方决定):
// verdict(report) === 'red'     → 命中高置信度风险信号,建议默认挡住,允许用户确认后继续
// verdict(report) === 'review'  → 带特权能力,建议展示能力清单
// verdict(report) === 'clear'   → 本次静态扫描未检出(不是安全背书)

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

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(当前为 2):输出形状出现破坏性变更,或新增集成方可以依赖的稳定字段时递增,检测规则改动不会 bump。schema 2 新增 facts[],其中 facts[].id 是稳定的过滤键;schema 1 的字段没有删改,能力取值和红线模板也与 schema 1 相同。细节见上两份文档;仓库内 Path A 冒烟样例:scripts/market-gate-demo.mjs。

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

判定原则

  • 代码判定,结果可复现:判断在规则表里完成,不烧 token。
  • 只报告「有」:下「检测到某能力」的结论;未检出不做安全推断,也不构成安全背书。
  • 证据可复核:每个能力命中都带 文件:行号 和原文片段。
  • seam 表可热更:能力判定规则是一张数据表(src/core/seams.ts),DSH 接口变了改表不改引擎。

已知局限

  • 装后体检:profile 模式审计的是已经安装的插件;install/postinstall/prepare 在你第一次扫描前就可能已经跑过。--dir 模式可在安装前对解压目录扫描(但安装脚本本身仍可能在 market 解包/安装阶段已执行)。
  • 静态扫描有漏判/误判(运行时才加载的能力看不到;动态 import('node:' + …)、字符串拼接、混淆后的 eval/Function 仍可能绕过规则表)。
  • 不展开依赖树:目录遍历跳过 node_modules,import 'lodash' 这类包名不跟。相对路径如果落在本包 node_modules 里,会跟进去扫。
  • 凭据读值判定覆盖到哪些写法:接缝服务(ctx.get('credentials'),含 await / 可选链 get?.;接收者认 ctx / this.ctx / *Ctx)、属性直取(ctx.credentials)、从上下文解构(含改名 credentials: creds)、以及经这些来源再赋值的别名(含 b = a、provider = credCtx.credentials ?? null),其上的 resolve / read / readRecord / get* 都算读到值;keytar / keychain 的 getPassword / findCredentials 等,包括默认导入、import * as、import { default as … }、require 改名也算。裸 readRecord( / this.readRecord() 不算(本地同名函数);ctx.get('credentials').readRecord(…) / credentials?.resolve(…) 等链式与可选链仍算。仍漏判:解构到函数名(const { resolve } = ctx.credentials 后 resolve(…))——它与 Promise executor 同名,按行匹配会把 new Promise(resolve => …) 误判成读值(实测会把 dsh-pocket、agent-teams 误红),需要作用域追踪;密钥路径先存进变量再 readFileSync(p) 同样漏判;接收者若既不叫 ctx 也不以 Ctx 结尾(如 context / Context)也不认。
  • new URL 的 base 参数不记为去向,因此 const u = new URL('/x', 'http://evil'); fetch(u.href) 这种写法不含明文 http 红线——这是该豁免的已知代价,不要再扩大 base 豁免范围。
  • 完整的相对路径字面量(fetch('/api')、fetch('./x'))以及 blob: / data: 字面量不算 network——前者是同源调用,后者只走 scheme fetch、不会发起 HTTP。引号里的 ${ 只是路径字符。反引号里,插值前面已经是 / 加至少一个非斜杠字符,或已经是 ./、../ 时,也算同源(fetch(`/dsh-xu/${name}`))。fetch(`/${name}`)、fetch(`${url}`)、变量参数和拼接仍记为 network;\、\n、\u 以及八进制 \057 折成 / 之后变成 //主机 的也记为 network。芯片会标「同源」或「外连」,但没有字面量外连不等于不出网,评分不变。
  • link: / 源码目录在 package.json 的 files 能精确解析时,不把 npm 不会发布的开发文件算进能力和红线(被发布代码 import 的、安装脚本点名的、入口文件仍然算)。* 表示全部路径,dir/* 表示该目录整棵树;src* 不含 src/ 里的文件。花括号、字符类、extglob、反斜杠写不准则不过滤。证据上的「浏览器端 / 服务端 / 命令行 / 疑似第三方」来自入口可达性和 sourcemap,只供阅读,不改分数。registry 解压出来的包本来就只有发布文件。
  • 注入 token 是字节 / 4 的粗估,不是精确计费。
  • link: / file: 本地安装的插件无法从 spec 推断来源,若其 package.json 未声明 repository,会显示"未声明仓库"。
  • repository 字段是插件自述,不与 npm 包名交叉验证;非 http(s) 协议不会渲染成可点击链接。

开发

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

路线图

  • 当前:已装插件体检 + CLI --dir + Web 分项报告;持续加固扫描覆盖面
  • 集成:awesome-dsh-plugin 已在目录构建期接入扫描,dsh-market 与目录站只陈列事实(由对方维护,可替换)
  • v2:结构化事实 facts[](schemaVersion: 2)从 0.2.0 起提供;接下来是版本间升级漂移对比 + 调用方传入 registry 元数据
  • 之后:配合目录侧的版本升级与误报反馈;CI 中的升级漂移断言
  • 有条件再做:社区具名人工审阅,独立仓库、绑定版本、可撤销

完整说明见 docs/POSITIONING.md。

License

MIT

Plugins associés