DeepSeek Harness 在 exFAT 工作区 ACL 授权成功却没效果?沙箱与版本令牌排查

故障排查发布于 2026-10-20作者: DeepSeek Plugin 插件市场
DeepSeek HarnessACL 沙箱exFATFILE_PERSISTENT_ACLS版本令牌
DeepSeek Harness 在 exFAT 工作区上 ACL 沙箱授权返回成功却完全不起作用,同尺寸外部改写还会被静默覆盖:根因是 grant 不探测卷能力,而版本令牌在 2 秒时间戳卷上退化。给出 fail-closed、换 NTFS、关沙箱只读三条路径。

在 DeepSeek Harness 的 exFAT / FAT32 工作区上,ACL 沙箱的 grant 会返回成功,但卷上根本不保存 DACL,受限子进程照样能写工作区;同一类卷还会让「文件已变」的版本令牌退化成 size 加 2 秒 mtime。 两个失效都不报错:前者是安全边界失效,后者是数据完整性缺口,排查时必须分开看,也不是某个 DSH插件 的配置问题。据讨论,报告者在 Windows 与 exFAT 工作区上实测,社区成员随后逐行核对了 sandbox-windows-acl 与 session-persistence-jsonl 的源码(讨论原文)。

DeepSeek Harness 在 exFAT 工作区上「授权成功但零效果」是什么样

表现是 grant 不报错、工具调用不被拒绝、工作区文件仍可被任意写入,三项同时成立。 按下面的顺序确认,别一上来就改配置:

  1. 在 PowerShell 执行 Get-Volume -DriveLetter E | Select-Object DriveLetter,FileSystem,预期看到 exFAT 或 FAT32;若显示 NTFS,本文两条失效都不适用。
  2. 确认工作区根到底是卷根还是子目录:(Get-Item E:\proj).FullName。据讨论,包内 README 对「grant root 本身是 FAT 卷」与「granted roots 之外的 FAT 目标」给了两句不同的话,结论要按实际层级收窄。
  3. 让 Agent 在工作区里新建一个文件,再用另一个进程改写它,预期两个动作都不被拦截。
  4. 执行 icacls E:\proj 记录一次输出,触发一次 grant 后再记录一次,预期两次完全一致——没有新增 ACE,说明授权没有落到卷上(社区核对源码)。

exFAT 上的写入失败还有另一副面孔:EISDIR 与盘根 EPERM mkdir,见 exFAT 工作区写入报 EISDIR 排查。

同一台机器上换成 NTFS 目录做对照,差别会非常直接:

观察项exFAT 工作区NTFS 工作区
grant 返回值成功成功
icacls 前后差异无有新增 ACE
受限子进程写工作区允许拒绝

为什么 grant 不报错:它只兜 Win32 错误,不探测卷能力

grant.ts 的 fail-closed 只覆盖「Win32 调用返回错误」这一种情况,而 exFAT 上 SetNamedSecurityInfoW 返回的是 ERROR_SUCCESS。 没有错误可抛,throwLastError 和 AggregateError 就没有触发点。社区核对源码后的链条是:

  1. packages/sandbox/sandbox-windows-acl/README.md 的 :190 写着「FAT 卷作为 grant root 会 fails loudly」。
  2. 同一份 README 的 :204 又写着「对 FAT 类卷的 warn-only 姿态已记录但未实现」,两句话互相矛盾。
  3. grant.ts 里没有 GetVolumeInformation,也没有任何卷能力探测;实现把「调用成功」等同于「DACL 已写入」。
  4. exFAT 没有安全描述符存储,调用不报错也不保存,受限令牌拿不到任何限制;于是失败既不响亮,也不可观测。

这说明问题不是某个分支写错,而是「README 承诺需要的能力探测」在实现里根本不存在。修复方向也随之确定:把探测补上,而不是在写入失败时再猜原因。

版本令牌为什么漏检外部改动:五元组只剩两项有效

会话持久化的物理 revision 是 dev:ino:size:mtimeNs:ctimeNs,在 2 秒时间戳、按卷常量 ctime、inode 可回收的卷上,真正还能区分改动的只剩 size 和 2 秒粒度的 mtime。 这条与沙箱无关,属于独立的完整性缺陷。复现:

  1. 在工作区放一个测试文件,记录大小,然后让 DSH 读取一次。
  2. 在同一个 2 秒窗口内用另一个工具写回相同大小的内容,预期 mtimeNs 的纳秒部分为 0、秒级时间戳不变。
  3. 回到 DSH 继续编辑同一文件,预期不会出现「文件已被外部修改」的拦截,改动被直接覆盖。
  4. 把测试内容换成不同大小再重复一次,预期这次能触发拦截——这能证明守卫没坏,坏的是令牌分辨率。

五元组各分量在这类卷上的实际贡献:

分量在 exFAT 上的表现是否还能区分改动
dev同一卷内恒定否
ino删除重建后可能复用不可靠
size精确是
mtimeNs2 秒量化,纳秒位为 0仅同窗口外
ctimeNs每卷常量否

备份还原、同步盘、杀毒软件重写这类外部动作,在 2 秒粒度卷上都可能被静默放行。

路径一:DeepSeek Harness 的 grant 前查 FILE_PERSISTENT_ACLS,不支持就 fail-closed

最省事的修法是在授权入口加一次卷能力探测,把「不支持」变成采用阶段的明确错误,而不是运行期的静默放行。 步骤:

  1. 在 grant 入口对 grant root 调用 GetVolumeInformationW,读取返回的文件系统标志位。
  2. 判断 FILE_PERSISTENT_ACLS(0x00000008)是否置位。
  3. 未置位时走与现有 throwLastError 相同的失败路径,错误信息里带上卷路径和「不支持持久 ACL」。
  4. 改动示意(不是仓库原代码):
ts
const flags = volumeFlags(root) // GetVolumeInformationW 的输出
if ((flags & FILE_PERSISTENT_ACLS) === 0) {
  throw new Error(`ACL sandbox unsupported: ${root} does not preserve ACLs`)
}
  1. 用 exFAT 工作区重新打开一次,预期采用阶段就报错;再在 NTFS 工作区重复一次,预期行为与改动前一致。

路径二:把 DeepSeek Harness 工作区迁到 NTFS 卷

如果这份工作区必须保留写权限隔离,换卷比打补丁彻底:NTFS 会真正保存 DACL,五元组令牌也恢复分辨率。 步骤:

  1. 执行 robocopy E:\proj C:\work\proj /E /COPY:DAT /R:1 /W:1,预期退出码小于 8。
  2. 执行 Get-Volume -DriveLetter C | Select-Object FileSystem,预期为 NTFS。
  3. 在 DSH 里移除旧工作区,重新把 C:\work\proj 添加为工作区。
  4. 重复 icacls 对照:grant 前后应出现新增 ACE;再跑同尺寸改写测试,预期这次会触发「文件已变」拦截。

路径三:关掉 DeepSeek Harness 的 ACL 沙箱,把工作区降为只读

既不想换盘、又不想改宿主代码时,正确姿势是承认这条边界不存在,然后收回写权限,而不是继续假装沙箱生效。 步骤:

  1. 在「设置 → 插件」里停用 ACL 沙箱对应的插件行,重启后预期日志不再出现 grant 记录。
  2. 把工作区指向只读来源:写保护的 U 盘、只读共享,或 NTFS 上仅授读取权限的目录(icacls C:\ro /grant "%USERNAME%:(OI)(CI)(RX)")。
  3. 让 Agent 做一次读、一次写,预期读成功、写在文件系统层被拒,且报错不来自沙箱。
  4. 把这条件写进团队说明:丢失的是工作区写能力,换来的是不再有「以为被隔离」的错觉。

DeepSeek Harness 三条路径各自的复测项

三条路径解决的不是同一件事:路径一让失败变响,路径二恢复真实隔离,路径三放弃写能力。 按你选的那条逐项验:

  1. 路径一:exFAT 上采用即报错;NTFS 上 grant 前后 icacls 有差异;原有 NTFS 写入流程不受影响。
  2. 路径二:同尺寸外部改写能触发拦截;受限子进程写工作区被拒;会话文件不再出现静默覆盖。
  3. 路径三:写操作在文件系统层被拒;读、列目录、检索仍可用;不要再对外宣称工作区受 ACL 保护。
  4. 无论选哪条,都把版本令牌的退化单独记录——它不会随沙箱修好而消失,只有换到 NTFS 才会一起恢复。

常见问题

DeepSeek Harness 在 exFAT 工作区提示授权成功,为什么文件还能随便改?

DeepSeek Harness 的 grant 在 exFAT 上返回成功却零效果,因为 exFAT 不保存 DACL,SetNamedSecurityInfoW 返回 ERROR_SUCCESS 但什么都没写进卷里,受限子进程实际没有受到任何约束。grant.ts 的 fail-closed 只处理 Win32 调用报错的情况,没有报错就没有落点。

exFAT 卷上 SetNamedSecurityInfoW 返回成功却不保存 DACL,怎么提前发现?

在 DeepSeek Harness 的 grant 之前调用 GetVolumeInformationW,检查返回标志里的 FILE_PERSISTENT_ACLS(0x00000008)是否置位。未置位说明该卷不保留、不强制 ACL,应直接按不支持处理并抛错,而不是继续授权。

为什么 exFAT 工作区里同一个文件被外部改写,DeepSeek Harness 没有提示文件已变?

DeepSeek Harness 的会话版本令牌是 dev:ino:size:mtimeNs:ctimeNs 五元组,在 2 秒时间戳精度的卷上纳秒位恒为 0、ctime 是卷常量、inode 还可能被回收。同尺寸、同 2 秒窗口内的外部改写,五元组算出来一样,守卫不会开火。

不想换盘也不想关沙箱,能不能只让 ACL 沙箱在 exFAT 上明确报错?

可以,给 DeepSeek Harness 的 grant 入口加一次卷能力探测即可,代价是该工作区在 exFAT 上直接不可用。这是 fail-closed 的取舍:宁可采用阶段失败,也不要运行期给出「已隔离」的假象。

相关术语

FILE_PERSISTENT_ACLS
FILE_PERSISTENT_ACLS 是 `GetVolumeInformationW` 返回的文件系统标志位(0x00000008),表示该卷保留并强制执行访问控制列表。FAT/exFAT 不置位,NTFS 置位。— https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getvolumeinformationw
DACL(Discretionary ACL)
DACL 是安全描述符中决定「谁能对这个对象做什么」的访问控制列表,由 ACE 组成。它必须由文件系统持久保存才能生效,exFAT 没有这项存储能力。— https://learn.microsoft.com/en-us/windows/win32/secauthz/access-control-lists
file version token(文件版本令牌)
file version token 是持久化层用来判断文件是否被外部改动的物理版本标识,当前实现取 `dev:ino:size:mtimeNs:ctimeNs` 五元组。时间戳精度和 inode 稳定性都会直接影响它的判别力。— https://github.com/deepseek-ai/deepseek-harness/discussions/10216

来源