DSH | dsh-doctor + dsh-security | 诊断与安全检查工具:40+31 项检查,含 dsh 起不来时的自救与升级前后复检 #6678
Replies: 5 comments
|
这套覆盖面很实用,尤其 给 remote catalog 提一个候选检查(你们的 profile 组里似乎没有):已装插件声明的 host peer 范围 vs 磁盘上实际安装的 host 版本。这类不匹配是升级后无法启动的常见原因之一,而且可以完全离线判定。 规则要点(踩过的坑都在这):
一个真实案例: 我这边把同一套规则实现成了单个检查( |
|
这条提醒正好命中我们自己的一个 bug —— 谢谢,这是本次讨论里最有价值的一条反馈。 你说的 strict semver 误报,我们确实踩了。 我们的安全层( 第一行就是我们 已经改成你描述的三态 + rc 规则(并逐例对齐):
你的检查我们实现进了 profile 组(如你所说,它该在默认运行里,而不是只在 以及两条"不得误报": 关于 顺带回答你关于 catalog 的一点:我们的远程 catalog 是数据驱动的(探针类型 + 正则/JSON/文件判定),像 peer 范围这种需要逻辑的检查进不了 catalog,只能落在内置检查集里——所以它现在是内置的 P19,随版本发布。 (两处修复均已发版: |
|
既然现在生态里有两份独立实现(你们的 方法:从我们的生态快照里取 1639 条「精确范围 × 已装版本」逐条比对。脚本可复用,任何导出 node scripts/range-difftest.mjs --other <module.mjs> --corpus compat.jsonhttps://github.com/ciceroyang/dsh-doctor/blob/main/scripts/range-difftest.mjs 结果:58/1639 判定不同,9 类形态,两个方向都有。机制很明确,两类: A. 部分版本被当成不可解析(31 条:我们 satisfied / 你们 unsatisfied)
这一类会让 P19 误报健康的声明(而它报 warn),和你们刚在 SP5 修掉的是同一类问题、不同来源。修法:partial 补 0( B. 预发布降级的位置(35 + 2 条,两个方向)现在的规则是在失败之后判断
建议按两次判定合并,而不是在失败路径上打补丁: 这样 A 与 B 同时消失,也与 #1719 里「未知不得折叠成任一确定态」一致。 两句说明
快照每周更新,随时可复跑:https://github.com/ciceroyang/dsh-doctor/releases/tag/ecosystem-compat |
|
把上一条里的规则写成了文档,方便你们(或任何第二实现)直接对齐而不必从对话里拼: https://github.com/ciceroyang/dsh-doctor/blob/main/docs/host-peer-declarations.md 含:解析顺序(跟随软链接 / 作用域名两段 / 内置与相对路径排除)、三态语义、以及那张 strict/numeric 两次判定 的合并表和你我踩过的四个角落( 等 P19/规则的修改落地后,我这边用 |
|
跑完你的 harness,两条语料都是 0 分歧: (修复前是 58 处,全部落在你点名的四个角落。) 但过程中发现一处规范与实现不一致,建议先钉住再让别人对齐 —— 因为它正是产生分歧的地方: 文档表格写的是 而你的实现(
两者对旗舰用例给出相反的结论: 另外两个角落我们也补齐了:部分版本( 我们把一致性做成了回归测试: 最后采纳了你的一条生态发现:503 仓库里 162 个声明了 bundle 却完全未声明 host 范围——按你的规则 1 那是"无信息",不能读成兼容。P19 现在会在 detail 里如实计数(我们本机:78 条声明已核对 / 26 条未知 / 5 个 bundle 未声明),但不生成发现:声明与否是作者的选择,消费者该做的只是别把它读成"已核对"。 已发版 |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Two open-source tools for the DSH ecosystem — a diagnostics CLI and a security-check framework. Both are ordinary npm packages; nothing here needs a patched host.
@moonquake2004/dsh-doctor— 40 checks in three groups.envshape, node-pty native binding, storages JSON, contract anchors,.npmrcworkaround,dshin PATH, settings writability, runtime zstd stabilityfile:deps, named-import/export mismatch, manifest version, client bundles, dist-tag health, provenance verifiabilityIt emits the
dsh-doctor/v1envelope (name + status + exit code) so CI and support scripts consume it uniformly, and it can pull new checks from a remote catalog without a release.@moonquake2004/dsh-security— 31 checks in four layers!!jsconfig-as-code, Code-Mode × sandbox mismatch, prompt-injection surface in plugin content, provenancePlus optional integrations with four community tools (poison-guard, plugin-reducer, ecosystem; sandbox-audit retired after we found it was never published).
The part most people actually need: when dsh will not start
Installing an incompatible plugin, or upgrading dsh against older plugins, is the most common way to end up with a host that refuses to boot — and at that point you cannot use dsh to diagnose it. These are plain Node CLI (
npx) and do not need dsh to boot:--boot-checkimports each enabled entry in a subprocess and classifies the failure (missing export / not installed / native ABI / module format / hang) — that is exactly what a boot does, so it is an explicit opt-in flag rather than default behaviour.--quarantineedits only the profile's boot list (it never deletes packages) and always writes a timestamped backup first.What this work surfaced
Everything below has a reproduction in the linked thread, not just a claim:
dsh webstartup — one report had it happen three times in five days (Corrupt session log (first frame is not the header) blocks `dsh web` startup and session listing #6651). Our own whole-store scan used to call such a log "healthy"; we treated that as a bug in our tool and fixed it.!!jsis evaluated in the host process at load vianew Function("ctx","expr"," with (ctx) { return eval(expr) }"), outside the agent sandbox. Two plugins on our machine use it; we published a dossier and opened an issue upstream.namebut noversionthrows in the package inventory as soon as a loose local module is resolved (Bug: versionless profile manifests break DeepSeek requests for local modules #6667) — and the manifest the harness itself generates has exactly that shape.Interop and posture
dsh-doctor/v1envelope, and follow the v1 vocabulary alignment for check names and severities (💡 Idea: Add 'dsh doctor' CLI command for environment & dependency diagnostics #1719). What we could not verify is published as unverified.SECURITY.mdand GitHub private vulnerability reporting enabled. We treat false negatives (a check that silently stops matching after a field moves) and high-noise false positives as vulnerabilities of the tool — both end with the operator ignoring it.Honest limits: most checks are static/offline; we cannot verify Windows-specific behaviour from macOS (CI now runs Ubuntu + Windows × Node 22/24, which immediately found a real Windows-only bug in one check); the runtime checks read the latest settled session, so they deliberately do not judge the session you are in right now.
Counterexamples, wrong-verdict reports and corrections are the most useful thing you can send. Check lists and sources: dsh-doctor · dsh-security
中文版 / ZH
这是给 DSH 生态做的两个开源工具:一个诊断 CLI、一个安全检查框架。都是普通的 npm 包,不需要改动宿主。
@moonquake2004/dsh-doctor—— 40 项检查,三组.env形态、node-pty 原生模块、storages JSON、契约锚点、.npmrcworkaround、dsh是否在 PATH、settings 可写性、运行时 zstd 稳定性file:依赖悬空、命名导入/导出缺失、manifest 版本、client 产物、dist-tag 健康度、来源可验证性输出统一采用
dsh-doctor/v1信封(检查名 + 状态 + 退出码),便于 CI 与支持脚本消费;新检查可从远程目录拉取,无需发版。@moonquake2004/dsh-security—— 31 项检查,四层!!js配置即代码、Code Mode × 沙箱错配、插件内容里的提示注入面、来源可验证性另含对四个社区工具的可选集成(poison-guard、plugin-reducer、ecosystem;sandbox-audit 因"从未发布到 npm"被我们退役)。
大多数人真正需要的那部分:dsh 起不来时怎么办
装了个不兼容的插件,或升级 dsh 后与旧插件不兼容,是最常见的"宿主拒绝启动"来源——而那时你已经没法用 dsh 自己来诊断。下面这些是普通 Node CLI(
npx直跑),不需要 dsh 能启动:--boot-check会在子进程里真的去 import 每条启用的 entry,并归类失败原因(缺导出 / 没装全 / 原生 ABI 不匹配 / 模块格式 / 顶层卡死)——这正是"启动"本身的语义,所以它是显式开关而非默认行为。--quarantine只改 profile 的启动列表(从不删除任何包),且必定先写带时间戳的备份。这轮工作暴露出的东西
下面每一条都在对应帖子里有复现,不只是结论:
dsh web启动——有报告 5 天内遇到 3 次(Corrupt session log (first frame is not the header) blocks `dsh web` startup and session listing #6651)。我们自己的全库扫描此前会把这种日志报成"健康",我们把那当作我们工具自己的 bug 修掉了。!!js是在宿主进程内、沙箱之外于加载期求值的(new Function("ctx","expr"," with (ctx) { return eval(expr) }"))。本机有两个插件在用它;我们发布了分析档并向上游提了 issue。name但没有version,会在解析到"游离本地模块"时让 package inventory 抛错(Bug: versionless profile manifests break DeepSeek requests for local modules #6667)——而 harness 自己生成的 manifest 恰恰是这个形状。互通与安全姿态
dsh-doctor/v1信封,检查名与严重度遵循 v1 词汇表对齐(💡 Idea: Add 'dsh doctor' CLI command for environment & dependency diagnostics #1719)。我们没能验证的内容,一律标注为未验证。SECURITY.md,并已开启 GitHub 私有漏洞报告。我们把假阴性(检查因字段搬走而静默失配)与高噪声假阳性都当作工具的漏洞——两者的终点都是"用户开始忽略它"。如实说明边界:多数检查是静态/离线的;我们在 macOS 上无法验证 Windows 特有行为(CI 现已覆盖 Ubuntu + Windows × Node 22/24,加上的第一次就抓出一个真实的"仅 Windows"缺陷);运行时检查读的是最近一个已停止写入的会话,因此有意不去评判你正在进行的这一场。
最有用的反馈是反例、误判报告与纠正。 检查清单与源码:dsh-doctor · dsh-security
Live demo(输出原文照抄,未编辑)
在 macOS / Node 24 的一个一次性临时 profile 上跑出,复现的正是社区真实遇到的那一类失败:插件导入了一个已被移除的导出。
三条命令,dsh 又能起来了——原始
package.json已备份、一行命令可撤销。然后你可以从容地升级那个插件。同一流程的英文版(English version of the same flow)
如实说明:诊断消息目前是中文(英文输出模式在待办上);上面第二段是把同一份输出译出来方便阅读,命令行实际打印的是中文。若你要英文输出,欢迎在 issue 里说一声——这也是判断优先级最直接的方式。
升级前记一次基线(真实 profile,未编辑)
一个具体例子:一条社区评论怎样删掉了我们的一个误报
介绍帖里说"我们把误报当漏洞"是个主张;这里给一个有编号、有日期的实例,因为它就发生在本贴的评论区。
1. 提案与提醒。 @ciceroyang 在本贴提议增加一个检查(已装插件声明的 host peer 范围 vs 实际提供的核心版本),并附了一条提醒:DSH 生态几乎全在预发布上,而 strict semver 的预发布规则要求"比较器与被判版本处于同一
[major,minor,patch]元组"才允许匹配,于是在全员 rc 的生态里会产生误报 ——>=0.1.0-rc.5 <0.2.0应当接受0.1.5-rc.2,字面规则却会拒绝。2. 我们用真 node-semver 复核,他说得对:
3. 而我们的代码正是这样写的。
dsh-security的 SP5 判定"插件声明的核心范围是否接受实际安装的核心"时,直接调satisfies(..., { includePrerelease: false })—— 因此它会把一个健康的插件报成"不支持当前核心"。这不是别人的坑,是我们自己的:SECURITY.md里我们把"误报健康环境(用户因此开始忽略工具)"明确列为工具的漏洞,而它正发生在同一时刻我们发出去的版本里。4. 改法(两条一起,避免两个组件用不同语义):
checkRange改为三态 + rc 规则:区间含任何预发布比较器 → 按数值判定(接受0.1.5-rc.2,拒绝越界者);纯 release 区间面对预发布安装版本 →unknown;SP5 只对unsatisfied生成发现,unknown既不算兼容也不报。dsh-doctorprofile 组,默认运行即覆盖,不必带--security),并逐条照做他列的坑:只取@deepseek-ai/*、host 版本跟随软链接(file:/dev 安装与共享镜像根都是软链接,用 lstat 风格会误报)、作用域名按第一段斜杠切、三态。他自己的真实案例现在可复现:dsh-win32声明>=0.1.0-rc.5 <0.1.0-rc.7而实际提供0.1.5-rc.2→ 报出。5. 回归测试里有两条是"不得误报"(正是上面第 2 步的第一、三行),另有一条确认真实越界仍要报。
dsh-security@0.4.5、dsh-doctor@0.4.23已发布。我们为什么把这个写进介绍帖:一个工具的检查质量取决于它愿不愿意承认自己报错过。这条规则不是我们想出来的,是我们写错、被社区纠正、然后改对的;把它记在公开处,比任何"高准确率"的说法都更能说明你拿到的东西是怎么维护的。规则与用例的原始实现见 @ciceroyang 的仓库(本贴评论区有链接),P19 的代码注释里也标注了出处。
如果只带走两条命令
前面讲了能力,这里只留"明天就能用上"的部分。
① 装插件时直接用它来装 —— 不要让"装完才发现起不来"发生:
装完立刻验证;坏了自动隔离它、dsh 照样能起来;隔离也救不回则整体回滚——最坏情况只是一次无害的失败尝试。
② 升级 dsh 前记一次基线 —— 升级是最容易"升完就起不来"的时刻:
已经起不来了? 三条命令,同样不需要 dsh 能启动:
一条经验法则:在重启 dsh 之前跑一次
--boot-check。此时 dsh 还活着,你能顺手把问题处理掉;等它起不来再查,代价总是更高。If you take away two commands
① Install through it, so "installed it, now nothing boots" stops happening:
It verifies immediately after installing; on failure it quarantines the offender (dsh still starts); if even that cannot save it, it rolls back to the pre-install manifest. Worst case becomes a harmless failed attempt.
② Record a baseline before upgrading dsh — the moment breakage is most likely:
Already broken? Three commands, and again none of them needs dsh to boot:
One rule of thumb: run
--boot-checkbefore restarting dsh. While it is still running you can fix things calmly; after it refuses to start, every step costs more.All reactions