dsh-tool-archive:为智能体提供确定性 tar 归档与安全解包

前言

在 DeepSeek Harness(DSH)里,模型可以通过工具读写文件、打包目录、解包归档。Shell 里「解个包」往往只是一行命令,但对被调用的进程来说,tar 成员名可以是 ../../.ssh/authorized_keys、绝对路径、指向树外的符号链接或硬链接——任意一种都可能把「解包」变成「往本机任意路径写文件」。

社区插件 dsh-tool-archive(维护者 yuehancn,GitHub:yuehancn/dsh-tool-archive)在 DSH 进程内提供一套零外部依赖的 tar 读写实现:打包时产出可复现的哈希与清单,解包前可只读清单、解包时对路径与成员类型做统一校验,并支持不解包或与目录/digest 比对验证。目录页:SkillHub - dsh-tool-archive。

这是什么

dsh-tool-archive 是 DeepSeek Harness 的归档工具箱插件(分类:模型推理)。它不调用系统 tar,而是在插件内自行实现 ustar 的写入与多种方言的读取,向 Harness 注册四个工具:archive_status、archive_pack、archive_unpack、archive_verify。

tar 没有中央索引,成员名、大小、类型散落在 512 字节块头里,因此「包里有什么」通常需要遍历归档而不是查表;该插件的设计也支持在不落盘解包的前提下列出内容并报告格式与约束。

四个工具分别做什么

工具 作用
archive_status 报告写哪种 tar、拒绝哪些成员类型、路径需满足什么条件、输出去哪
archive_pack 把目录打成 tar,返回每个文件的 SHA-256 清单与整树 digest
archive_unpack 列内容或解包;绝对路径、..、符号链接、硬链接、设备节点等一律拒绝
archive_verify 不解包核对:给目录比目录,给 digest 比 digest

确定性:同一内容树 → 同一 digest

若把 uid/gid/mtime、遍历顺序等直接纳入哈希,同一目录在不同机器或不同 readdir 顺序下会得到不同归档,清单比对无法区分「内容变了」和「只是环境不同」。

该插件在打包时采用固定规则:成员按路径排序;uid/gid 固定为 0;文件模式 0644、目录 0755;treeDigest 对「类型 + 路径 + 大小 + 内容哈希」做 SHA-256。README 说明:同一份内容在不同机器上 digest 与归档字节应一致;treeDigest 描述的是内容,改权限不会让 digest 变化,以便跨机器比对。

路径校验与安全边界

所有会落到文件系统的成员名经 resolveWithin / resolveEntry 处理。拒绝情形包括:绝对路径、.. 组件(含嵌在中段)、归一化后为空、空路径组件、含 NUL、符号链接/硬链接、设备/FIFO、未知类型标记、头部 checksum 不符等。

实现上强调:在 path.resolve 之前只保留路径的最后一段,避免 Windows 上绝对路径第二参覆盖输出目录;对 ././. 等需循环归一化到不动点;./a.txt 等归一化后安全的名字会放行。

解包时 strict 默认为 true:一旦有成员被拒绝,整体不写盘。读取比写入更宽容(兼容多种老 ustar 头布局),但名字长到纯 ustar 放不下会报错,不截断、不用 GNU long-name / PAX 扩展。

启用与配置

资料中的快速开始通过 Cordis 插件条目启用,并给出 config 字段示例(安装方式以你使用的 DSH 版本与 SkillHub / 仓库说明为准,勿凭仓库名自行拼接未在文档出现的命令):

- id: tool-archive
  name: dsh-tool-archive
  config:
    workDir: C:/work/project
    archiveDir: C:/work/project/archive-output
    extractDir: C:/work/project/archive-extract

package.json 要求 dsh >= 0.2.0-rc.2,peer 依赖 @deepseek-ai/cordis ~4.0.4、@deepseek-ai/dsh-tools >= 0.2.0-rc.2;插件版本 1.0.0,许可 MIT。

常用配置项(默认值见 README):

键 默认 说明
workDir . 相对路径解析基准
archiveDir archive-output 归档读写目录
extractDir archive-extract 解包根目录
exclude node_modules/.git/.DS_Store/Thumbs.db 按末段名跳过
maxFileBytes 0 单文件上限;0 表示不限
maxEntries 20000 超出会告警
maxTotalBytes 2 GiB 解包总量与读入内存上限
maxNameBytes 1000 成员名字节上限
hash true 打包时算 SHA-256
noOverwrite false 解包时是否拒绝覆盖
status/pack/unpack/verify true 分别开关四个工具

典型用法

在插件已注册的前提下,调用方式如下(路径相对 workDir / 配置的归档目录,以你环境中的 context 为准):

// 打包
await archive_pack({ source: "src" })
// → { file, bytes, files, directories, contentBytes, overheadPercent,
//     format: "ustar", treeDigest, manifest: [...], skipped: [] }

// 不落盘先看看里面有什么
await archive_unpack({ file: "pack.tar", listOnly: true })

// 解包(危险成员会被拒;strict 默认 true,一旦有拒绝就整体不写)
await archive_unpack({ file: "pack.tar", dest: "run-1" })

// 验证
await archive_verify({ file: "pack.tar", against: "src" })
// → { ok: true, treeDigest, unchanged: 5, added: [], removed: [], changed: [] }

本地跑插件测试:

node _test/run-all.mjs

README 称共 636 条断言、四个测试套件。

适用场景与限制

适合: 需要在 DSH 工作流里把项目目录打成可比对 digest 的 tar、在解包前审计清单、对不可信 tar 做 zip-slip 类防护、或与源目录做 verify 的场景。

务必注意:

  • 插件在当前 DSH 进程权限下运行;启用前应阅读源码与 MIT 许可,确认路径与上限配置符合你的安全策略。
  • 不压缩,输出为纯 .tar;不解符号链接与硬链接(刻意拒绝);不支持 GNU long-name / PAX,极深路径不适合用本插件打包。
  • 在大小写不敏感文件系统上解包时,归档内若同时存在仅大小写不同的两个名字,可能互相覆盖;插件在 planExtraction 阶段会检测并告警。

DSH 生态强调「一切皆插件」;SkillHub 等社区目录为独立站点,与 DeepSeek / 幻方无官方从属关系。

结尾

dsh-tool-archive 把「打包、列清单、受控解包、验证」收拢到同一套确定性 tar 与路径守卫里,适合作为模型侧归档操作的默认工具箱。进一步了解可访问 SkillHub 目录页 与 GitHub 仓库。

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

Xiaoye