DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

NingZhenPeng /

NingZhenPeng/dsh-plugin-preflight

Verified

DSH 插件安装预检闸门:装之前跑九项预检,拒绝即拦截,并留下审计日志

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@462e8eb1

dsh-plugin-preflight

DeepSeek Harness(DSH)的插件安装预检闸门。装插件之前先核一遍规则,判定为「拒绝」就中止安装,并把每次尝试写进审计日志。

它是一个 DSH 插件(dsh bundle),通过 cordis.patch.yml 挂到宿主的插件加载链上,在 pluginManager.installBundle 之前插入九项预检。

它不是市场,也不替你决定装什么——它只在你按下「安装」到 pnpm 真正执行之间,插一道检查。

它拦什么

九项规则,阈值全部在 policy.json,代码里不含判断标准:

# 检查 阻断级
1 GitHub 仓库存在、未归档、90 天内有推送 ✗ 拒绝
2 无 preinstall / install / postinstall ✗ 拒绝
3 未被 deprecated ✗ 拒绝
4 直接依赖数 > 8 拒绝、> 1 警告 ✗ / !
5 兼容宿主版本(dsh.compatibility / engines.dsh / peer 范围) ✗ 拒绝 / ! 警告
6 自更新、热重载痕迹(脚本 / 依赖 / README) ! 警告
7 是否要求第三方账号 ? 需人工
8 npm provenance 签名 ! 警告
9 装后钉精确版本 · 提醒

只拦「明确拒绝」:警告与需人工一律放行,交给人判断。

怎么工作

安装请求
   ↓
pluginManager.installBundle(被本插件包装)
   ↓
跑 scripts/preflight.mjs <包名> --json
   ↓
拒绝 → 抛错中止 + 写审计 + 在「安装详情」里贴一份结构化报告
通过 → 原样调用原方法
预检跑不成(断网 / 限流 / 超时) → 见下方三种处置

预检跑不成时怎么办

这是唯一可能被「绕过」的环节——如果一律放行,那么让网络卡住就成了绕过手段。所以默认拦截,并按以下顺序处置:

  1. 30 分钟内验过同一个包 → 沿用上次结论(verdict-cache.json),不重复联网;
  2. 没缓存且策略为 block(默认) → 中止安装,报告里写明原因与三种处置办法;
  3. 策略为 allow → 放行,审计记 unverified,界面不提示成功。

策略在面板「高级设置 → 预检跑不完时」切换,热生效。离线环境建议选「放行」,否则装不了任何东西。

注意:这是「安全」与「可用」的取舍,没有两全。默认拦截的代价是断网时装不了插件;默认放行的代价是网络异常等于没设防。

安装

本插件面向 DeepSeek Harness(DSH)桌面端与 Web 端。

桌面端:DeepSeek Harness 插件页 →「添加插件」→ 填 dsh-plugin-preflight(已发布到 npm 时)或本目录的绝对路径 → 重启。

从 GitHub 安装(未发布 npm 时):

dsh plugin --profile desktop add github:NingZhenPeng/dsh-plugin-preflight

命令行(仅纯 web profile):

dsh plugin --profile web add dsh-plugin-preflight

配置

cordis.patch.yml 里的 config(整块替换,改一个键要重述其它键):

键 默认 说明
mode enforce 启动默认模式;运行期以 runtime.json 为准
dataDir 空 数据目录。空时回退 workspaceRoot/docs/audit,再回退 ~/.dsh/plugin-guard/audit
workspaceRoot 空 旧配置兼容用,建议新装时改用 dataDir
timeoutMs 20000 单次预检超时

runtime.json 里可热改的项(面板「高级设置」写的就是它):

键 默认 说明
mode enforce enforce 拦截 / warn 只记录
onUnverified block 预检跑不成时:block 拦截 / allow 放行
timeoutMs 20000 预检超时(毫秒)
auditMaxBytes 1048576 审计文件上限,超过轮转,保留 3 份旧日志
disabledRules / allowlist / thresholds / customRules 空 见上文

它放在数据目录,不在安装目录(0.15.0 起):dataDir → workspaceRoot/docs → ~/.dsh/plugin-guard,与审计同一处。 写在安装目录会导致升级插件时设置被 pnpm 重装覆盖、只读安装则根本写不进去。旧版本留下的 runtime.json 会被自动读取,改一次设置后即迁到新位置。

自定义规则

内置九条之外,可以自己加检查。规则写在 runtime.json 的 customRules,或直接在面板「高级设置 → 自定义规则」里添加。

四个字段各管什么

字段 作用 边界
标题 title 报告里显示的名字 必填。只影响显示,不参与判断
条件 when 唯一的判断依据 必填。语法见下;写错会直接不生效(见「边界」)
等级 status 命中后算多严重 warn / reject / manual 三选一,默认 warn
说明 detail 报告里那句人话 可空;支持 {字段} 占位,如 只有 {repo.stars} star

四者是与的关系:条件命中 → 按等级追加一条结果 → 显示标题与说明。不命中就什么都不发生,不会出现在报告里。

条件语法

字段  比较符  值
  • 比较符:< > <= >= == !=
  • 值:数字 100、字符串 "abc" 或 abc、布尔 true / false
  • 多个条件用 &&(与)、||(或)连接;&& 优先,不支持括号
repo.stars < 50
repo.idleDays > 180 && repo.stars < 100
author == "someone" || name == "bad-pkg"
deprecated == true && provenance == false
deps contains "axios"
npm.weeklyDownloads < 100

数组字段(deps / scripts / keywords)用 contains 判断包含,其余字段用比较符。

可用字段(全部来自预检收集到的事实)

字段 类型 含义 数据来源
repo.stars 数字 star 数 GitHub API
repo.idleDays 数字 距最后一次推送的天数 GitHub API
repo.archived 布尔 是否已归档 GitHub API
repo.found 布尔 是否找到仓库 元数据 / README 推断
repo.slug 字符串 owner/repo 同上
repo.forks 数字 fork 数 GitHub API
repo.openIssues 数字 未关闭 issue 数 GitHub API
repo.license 字符串 仓库许可证 SPDX GitHub API
depsCount 数字 直接依赖数 npm 元数据
deps 数组 依赖名列表(用 contains) npm 元数据
scripts 数组 脚本名列表(用 contains) npm 元数据
scriptsCount 数字 脚本数 npm 元数据
keywords 数组 关键词(用 contains) npm 元数据
keywordsCount 数字 关键词数 npm 元数据
hasInstallScript 布尔 是否有 install 类脚本 npm 元数据
deprecated 布尔 是否已弃用 npm 元数据
provenance 布尔 是否有 npm provenance 签名 npm 元数据
name / version 字符串 包名 / 版本 npm 元数据
hostVersion 字符串 当前宿主版本 宿主注入
license / author / description / source 字符串 元数据 npm 元数据
npm.weeklyDownloads 数字 最近一周下载量 npm downloads API(仅当规则用到才查)

边界(会踩的地方)

情形 实际行为
字段名写错 保存时直接拒绝,提示「未知字段「repo.star」,可用字段:…」。不会再留一条永不命中的规则
条件语法不合法(缺比较符等) 保存时拒绝,提示「无法解析的条件」
对数组字段做比较(keywords / deps / scripts) 恒为 false——暂不支持 in / contains
字符串比较 区分大小写;== 对数字与字符串都按字面比
同一字段用不同表示 deprecated == true 可以,deprecated(裸写)不行——必须有比较符
多条自定义规则 互相独立,都要评估;命中几条就追加几条
等级 决定结论:reject > manual > warn > pass
与内置规则冲突 不会覆盖内置规则,只会叠加;内置规则说拒绝,自定义规则说通过,结论仍是拒绝
关掉某条自定义规则 从 customRules 里删除即不再评估(面板上点 ✕)

一条完整示例

{
  "id": "custom-abc123",
  "title": "冷门仓库",
  "when": "repo.stars < 50 && repo.idleDays > 90",
  "status": "warn",
  "detail": "{repo.slug} 只有 {repo.stars} star,且已 {repo.idleDays} 天没更新"
}

命中后报告会多一行:

! 冷门仓库:owner/repo 只有 12 star,且已 210 天没更新

运行期开关(热生效,无需重启)

侧栏「插件预检」面板里切换,或直接改 runtime.json:

{ "mode": "warn" }
  • enforce:预检拒绝时中止安装
  • warn:照常安装,只写审计

包里的 cordis.patch.yml 不写绝对路径;要固定数据位置,在你自己的 profile 补丁里覆盖 plugin-guard 行。

数据在哪

默认在 ~/.dsh/plugin-guard/(可用 dataDir 或 workspaceRoot 改):

文件 内容
runtime.json 运行期配置(模式、阈值、自定义规则)
audit/plugin-guard.jsonl 每次安装尝试一行(含放行、拦截、无法验证)
audit/last-block.md 最近一次拦截的人可读说明
audit/last-block.json 同上,结构化副本,供面板读取

自测

node scripts/selftest.mjs

41 项断言,覆盖:通过放行、拒绝拦截、警告放行、预检失败默认拦截、策略放行、本地路径跳过、模式热切换(warn→放行→enforce→再拦)、卸载还原、审计落盘与去重、审计轮转、请求准入(DNS rebinding / 局域网 / Origin)、降级时仍然拦截、运行期配置落在数据目录、对外文案不泄露本机路径、结构化报告、自定义规则校验(未知字段 / 语法错 / 非法等级 / 缺标题 / contains)。

不联网、不起子进程(预检用桩函数注入)。

自测只随 GitHub 仓库提供,不进 npm 包(files 里不含它)。

本地开发

git clone https://github.com/NingZhenPeng/dsh-plugin-preflight.git
cd dsh-plugin-preflight

克隆后必须做一次:启用钩子

钩子文件放在 githooks/(这样能随仓库分发),但 Git 默认只去 .git/hooks/ 找。 .git 不属于仓库内容,克隆不会带过来,所以必须显式告诉 Git 换位置:

git config core.hooksPath githooks

不做这一步的后果:提交与推送都不会被扫描,敏感内容可能直接进入公开历史。

启用后有两道本地防线:

钩子 时机 检查
pre-commit 提交前 暂存内容里的绝对路径 / token / 邮箱 / 私钥
pre-push 推送前 上面这些 + 提交身份必须 noreply + 版本与 CHANGELOG 一致 + 无私有目录

手动跑一次检查

node scripts/prepush-check.mjs

预期:✓ 推送前检查通过(版本 x.y.z,提交身份 N 条)

贡献方式

本仓库是发布产物:源码在维护者的私有工作空间,经白名单复制与扫描后出库到这里。 直接在本仓库改代码,下次出库会被覆盖——请提 issue 或 PR,由维护者回到源头修改。

发布清单

包名已改为 dsh-plugin-preflight(无作用域、无 private),可直接 npm publish。

发布前按顺序核对:

  1. 先跑自测:node scripts/selftest.mjs(应全绿),并把 package.json 版本与 CHANGELOG 最新条目对齐;
  2. npm pack --dry-run 看清单:确认 files 里是 11 项、无 node_modules、无 docs/audit 等运行期数据;
  3. 干净 profile 实装:不要在自己日常用的 profile 上试首次发布,新建一个临时 profile 装一遍;
  4. npm publish(无作用域包直接发;若改回 scope 需 --access public);
  5. 发布后去市场申请收录(dshmarket 界面右上角「申请收录插件」)。

发布过就要维护:宿主还在 0.2.x-rc 阶段,内部结构会变。每次 DSH 大版本升级后,都要回来确认 capabilities 探测是否仍全绿。

版本纪律:每次改行为都要同时动 package.json 的 version 和 CHANGELOG 对应小节——这个仓库曾出现 package.json 停在 0.3.0、CHANGELOG 已到 0.9.2 的漂移。

已知限制

  • 依赖宿主未公开的内部结构:pluginManager.installs、plugin-manager/install-log 事件、sidebar.panellist 槽位。宿主升级后可能静默失效——失效时闸门仍会拦截,只是界面提示与统计可能缺失。启动时会探测并告警,面板顶部也会显示「兼容性降级」。
  • 只覆盖走 pluginManager.installBundle 的安装。插件页「添加插件」与 dshmarket 市场走这条通道(已实测);CLI dsh plugin add、手改 package.json + pnpm install 绕得过去。
  • 第 6、7 条是关键词启发式,会漏也会误报。
  • 预检跑不成时默认拦截,离线环境需把策略改成「放行」。
  • 接口准入只防远程:本机进程可直接改 runtime.json,HTTP 层挡不住。

跨平台

平台 状态
Windows 实测(开发与验证环境)
macOS / Linux 未实测,但已消除静态平台假设

已确认没有的平台假设:

  • 生产代码里无 pwsh / powershell、无 process.platform 分支、无写死盘符路径([a-zA-Z]:[\\/] 只用于「判断是不是本地路径」的正则,跨平台通用);
  • 数据目录用 os.homedir(),路径全部走 node:path;
  • 解释器回退链:配置指定 → 宿主 node → 若宿主是 Electron,用 ELECTRON_RUN_AS_NODE=1 跑自己(macOS 桌面端可能没有 node 在 PATH 上,这条是关键)。

仍未验证的部分:macOS 桌面壳的 resources 路径、link: 依赖在 Unix 上是 symlink 而非 junction。

许可

MIT

—/ 5

No ratings yet

Verified DSH bundle

Commit 462e8eb1a7c7

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