dsh 插件安全吗?安装前如何审查代码
dsh 插件会以 agent 权限运行,且与 harness 其他部分没有隔离。了解它能访问 API 密钥、文件、Shell 和子进程的范围,以及安装前的审查方法。
什么是 dsh 插件?它能做什么?
dsh 插件是 DeepSeek Harness 加载到自身进程中的 Node 包。安装插件后,插件中的代码会以 agent 的权限运行,并运行在 agent 已经能够访问的计算机上。已加载的插件与 harness 的其他部分之间没有隔离。因此,安装插件前应先确认其代码能够访问哪些资源,以及如何将访问范围控制在最小程度。
dsh(DeepSeek Harness)是 DeepSeek AI 开源的智能体运行框架,基于名为 Cordis 的插件框架构建。这里的“运行框架”非常关键,因为智能体运行框架是包裹模型运行的程序,负责控制循环、工具和权限;插件也正是在这一层接入。项目自己的 README 说明,所有组件都是插件。模型适配器是插件。您输入内容的 Web 界面也是插件。您从项目外部安装的任何组件,都会进入同一棵组件树,并与随项目发布的组件处于相同的信任级别。如果您还没有部署过它,请先在 VPS 上部署DeepSeek Harness,然后再回来向其中添加任何组件。
插件可以访问的扩展点列在仓库的 AGENTS.md 中。截至 August 2026,这些扩展点包括:
- LLM(large language model,大语言模型):您的 API key 所使用的提供商
- Shell:bash 能力,提供 local 和 pwsh provider
- Filesystem:受策略控制的文件访问
- Web:搜索和抓取 provider
- Subprocess:进程树 provider
- Workflow:worker thread
- Subagent:将任务委派给其他 agent
- Settings and credentials:已保存的配置和环境变量
插件还会在 ctx.tools 上注册工具。文档明确说明,已注册工具的 schema 会参与 prompt 组装。后半部分经常被忽略。插件即使不执行异常代码,也可以改变 agent 决定执行的操作,因为插件提供的描述会成为模型读取的文本。这与 针对编码 agent 的 prompt injection 属于同类问题,但有一个区别:这些文本会在安装插件时加入,并一直保留到您移除插件为止。
dsh 如何查找和加载插件?
没有全局插件目录。运行中的 dsh 是在启动时由多个有序层组合成的插件树,而保存这些选择的单元是 profile。$DSH_HOME 默认为 ~/.dsh,每个 profile 都位于 $DSH_HOME/profiles/<name> 中。web 和 headless profile 会在首次使用时根据随软件发布的模板自动创建。
profile 目录包含两个决定全部内容的文件:
package.json,其中包含树外插件依赖项,以及带有有序bundles列表的dsh.profile清单cordis.patch.yml,这是覆盖这些软件包的自定义补丁层
ls ~/.dsh
ls ~/.dsh/profiles/web启动时按以下顺序应用各层,后应用的层优先:
- 空根层
- profile 的软件包,顺序以清单中的列表为准
- profile 的
cordis.patch.yml $DSH_HOME/cordis.patch.yml- 命令行传入的所有
--patch <path>覆盖层
以下两个标志会打印组合结果,但不会启动任何内容:
dsh --profile web --dump-default-config
dsh --profile web --dump-config--dump-default-config 仅打印组合后的树。--dump-config 会加入 profile 层和主目录补丁层,因此它最接近于准确列出下一次启动将加载的内容。在信任继承来的机器之前,先查看此结果。
需要注意这些补丁文件。这里的配置并非静态数据,因为该格式允许在插件的 config 块中使用带 !!js 标签的值。论坛帖子中复制的 cordis.patch.yml 片段属于代码,应按照处理同一来源 shell 脚本的方式处理。
dsh plugin add 实际运行什么?
dsh plugin --profile <name> <args> 会将其参数转发给该配置文件目录中的 pnpm,因此 PATH 中必须包含 pnpm。这里使用的是 pnpm 的命令:
dsh plugin --profile web add '<package-or-git-spec>'
dsh plugin --profile web remove '<package-name>'
dsh plugin --profile web why '<package-name>'
dsh plugin --profile web update安装 dsh 插件的安全模型,本质上与安装任何 npm 风格依赖项相同,只是多了一个步骤:安装结果会加载到您的代理进程中。该软件包会携带自己的依赖树,其中的每个软件包最终都会运行在同一进程中。npm 供应链攻击如何到达服务器中介绍的内容在此完全适用。
pnpm 10 及更高版本默认不会运行依赖项的构建脚本;是否允许运行由每个软件包单独通过 onlyBuiltDependencies 或 pnpm approve-builds 批准。请检查您使用的 pnpm 版本:
pnpm --version这个默认设置确实有价值,但它也是整个生态系统中最容易被过度解读的安全功能。被阻止的构建脚本会阻止代码在安装期间运行,但无法防护插件本身,因为插件的作用就是由该运行框架导入,并在下一次启动时调用。插件不需要 postinstall 钩子。因为它本来就是被允许加载的。
安装 dsh 插件前应阅读的内容
下载已发布的 tarball 并阅读其内容。解压归档文件不会执行任何操作。
npm pack '<package-name>@<version>'
tar -tzf '<package-name>-<version>.tgz'
tar -xzf '<package-name>-<version>.tgz'
less package/package.jsonpackage.json 中的 4 个字段可以提供大部分必要信息。阅读 scripts,了解 preinstall、install 和 postinstall 条目。对于不认识的名称,或与已知名称仅相差 1 个字符的名称,阅读 dependencies。对于软件包希望添加到 PATH 的内容,阅读 bin。阅读 main 或 exports 查找入口文件,然后打开该文件并继续检查。
接着阅读实际会被加载的代码。一个声明提供通知工具的插件,没有理由读取 ~/.ssh、调用你从未听说过的主机,或启动 shell。如果软件包只提供捆绑或压缩后的 JavaScript,而公共代码仓库中没有对应的源代码,那么这已经说明了问题。优先选择可以阅读源代码的插件,并优先选择规模较小的插件。
你也可以在不安装任何内容的情况下查询注册表:
pnpm view '<package-name>' dependencies
pnpm view '<package-name>' versions一个上周发布、只有 1 个版本、没有 repository 字段,且名称仿冒热门软件包的包,是任何注册表中最常见的老把戏。使用校验和验证下载内容是紧密相关的做法:在允许运行之前,明确知道你下载的内容。
固定版本,并保留锁定文件
浮动版本范围意味着,每次安装或更新时,agent 进程中的代码都可能在你未作决定的情况下发生变化。请固定版本。
dsh plugin --profile web add --save-exact '<package-name>@<version>'不同 pnpm 版本的选项位置可能不同,因此请检查结果,不要盲目相信命令。随后打开 profile 的 package.json,确认依赖项显示为不带 ^ 或 ~ 前缀的纯版本号。该文件决定实际安装的内容。
然后保留锁定文件。它固定的是完整的传递依赖树,而不只是顶层名称:
find ~/.dsh -maxdepth 3 -name 'pnpm-lock.yaml'将其与 profile 的 package.json 一起复制到备份位置。这两个文件可以在新服务器上重建相同的依赖树。只有在决定切换版本时才运行 dsh plugin --profile web update,不要将其作为例行整理命令。运行后请检查锁定文件的差异。
如果插件来自 git 而不是软件包注册表,请固定提交,而不是分支。形如 github:owner/repo#<full commit sha> 的规格会提供固定的依赖树。分支名称会在 pnpm 下次解析时指向该分支当时包含的内容,相当于把决定交给了其他人。harness 本身也需要遵循同样的原则,因为每个已发布的 dsh 构建都是候选版本,而未固定的安装在任何一天都可能解析到不同的版本。这是大多数 dsh 安装和版本错误的来源。
插件市场,以及“精选”到底有多大价值
dsh 提供插件市场,并以插件形式安装,这能说明其架构的一些特点:
dsh plugin --profile web add dshmarket重启后,插件会显示在 Settings 下的 Plugin Market 中。其 README 明确说明了限制。插件只能从精选注册表列出的来源安装,其他来源都会被拒绝。默认情况下会阻止构建脚本;如需启用,必须逐个软件包批准。终端插件在加入 Web 配置文件前会被标记。最重要的一句话是:列出插件不代表认可,因为这些插件是第三方代码。
精选列表可以提高最低安全水平,但不会替您审查代码。维护者账户易手后,它也无法告诉您插件的下一个版本会执行什么操作。对待一键安装时,应像对待同一作者提供的 curl | bash 一样谨慎。README 中还有一句话值得再次强调:导出的备份可能包含个人配置文件中的凭据,因此绝不要将其附加到公开 issue 或粘贴站点。如果您需要的是起始插件列表,而不是安装方法,请参阅配套文章 值得安装的 dsh 插件。
以独立用户运行 dsh,不要使用 root
审查可以降低恶意内容进入系统的频率。最小权限决定恶意内容进入后能够接触什么。在 VPS 上,设置这一层防护的成本很低。
为该运行环境创建独立的 Unix 账户和独立的 home 目录,绝不要以 root 身份运行:
sudo adduser --disabled-password --gecos "" dshrun
sudo -iu dshrun在该会话中启动运行环境,使其在该账户的 home 目录下写入数据:
npx @deepseek-ai/dsh webWeb UI 默认在 http://127.0.0.1:3080 上提供服务。保持此设置不变。如果您曾在另一台计算机上点击输出的链接却没有收到响应,dsh 输出该地址的含义解释了原因。任何能够访问该端口的用户都可以控制一个带有 shell 的代理,因此公开 3080 端口等同于公开一个带有友好界面的无 root 远程 shell。请改用 SSH 隧道从笔记本电脑访问:
ssh -L 3080:127.0.0.1:3080 you@your-vps然后确认没有进程监听公网地址:
ss -lnt | grep 3080本地地址应显示为 127.0.0.1:3080。如果显示为 0.0.0.0:3080,那么防火墙就是陌生人与您的代理之间的唯一屏障。在 VPS 上安全运行 Claude Code的原则同样适用于 dsh。为代理指定一个允许其任意修改的工作区目录,并将所有无法重建的数据保留在该计算机之外。更稳妥的做法是将该主机视为用于代码代理的一次性 VM,因为重建 VPS 只需 1 小时,而审计一台 VPS 则需要 1 周。
密钥存放在哪里,以及文件权限为何不够
dsh 将 API 密钥保存在 $DSH_HOME/.credentials.yaml 中,将环境变量值保存在 $DSH_HOME/.env 中,将模型设置保存在 $DSH_HOME/settings.yaml 中,并将会话历史记录保存在 $DSH_HOME/storages 下。每个文件应存放哪些密钥,以及每种模式下哪些内容实际会离开本机,是配置 dsh 的 API 密钥、模型和端点要解决的问题。在添加插件前,应先确定这些设置,因为接入的每个密钥都可能被插件读取。请锁定以下两个敏感文件:
chmod 600 ~/.dsh/.credentials.yaml ~/.dsh/.env
ls -l ~/.dsh600 模式允许所有者读取和写入,并禁止其他用户执行任何操作。无论使用哪种表示方式,这一点都很重要(chmod 的数字模式与符号模式)。但应明确它的保护范围。文件权限只能防止本机上的其他账户访问这些文件。它无法防止插件读取这些文件,因为插件以文件所有者的身份运行,并且位于读取这些文件的进程中。因此,让 AI 代理无法接触机密意味着根本不要将机密放在这台机器上。dsh 所在的机器只应保存它需要的那个模型密钥。云凭据和签名密钥应存放在其他位置。
读取网页的插件为何会改变威胁模型
Web 接口为插件提供搜索和抓取提供程序。插件将网页加载到会话中时,加载的是攻击者可以控制的文本。模型提示不会区分指令和数据,因此抓取的网页可能包含一行直接写给代理的内容,而持有 shell 功能的执行框架只需代理执行一步,就可能运行这条指令。
执行控制已经存在于执行框架中。dsh-base 是每个配置文件中的第一个组件包,提供沙箱和审批策略。请使用它。能够抓取不可信网页的会话,应对所有写入或执行操作要求审批,避免抓取到的指令自行变成操作。通过审批控制代理操作介绍了如何确定这条边界的位置。这种关系也是双向的,因为您自己的服务器也是其他代理会抓取的网页;关于这一点,请参阅在服务器上阻止 AI 爬虫。
如何检查插件修改了哪些内容?
先创建快照,安装插件,再创建快照,最后查看差异。
dsh --profile web --dump-config > /tmp/dsh-before.txt
dsh plugin --profile web add '<package-name>@<version>'
dsh --profile web --dump-config > /tmp/dsh-after.txt
diff -u /tmp/dsh-before.txt /tmp/dsh-after.txt差异会显示插件安装过程向组合树中添加了哪些条目。为实现一个小功能而安装的插件,如果添加了多个无法解释的条目,就应先停止操作并阅读源代码,再启动它。dsh plugin --profile web why <package-name>回答的是另一个问题:哪些直接依赖引入了指定的软件包。
已安装的软件包位于 $DSH_HOME/profiles/node_modules 下,因此您也可以查看磁盘上的目录树:
ls ~/.dsh/profiles/node_modules保留一个从不用于实验的备用配置文件。安装操作破坏测试框架后,启动 dsh --profile <clean-name> 即可在几秒内确认问题是否由该插件导致。
如何移除 dsh 插件?
dsh plugin --profile web remove '<package-name>'
dsh --profile web --dump-config > /tmp/dsh-after-removal.txt
diff -u /tmp/dsh-before.txt /tmp/dsh-after-removal.txt移除依赖项并不总会删除配置。写入配置文件中 cordis.patch.yml 的条目会保留,因为该文件属于您,运行框架不会替您重写。打开该文件,删除其中所有列出已移除软件包的代码块。
less ~/.dsh/profiles/web/cordis.patch.yml然后处理任何卸载命令都无法解决的问题。如果您因为不再信任某个插件而将其移除,那么它能够读取的内容已经被读取。请在提供商控制台中轮换 DeepSeek API 密钥,并轮换 $DSH_HOME 中存放的其他所有密钥。然后确认该插件运行所使用的 Unix 账户能够访问您网络其他位置的哪些内容。
简明版
- 安装前阅读已发布的 tarball,从
scripts和入口文件开始 - 固定确切版本;对于 git 规范,固定确切提交,并保留 lockfile
- 安装到一个 profile 中,并保留一个干净的 profile,以便出现问题时启动
- 每次安装前后都对比
--dump-config - 使用独立的 Unix 用户运行 harness,仅监听 loopback,并通过 SSH 访问
- 在服务器上只保留一个 API key;移除不再信任的插件当天轮换该密钥
这些都不是避免使用插件的理由。插件模型正是 dsh 有用的原因,而无法扩展的 harness 终究需要替换。这样做是为了明确安装了什么、来自谁、使用哪个版本,并将整个系统运行在可以重建的环境中。
FAQ
dsh 插件之间是否相互隔离?
不是。插件通过 Cordis 加载到 harness 进程中,可以访问文档所述的能力接口,包括 shell、文件系统、Web、子进程、子代理和凭据。dsh-base 是每个配置中的第一个 bundle,提供沙箱和审批策略,用于控制代理工具可以执行的操作;您的防护就来自这项策略。插件之间没有单独的权限边界,因此更准确的理解是:安装插件意味着您信任其作者,以及其依赖树中的每个软件包。
不运行安装脚本,也可以安装 dsh 插件吗?
pnpm 10 及更高版本默认阻止依赖项构建脚本,dsh plugin ... add 会转发到 pnpm。因此,在当前版本的 pnpm 中,除非您批准相应软件包,否则安装不会运行软件包脚本。使用 pnpm --version 确认版本。未读取的插件并不会因此变得安全。下一次启动时,harness 会主动加载插件并运行其代码,任何安装阶段的限制都不会影响这一点。
dsh 插件及其配置实际存放在哪里?
$DSH_HOME 默认为 ~/.dsh。配置文件位于 $DSH_HOME/profiles/<name>。每个配置文件都包含一个 package.json,其中记录插件依赖项;还包含 dsh.profile 清单,用于定义 bundle 的顺序,以及 cordis.patch.yml 补丁层。已安装的软件包位于 $DSH_HOME/profiles/node_modules 下。密钥位于 $DSH_HOME/.credentials.yaml,环境值位于 $DSH_HOME/.env,用户主目录级别的 $DSH_HOME/cordis.patch.yml 会应用于所有配置。运行 dsh --profile web --dump-config 可在不启动的情况下查看合并后的结果。
从 dsh 插件市场安装是否安全?
该市场只允许从经过筛选的注册表来源安装,并会阻止构建脚本,除非您逐个批准相应的软件包。这比从聊天窗口粘贴软件包名称已有明显改进。但其 README 仍明确说明,收录不代表推荐,因为这些插件是其他人编写的第三方代码。请阅读源代码并固定版本。请将 harness 运行在您能够承受丢失的用户账户上,最好运行在您能够承受丢失的机器上。