← 返回列表
未验证
一个 DeepSeek…
尚未跑自动兼容性验证,可查看页面内的依赖与入口分析。 · 最近上游提交 2026/8/22 · 已提供中文文档
DeepSeek Harness (dsh) 插件:对已安装插件进行基于 AST 的真实扫描,以检测恶意意图模式,并可选配周期性计划任务以及在发现严重问题时自动隔离。默认仅提供建议,并非杀毒软件特征库。
综合分
27
GitHub 分
27
用户评分
—
★ Stars
0
周下载量
—
安装插件(需先安装 dsh CLI 引擎:npm install -g @deepseek-ai/dsh)
dsh plugin --profile web add rand0wn/dsh-malware-audit该插件未发布到 npm,走 GitHub 源安装(pnpm 若拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑)
信任档位:仅索引本站尚未对其实装验证,仅收录元数据
- 是什么
- dsh 原生插件 · market
- 装得上吗
- 本站尚未做安装检查
- 安全吗
- 本站尚未对该插件做风险分级(暂未覆盖,不等同于无风险)
- 还在维护吗
- 更新放缓:最近一次提交在 35 天前
档位由下列信号合成:本站实装验证(真实安装,当前最高到 L4)· 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。下方各区块是它的证据明细。 验证判据与等级说明 →
数据截至 2026/9/20(元数据每日更新 · 实装验证按队列轮转,单条结论的验证时间见上方)
依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/cordis-plugin-timer@deepseek-ai/dsh-commands@deepseek-ai/dsh-home-paths@deepseek-ai/schemastery用户评分
还没有人投票,来当第一个
订阅周报,不错过优质插件更新
每周一封 · 高评分插件 + 新用户活动
README
由 DeepSeek 最新模型翻译生成dsh-malware-audit
CI
一个 DeepSeek Harness(dsh)插件,它会扫描已安装插件的真实语法树,查找形似恶意意图的模式——这是所有其他 dsh 审计工具都明确拒绝去做的一件事——并可选地提供周期性调度,以及在发现严重问题时选择性地自动隔离。
为什么
dsh 插件运行时拥有真实的文件系统和进程访问权限,而安装一个插件只需一行命令。现有的审计工具(dsh-security-audit、dsh-plugin-audit)在能力方面做得很好——它们会报告某个插件可以访问文件系统、网络或凭据,然后明确止步于判断意图:“一种审计辅助工具,而非杀毒软件。”
dsh-malware-audit 填补的正是这一特定空白:它寻找那几种能将“这个插件能做很多事”与“这看起来像是在隐藏它的所作所为”区分开来的技术——从字符串动态执行代码、获取并执行、跨插件文件写入(这正是现实中某个热重载插件用来向其他已安装插件注入代码的技术),以及形似数据外泄的网络调用。
仍然不是杀毒软件。 这里没有已知恶意软件包的签名数据库——它无法告诉你某个特定的 npm 包被报告已遭入侵。它是一个基于一组小型固定规则的启发式扫描器。与早期版本相比的变化是:检测现在遍历真实的 TypeScript 编译器 AST,而不是匹配原始文本,因此注释、字符串字面量或 JSDoc 示例都不再能触发发现——只有真实语法才行。关于仍可能触发的情况,请参见已知误报。
安装
dsh plugin --profile add dsh-malware-audit
将该包名添加到该 profile 的 dsh.profile.bundles 列表中(仅作为列出的依赖项并不会激活插件的 dsh.bundle 补丁——原因请参见 dsh-minimal-anchor 的 README):
{
"dsh": {
"profile": {
"bundles": [
"@deepseek-ai/dsh-base",
"@deepseek-ai/dsh-web-app",
"dsh-malware-audit"
]
}
}
}
用 dsh --profile --dump-config 确认它已组合成功——查找一个 malware-audit 条目。
用法
在任何会话中输入 /scan-plugins。它会扫描所有其他已安装的、在其自身的 package.json 中声明了 dsh.bundle 的包——这正是插件生态自身的注册表用来表示“这是一个 dsh 插件”的标记——覆盖每个本地 profile,打印发现摘要,并将完整报告保存到当前工作目录下的 .dsh-malware-audit/scan-.txt。
默认情况下,这完全是只读且手动的。有两件事会让它更
默认处于活动状态,但除非你进行配置,否则两者都是选择启用且关闭的:
- scheduleMinutes — 按间隔自动运行相同的扫描,
无需命令。
- autoQuarantine — 在发现
critical 严重级别模式的扫描(计划或手动)中,自动隔离该插件。
配置
profiles//cordis.patch.yml
- insert:
- id: malware-audit
name: 'dsh-malware-audit'
config:
maxFiles: 400
maxFileBytes: 262144
ignoreRuleIds: []
ignorePlugins: []
scheduleMinutes: 0
autoQuarantine: false
| 字段 | 默认值 | 描述 |
| --- | --- | --- |
| maxFiles | 400 | 每个插件的文件数量预算,超过后对该插件的扫描会被截断。 |
| maxFileBytes | 262144(256 KiB) | 大于此值的文件会被跳过,而不是被扫描。 |
| ignoreRuleIds | [] | 要完全跳过的规则 ID — 有效 ID 见下表。 |
| ignorePlugins | [] | 要完全跳过的插件目录名 — 一个你已经信任、不想每次都重新扫描的插件。 |
| scheduleMinutes | 0 | 自动扫描之间的分钟数。0 禁用计划。低于 5 会被拒绝(记录日志,而不是静默钳制)。 |
| autoQuarantine | false | 在发现严重问题时自动隔离插件。开启此项前请阅读隔离。 |
它检查什么
| 规则 | 严重级别 | 它捕获什么 |
| --- | --- | --- |
| dynamic-eval | critical | 对 eval()、new Function() 或 vm.Script/runInNewContext/runInThisContext 的真实调用 |
| decode-then-execute | critical | 一个 base64 解码后的值(Buffer.from(x, 'base64') 或 atob())被直接传入 eval() 或 require() |
| fetch-and-execute | critical | 一个 child_process.exec/execSync 调用,其字符串参数通过 shell 调用 curl/wget 并管道传入 shell |
| cross-plugin-write | critical | 一个真实的 fs.writeFile/writeFileSync/createWriteStream 调用,目标是另一个插件的 node_modules 目录内的路径 — 一个真实的热重载插件用来向其他已安装插件注入代码的技术 |
| raw-ip-network | warning | 使用原始 IP 字面量 URL 而非主机名调用 fetch()/axios/http.request/https.request |
| env-exfil-shape | warning | 一个网络调用(如上所述),其自身的参数列表引用了 process.env |
| child-process-shell | notice | 任何 child_process.exec/execSync 调用 — 本身并不坏,但鉴于传给它的内容,值得一看 |
有意仅限定于 .js/.ts 源文件(不包括 .md/.json),并且
仅在每个插件自己的目录内(跳过其 node_modules、
.git、lib、dist、build)— 一个资源预算限制每个插件扫描的文件数
(400)和文件大小(256 KiB)。解析失败的文件会被
跳过,而不是被视为错误 — TypeScript 解析器有意
容忍错误,基本上从不抛出异常,但一个真正无法解析的
文件只是不贡献任何发现,而不是使扫描崩溃。
隔离
当 autoQuarantine: true 且扫描发现严重级别的模式时,
quarantinePlugin():
1. 将插件的 node_modules 条目移动到
.dsh-malware-audit/quarantine/-/。对于开发链接
安装(dsh plugin add /local/checkout,即 node_modules 中的符号链接),
这会移动符号链接本身——绝不会移动它所指向的真实检出目录。
已通过测试验证:真实目标目录及其内容在之后被确认仍然存在且未被改动。
2. 从每个本地配置文件的 dsh.profile.bundles 列表中移除该插件的名称,
只要该列表提及了它,覆盖所有本地配置文件。这一步不是可选的——留下一个
指向现已缺失的 node_modules 包的 bundle 条目,会导致下次启动时
硬启动失败(cannot resolve profile bundle),这正是本项目在自身开发
早期意外遭遇过的崩溃。隔离而不修复 bundles 列表,等于用“配置文件
完全无法启动”来换取“插件可能有恶意”,这严格来说更糟。
隔离不会做的事: 停止当前进程中已在运行的插件实例。从这里
没有同进程 API 可以处置另一个插件的活动 Cordis fiber——隔离在
每个受影响配置文件的下一次启动时生效,而非立即生效。如果定时扫描
在会话中途隔离了某个东西,该插件会继续运行直到下次重启。
要恢复被隔离的插件,将其目录从
.dsh-malware-audit/quarantine/ 移回配置文件的 node_modules 下,
使用其原始名称,并将其重新添加到该配置文件的 dsh.profile.bundles
列表中。目前还没有自动恢复命令——这是一个手动、刻意的步骤。
考虑到错误隔离会造成真实的中断(一个插件停止加载,可能还是你每天
都在用的插件,而依据只是一个明确并非恶意证据的启发式发现),
autoQuarantine 默认值为 false。只有在你手动运行过几次
/scan-plugins 并信任其对你实际安装插件的信噪比之后,才开启它。
定期扫描
将 scheduleMinutes 设置为大于 0 的值,即可在 harness 进程保持运行的
期间,按相同的时间间隔自动运行相同的扫描——通过
@deepseek-ai/cordis-plugin-timer 的 ctx.interval() 实现,dsh-base
已在每个配置文件中挂载了它。这是一个活动的、进程内定时器,而非
操作系统级别的 cron 任务:它在每次重启时重置,并且只在 dsh 进程
运行时触发,这对于 dsh web 的长期运行服务器来说已经足够,但也意味着
当 harness 本身未运行时什么都不会执行。
已知误报
这些是从测试套件中记录下来的,并非隐藏——AST 重写修复了最严重的
误报(注释、字符串、JSDoc 示例以及普通的 require() 都不再触发任何
东西——全部经测试确认),但仍有一些真实的误报存在,它们全都源于
规则形态本身,而非解析问题:
- raw-ip-network 会在知名基础设施 IP 上触发,例如 AWS 的
169.254.169.254 元数据端点或 169.254.170.2 ECS
凭据端点,这些在任何 AWS SDK 依赖自身的源代码中都是完全标准的——
属于预期情况,并非插件做错了什么,但仍然会被报告,因为仅凭字符串
字面量,规则无法区分“众所周知的基础设施地址”和“攻击者控制的地址”。
- env-exfil-shape 是一种同一调用表达式启发式,而非真正的数据
流——它只能捕获直接出现在网络调用自身参数列表中的 process.env,
而无法捕获几行之后的 const e = process.env; ...; fetch(url, {
body: e })。真正的数据流分析超出了本工具的范围;它能捕获的是直接、
粗心的情况,而非规避性的情况。
- cross-plugin-write 只识别一组固定的写入调用名称
(writeFile、writeFileSync、fs.writeFile、
fs.writeFileSync、fs.promises.writeFile、createWriteStream、
fs.createWriteStream)——通过重命名导入、包装函数或更低层的
fs.open/fs.write 文件描述符对执行的写入将无法匹配。
- decode-then-execute 的变量追踪是一个扁平的、全文件的、
无作用域的映射,而非真正的作用域分析——这是在实际运行中发现的,
而非通过审查发现的:早期版本只能内联捕获
eval(Buffer.from(x, 'base64').toString()),而完全遗漏了更常见的
const payload = Buffer.from(x, 'base64').toString(); eval(payload)
这种两条语句形式,而这正是验证期间使用的合成测试插件所采用的形式。
通过在整个文件中构建 name -> initializer 映射并对其进行检查来修复,
但这意味着在两个不相关的函数中重复使用的变量名可能发生冲突,并且
初始声明之后的重赋值不会被追踪。
工作原理
发现。 没有可从插件内部使用的清单服务
(@deepseek-ai/dsh-host-plugin-inventory 仅限远程、客户端侧),并且
从 import.meta.url 推导插件自身位置对于本地链接安装并不奏效——
dsh plugin add /local/checkout(pnpm link:)安装会使配置文件的
node_modules 条目成为一个符号链接,而 Node 的模块解析会在计算任何
相对于 node_modules 的路径之前跟随该符号链接到其真实位置,因此没有
任何原生 API 能报告加载器所使用的表观(相对于配置文件的)路径。在确定
真正的方法之前,通过对真实的开发链接安装进行直接测试予以确认:
@deepseek-ai/dsh-home-paths 的 dshHomePath() 从显式配置 / $DSH_HOME
环境变量 / ~/.dsh 解析 $DSH_HOME——从不通过文件系统遍历——因此它
不受此插件自身安装方式的影响。在此基础上,每个配置文件的 node_modules
都会被直接扫描(跨配置文件按真实路径去重),并过滤出声明了 dsh.bundle
的包。
该过滤器与发现机制同样重要:提升后的 node_modules 还包含每个真实
插件。这个扫描器的早期版本曾在一个真实的多插件配置文件上验证过,报告了 316 个包中的 30 个“发现”——几乎全是 zod 和 Node 自身类型声明中提及 eval() 的 JSDoc 注释,而非任何已安装插件实际执行的操作。只有声明了 dsh.bundle 的包——插件生态自身注册表所使用的同一约定——才算作需要扫描的插件。
检测。 scanText 使用 ts.createSourceFile 解析每个文件,并遍历真实的 AST(ts.forEachChild),匹配特定的节点形态——对 eval 的 CallExpression、命名 Function 的 NewExpression、对已知写入函数的 CallExpression 且其第一个参数是包含同级 node_modules 路径的字符串字面量——而非扫描原始文本。注释和无关的字符串内容是在任何节点存在之前就被解析器剥离的琐碎内容,因此它们在结构上无法触发发现;这正是修复早期基于正则的版本所存在的 JSDoc/注释/字符串误报的原因,也是让 cross-plugin-write 首次能够区分实际写入调用与普通 require() 的原因。
scanText/scanPluginDir 是纯函数/IO 分离的函数,由单元测试覆盖,包括证明每个已记录的误报修复的测试夹具(包含 eval(x) 的注释、引用 eval() 的 JSDoc 块、对同级插件的裸 require());findInstalledPluginDirs 针对真实的临时目录 $DSH_HOME 布局进行测试;quarantinePlugin 针对真实的符号链接目录进行测试,确认真实目标从未被触碰,只有链接被移动。
针对真实的本地 dsh web 启动进行了验证,而不仅仅是类型:与另一个真实插件以及一个合成的、声明 dsh.bundle 的插件一起安装,该合成插件植入了 eval()、一个 base64 解码后执行的模式,以及一个 child_process.exec 调用,通过配置了 ignoreRuleIds 的真实浏览器会话运行 /scan-plugins,并通过会话日志确认恰好是未被忽略的发现命中了合成插件,而真实插件上为零。
开发
npm install
npm run typecheck
npm test
scanPluginDir 不需要 dsh 运行时——examples/scan-standalone.ts 直接扫描磁盘上的任意目录,可用于在你甚至安装之前检查插件检出,或在你自己的插件的 CI 步骤中使用:
npx tsx examples/scan-standalone.ts /path/to/some/plugin/checkout
许可证
MIT