DeepSeek Harness 沙箱报 Win32 5:SetNamedSecurityInfoW 成因与修复
在 Windows 上把 DeepSeek Harness(DSH)的文件策略设为 workspace-write 后,如果任何 shell 命令都在执行前直接失败、报 SetNamedSecurityInfoW failed (Win32 5): grantWrite(<你的工作区>),那不是你的命令有问题,而是沙箱给工作区写 ACL 授权这一步被 Windows 拒绝了:grantWrite() 要在一次 SetNamedSecurityInfoW 调用里同时改 DACL 和 SACL 的 Low 完整性标签,而写标签需要 WRITE_OWNER;当调用者既不是目录属主、也拿不到这一位时,Windows 返回 ERROR_ACCESS_DENIED (5),沙箱 fail-closed,子进程根本不会被创建。 与它同族的还有两个坑:沙箱写下的可继承 Low 标签会留在磁盘上,让工作区里的 .bat/.cmd/.exe 双击弹「无法验证发布者」、让 MSYS2 与 MSVC/cargo 编译链失败;再往外一层,工作区里的 junction 曾能让受限进程删掉工作区外的文件。本文把这三件事按「授权失败 → 标签副作用 → 边界越权」拆开,每段都给可粘贴的命令和不止一种解法。
SetNamedSecurityInfoW Win32 5:为什么 workspace-write 所有命令都在启动前失败
这类报错的本质是「授权」失败,不是「执行」失败——命令还没被 spawn,所以你在命令里写什么都救不回来。 报错形态在所有同族讨论里高度一致:
Error: SetNamedSecurityInfoW failed (Win32 5): grantWrite(D:\<workspace>)
Win32 5 就是 ERROR_ACCESS_DENIED。因为沙箱是 fail-closed 的,任何 Win32 失败都会抛错、绝不 spawn 一个未受限的子进程——这个行为本身是对的,问题在于某些环境下 grantWrite 的前提根本无法满足,而用户只看到一行裸错误码(#7804)。
对照现象很有诊断价值,先记住这三条:
- 切到
danger-full-access,同一条命令立刻正常——说明失败发生在沙箱初始化阶段,与命令本身无关。 - 切到
read-only也正常——只读模式不授予可写根,grantWrite根本不会被调用(#7804)。 - 文件工具(read / glob / grep / write / edit)与 MCP 工具不受影响——受影响的只有需要受限 shell 的那条路径。
机制:一次调用要同时写 DACL 与 SACL,标签那步卡在 WRITE_OWNER
根因是「合并写」:一次 SetNamedSecurityInfoW 同时改 DACL 和 SACL 的完整性标签,而后者需要 WRITE_OWNER。 从 0.1.7-alpha.1 起,grantWrite() 在一次调用里做三件事:
- 写入 capability SID 的允许 ACE(
(OI)(CI)可继承,掩码GRANT_MASK,不含WRITE_DAC); - 写入 world 删除拒绝 ACE(
Everyone:(CI)(DENY)(DC),堵住父目录FILE_DELETE_CHILD删除逃逸); - 写入可继承的 Low 完整性标签(
S-1-16-4096、NO_WRITE_UP、OI|CI)。
对应的 SecurityInfo 参数从 4(DACL_SECURITY_INFORMATION)变成 20(DACL | LABEL)。而 Windows 上:
- 目录属主只隐含
READ_CONTROL+WRITE_DAC; - 写 SACL 里的标签需要
WRITE_OWNER; - 于是当 DACL 里没有任何一条给当前调用者的 ACE、属主又不是调用者时,整个合并调用返回
ERROR_ACCESS_DENIED (5)。
这正对上包内 README 的已知限制:「被授权目录必须由调用者拥有并授予 WRITE_OWNER……DACL 只授予 Modify 的目录现在会大声失败」(#7504)。换句话说,这不是 bug 撞上了异常环境,而是文档化的前提在 Windows 最常见的两种目录形态上天然不满足。
四类触发场景:同一个错误码,四种现场
同一个 Win32 5 至少有四条来路,先判现场再选修法,别一律照抄同一条命令。 对照表:
| 场景 | 现场特征 | 判据 | 来源 |
|---|---|---|---|
| 非系统盘(数据盘)工作区 | 工作区在 D:\、F:\ 等非系统盘,%USERPROFILE% 之外 | 该卷默认 ACL 是 Authenticated Users: Modify + Users: RX,无调用者显式 ACE | #7804 |
| 目录属主不是调用者 | (Get-Acl <dir>).Owner 返回 BUILTIN\Administrators 等 | 属主 DENY-only,且无 SeTakeOwnership,/grant 本身也会被拒 | #7771 |
| 受保护 DACL 的子目录 | 工作区根能写,某些子目录写不进(读正常) | 子目录执行过 icacls /inheritance:r,不继承根的可继承 ACE | #8409 |
| desktop 授权缓存失效 | 首次成功过,之后根上的能力 ACE 被删,连根都写不进且不自愈 | desktop 只在每个服务器生命周期物化一次授权、之后只信内存缓存 | #8409 |
前两类是最常见的:判据都是**「继承链上不存在一条给调用者 WRITE_OWNER 的 ACE」**,与目录深度、盘符类型都无关;工作区在数据盘上时几乎必中,而放在 C:\Users\<你>\… 下新建的目录天生继承调用者的 FullControl,所以常规开发机上看不到这个 bug(#7804)。
第三类和第四类症状不同:根能写、子目录不能写,或者首次能写、之后连根都不能写。第三类的现实来源是 robocopy /COPYALL、提权安装器设过显式权限、解压/备份还原流程;第四类只出现在 desktop 形态,agentless runner(每次 spawn 自行授权、每次实读 DACL)会自动补回能力 ACE、能自愈(#8409)。
编号自查:五步定位到具体是哪一类
不要一上来就改 ACL——先跑完下面五步,把「哪条前提没满足」测出来,再决定用哪个方案。
- 查令牌完整性级别与组的可用性。在非受限的 PowerShell 里执行
whoami /groups,预期看到Mandatory Label\Medium Mandatory Level;若BUILTIN\Administrators显示Group used for deny only,说明你是未提权的管理员账户,Administrators 的 ACE 对你不可用(#7771)。 - 查有没有能「绕过去」的特权。执行
whoami /priv,重点看有没有SeTakeOwnership、SeRestore、SeBackup。这三个都没有时,你无法直接夺取属主,第 3 类场景的修法必须提权(#7771)。 - 查目录属主是谁。执行
(Get-Acl '<工作区>').Owner。返回你自己 → 走「非系统盘 + 自己拥有」那格;返回BUILTIN\Administrators或其它账户/组 → 走需要提权的那格(#7804)。 - 查 DACL 里有没有当前用户的显式 ACE。执行
icacls "<工作区>",看是否出现你自己的账户名。若只有NT AUTHORITY\Authenticated Users:(I)(M)这类继承项,说明你拿到的只是 Modify,没有WRITE_OWNER(#7804)。 - 用系统自带工具复现同一条权限路径(不需要启动 DSH)。在非系统盘新建目录后执行
icacls <dir> /setintegritylevel Low,预期Access is denied.(退出码 5),与 DSH 报的Win32 5一致;随后补一条完全控制再重试,预期成功:
# 1) 非系统盘新建目录(继承该卷默认 ACL)
New-Item -ItemType Directory -Force F:\probe | Out-Null
(Get-Acl F:\probe).Access | Where-Object IdentityReference -match $env:USERNAME
# → 无输出:调用者没有显式 ACE
# 2) 写 Low 完整性标签 == 沙箱 grantWrite 要做的同一件事
icacls F:\probe /setintegritylevel Low
# → Access is denied. (exit code 5) ← 与 DSH 报的 Win32 5 一致
# 3) 给调用者补一条完全控制(含 WRITE_OWNER)
icacls F:\probe /grant "$env:USERNAME:(OI)(CI)F"
# 4) 同一操作立刻成功
icacls F:\probe /setintegritylevel Low
# → Successfully processed 1 files. (exit code 0)
对照实验:把第 1~2 步换到 C:\Users\<你>\ 下重做,天生成功,因为新目录继承了调用者 FullControl(#7804)。
多种修复方案:从最小权限到换目录,按场景挑
修复的核心只有一件事——让授权那一刻的上下文对工作区目录持有 WRITE_OWNER;差别在于用多小的权限、要不要提权、要不要动属主。 下面六个方案按「侵入性从小到大」排,方案一、二、四是绝大多数人的答案。
方案一:原地补最小权限 (WO)(推荐,免提权、权限最小)
适用「非系统盘 + 目录属主就是你自己」这一格。据讨论中用户的实测,这一格最小充分权限就是 (WO),(F) 同样可行但不是最小,而且两者都不需要提权(#7804、#8409)。
- 关掉正在报错的 DSH 会话(授权是开会话时做的,改完 ACL 后重开会话生效)。
- 在普通(非提权)PowerShell 里执行授权命令,把
<工作区>换成你的实际路径:
icacls "<工作区>" /grant "$env:USERNAME:(OI)(CI)(WO)"
# → Successfully processed 1 files. ← 成功
- 验证当前用户已持有
WRITE_OWNER:重新执行icacls "<工作区> /setintegritylevel Low",预期从Access is denied.变为Successfully processed(#7804)。 - 重开 DSH 会话,在
workspace-write下跑一条最小命令(如pwsh -Command "echo hi"),预期有输出、不再报Win32 5。 - 需要回滚时执行
icacls "<工作区>" /remove:g "$env:USERNAME",把这条授权撤掉。
方案二:补完全控制 (F)(最通用,传播较慢)
(F) 含 WRITE_OWNER,适用面比 (WO) 广,代价是权限更大、且可继承 ACE 会在整棵树上传播。讨论中实测全树传播大约需要 3~4 分钟(#7804)。
- 执行
icacls "<工作区>" /grant "$env:USERNAME:(OI)(CI)F"。 - 等待命令返回
Successfully processed;工作区很大时给继承传播留几分钟。 - 用
icacls "<工作区>"确认出现你自己的账户条目,再重开会话验证。 - 回滚:
icacls "<工作区>" /remove:g "$env:USERNAME"。
方案三:改属主 /setowner(需要提权,且不是「等价替代」)
注意这条有一个被公开更正过的坑:/setowner 不是 /grant 的等价替代。 当属主已经是调用者本人时它是 no-op(既不改 DACL、也不授予任何新权限);当属主不是调用者时,它本身需要提权,而属主身份只隐含 READ_CONTROL + WRITE_DAC,不含 WRITE_OWNER,之后仍须补一条 (WO)(#7804)。
- 以提权身份打开 PowerShell。
- 执行
icacls "<工作区>" /setowner "$env:USERNAME",预期Successfully processed。 - 回到非提权会话,按方案一补
(WO):icacls "<工作区>" /grant "$env:USERNAME:(OI)(CI)(WO)"。 - 重开会话验证;只做第 2 步不做第 3 步仍会失败。
方案四:把工作区挪到用户配置目录下(最省事,绕开而不是修复)
C:\Users\<你>\… 下新建的目录继承调用者的显式 FullControl,条件天生满足(#7804)。
- 把项目拷到
C:\Users\<你>\repos\...之类的路径下。 - 在 DSH 里把该目录重新连接为工作区。
- 以
workspace-write开新会话,跑一条最小命令确认不再报Win32 5。 - 好处是不动任何现有 ACL;代价是项目盘符变了,注意同步你的构建脚本与路径配置。
方案五:临时切 danger-full-access(应急,不推荐长期用)
切过去确实立刻可用,但这恰好是沙箱想避免的状态:把工作区放在非系统盘的 Windows 用户会因此被「静默降级成不安全配置」(#7804)。
- 只在急需跑一条命令时,把该会话权限临时设为
danger-full-access。 - 用完立即切回
workspace-write,并用方案一/二补好授权。 - 不要把
danger-full-access写进默认配置当长期方案。
方案六:用社区的诊断插件把错误变成可读提示
@argszero/cordis-plugin-sandbox-grant-advisor 在 tools/post-execute 上识别这条拒绝签名,按工作区属主分叉给出可粘贴的修复命令(属主是自己 → 免提权 (WO);属主不是 → 提权 F 或先改属主),并说明版本边界。它不写任何 ACL、不提权,只是把裸错误码翻译成结论(#7804、#7735)。安装命令:
npm i @argszero/cordis-plugin-sandbox-grant-advisor
在 profile 的 patch 层挂载:
- insert:
- id: sandbox-grant-advisor
name: '@argszero/cordis-plugin-sandbox-grant-advisor'
另有一条重要事实:沙箱内的 agent 自己修不了这个 ACL。 受限令牌对该目录只有 capability SID 的
(W,D,DC)、不含WRITE_DAC,在workspace-write里跑同一批icacls会全部Access is denied。所以「预检 + 用户侧一次性修复」是唯一可行路径,任何「让 agent 运行时自愈」的设想都不成立(#7804)。
针对第三类(受保护 DACL 子目录)和第四类(缓存失效)的专用修法
这两类不走上面的 grant:
- 受保护 DACL 子目录:对那个写不进的子目录执行
icacls "<子目录>" /reset,让它恢复继承根的可继承 ACE,预期写入恢复;这是讨论中实测有效的应急补救(#8409)。 - desktop 缓存失效(根上的能力 ACE 被外部删掉、连根都写不进):重启桌面端 / 工作区服务器,让授权 map 清空并重新读 DACL。据源码核对,这张内存 map 就是 desktop 唯一查阅的东西,清空它即完成恢复(#8409)。
- 内置的
diagnose-windows-sandbox-acl技能也能诊断并修「标准用户 + 非系统盘」这一类:给出WRITE_DAC/WRITE_OWNER的检测结果并在需要时补FullControl。注意它随0.2.0才提供,0.1.7-*安装里没有,别在旧版本上找它(#8409)。
别把同症状的其它机制误判成授权失败
「Windows 上 workspace-write 不可用」目前至少有四条互不相同的机制,Win32 5 只是其中一条——先看报错文本再下手。 一位用户把早期报告做过归类(#8485),补上本文的授权失败共四类:
| 机制 | 典型症状 | 关键点 | 来源 |
|---|---|---|---|
| 授权失败(本文主线) | SetNamedSecurityInfoW failed (Win32 5): grantWrite(...) | 子进程未创建,处理 ACL 前提 | #7504 |
| 控制台宿主创建失败 | bash 无输出、exit code 3221225794(0xC0000142) | GUI 子系统入口进程无控制台,受限令牌里 conhost.exe 起不来 | #8485 |
| 令牌默认 DACL 缺「已启用」SID | can't create pipe / CreatePipe FAILED err=5 | 管道无父目录、只依赖令牌默认 DACL,两趟访问检查都不过 | #8485 |
| 令牌被降为 Low | MSYS2 程序全报 127(NtCreateDirectoryObject ... 0xC0000022) | Low 令牌写不进 Medium 的 \BaseNamedObjects | #8485 |
Low 完整性标签的副作用:双击弹「无法验证发布者」与编译链失败
Low 标签不只在会话里起作用——它是写在 NTFS 安全描述符上的常驻改造,会话结束、切换权限模式、甚至重启 DSH 之后都还在生效。 引入点是 d5ad3baeb5(2026-09-19,fix(sandbox): confine Windows deletes with a Low integrity label),首个包含该行为的 tag 是 dsh-v0.1.7-alpha.1,0.1.6-alpha.2 不含它(#7735)。因为标签是可继承的(OI|CI),被授权根目录下每一个文件都会被物化成 Low。
现象清单:一个标签,七种后果
同一个根因在不同工具链上表现完全不同,这也是它难排查的原因。 各讨论汇总的后果:
.bat/.cmd/.exe双击弹「打开文件 - 安全警告:无法验证发布者」——每次都要点确认。注意不是 Mark of the Web:对全部文件遍历备用数据流,除:$DATA外没有任何Zone.Identifier,属性页里也没有「解除锁定」可点(#7735)。- 可执行文件脱离 DSH 后在普通终端里以 Low 完整性运行:能读工作区外任何文件,但写/新建/改名工作区外的任何位置一律
Access is denied(Python 报[Errno 13] Permission denied)。切换权限模式、重启 dsh 都无效,只有把标签改回去才恢复(#7735)。 - MSVC / cargo 编译链整体不可用:
cc-rs的编译失败只剩一个裸exit code: 2,手工跑同一条cl.exe才拿到真实错误D8050: 无法执行 c1.dll: 拒绝访问。变量是构建产物是否落在 Low 树内——build-script 可执行文件落在树内就继承 Low、令 cargo 一侧以 Low IL 运行,写%TMP%、CARGO_HOME、registry 缓存这些树外 Medium 位置全部被拒(#7735)。 - RenderDoc 抓帧不产出:目标进程写用户临时目录返回
Access Denied (5),把捕获输出改到工作区内就成功(#7735)。 - MSBuild 拒绝构建「从互联网下载的不安全的源码」(#7735)。
- 注册表写入被拒:程序写
Software\JavaSoft\Prefs\...报Access denied,读同一个键却成功;以管理员身份运行同样失败,因为限制来自文件上的常驻标签,不是令牌(#7735)。 - MSYS2 程序全报 127:
NtCreateDirectoryObject(\BaseNamedObjects\...)返回0xC0000022,因为\BaseNamedObjects是 Medium 完整性目录(#8485)。
讨论中还有一组直接证据:对同一个文件调用 IInternetSecurityManager::MapUrlToZone,Low 标签下被判为 zone 3(Internet),恢复 Medium 后是 zone 0(Local Machine)(#7735)。
编号步骤:先确认标签,再决定改不改
动手前先确认「确实是被打了标签」,别把 MSBuild 的安全策略或杀软误判成同一条。 步骤如下:
- 查目录和目录内文件的标签。执行
icacls "<工作区>",预期看到Mandatory Label\Low Mandatory Level:(OI)(CI)(NW);对.bat执行icacls "<工作区>\run.bat",预期看到继承来的Low ... (I)(NW)(#7735)。 - 快速列出全部残留标签(含子目录):
icacls "<工作区>" | findstr /i "Mandatory",或Get-ChildItem -Force -Attributes ReparsePoint -Recurse "<工作区>"单独看 junction(#7735)。 - 双击一个工作区内的
.bat,预期弹「无法验证发布者」;换一个从未被本项目授权的目录做同样操作,预期不弹——这条对照能排除「文件本身有问题」(#7735)。 - 确认后执行恢复命令,把整棵树改回 Medium:
# 修复:Windows 会把可继承标签急切传播到整棵树,大工作区约数十秒到一分钟
icacls "<工作区>" /setintegritylevel "(OI)(CI)Medium"
- 验证:重新
icacls "<工作区>",Mandatory Label行应变成Medium Mandatory Level:(NW);再双击启动器,预期不再弹窗。修完不需要重新构建——Low/Medium 是安全描述符元数据,不是二进制内容(#7735)。
一个容易踩的坑:icacls <path> /setintegritylevel Low 不带 (OI)(CI) 时只标签目录本身、子目录不继承;在这种状态下复现不了故障,必须是 (OI)(CI)Low(#7735)。
规避与「会复发」:两条实测有效的做法
标签是常驻 + 精确匹配短路的,手工改回 Medium 会在下次以 workspace-write 打开该工作区时被覆盖回去。 所以除了上面那条恢复命令,另有两件可做的事:
- 把构建输出挪出工作区(编译链专用):把
CARGO_TARGET_DIR指向工作区外,讨论中做过 8/8 次完整 build 全部成功;而输出留在工作区内时必崩。这是一个精确的诊断信号——如果挪出去就好,基本可以确认踩的是标签这条(#7735)。 - 预期复发并理解它的两种形态:
hasExactLabel的精确匹配短路会让下次授权重新盖回 Low;而结果取决于届时 DACL 里有没有WRITE_OWNER——有就静默盖回 Low(「我的修复被覆盖了」),没有就抛ERROR_ACCESS_DENIED直接 fail-closed(工作区彻底不可用)。对已经用/setintegritylevel Medium修好树的用户,下次很可能撞上的是后者,看起来像「修复没保存住」,实际是权限门换了方向(#7735)。 - 别指望用「切 Full access」清标签:切权限模式不清除已有标签,必须手动恢复(#7735)。
- 社区有针对性的补丁,但未合入:讨论里提出过一条本地提交
f6698853f3,在每次授权结尾给授权根顶层的.bat/.cmd/.exe写一条显式、信息性的 Medium 标签(policy 0,不缩沙箱边界),已知边缘是「会话中途新建的启动器仍是 Low」「只覆盖根目录顶层」「撤销授权不回收文件上的标签」。这属于待官方合入的补丁分支,不是已发布修复(#7735)。
junction 越界删除:workspace-write 曾能删掉工作区外的文件
这是三条里唯一的安全边界问题:受限进程可以在工作区内建一个指向区外目录的 junction,再经该 junction 删除区外文件;写入会被拒,删除却被放行。 报错形态是「能删不能写」的诡异不对称(#7517)。
机制:授权给的是「父目录的删除权」,而别名的父目录在工作区内
删掉文件靠的是一条被 capability ACE 显式授予的 FILE_DELETE_CHILD,它在 junction 目录上同样成立。 拆开看:
- 沙箱的授权掩码
GRANT_MASK里含FILE_DELETE_CHILD (0x40),且 ACE 以「容器 + 对象继承」合并写入——所以你在工作区内建的 junction 目录,本身就继承到了 capability SID 的FILE_DELETE_CHILD,同时也继承了 Low 标签(#7517)。 - 同一份授权还写了 world 拒绝
FILE_DELETE_CHILD(Everyone:(CI)(DENY)(DC)),它删掉的是环境权限那条路,删不掉 capability 自己那一条——因为那条正是工作区内删除能力的来源(#7517)。 - 于是经
<工作区>\junction\某文件删除时,鉴权用的是别名父目录上已继承的删除权;而创建/追加必须通过受限 SID 的第二趟交叉检查,目标是区外目录、拿不到 capability ACE,所以被拒。这就是「能删不能写」(#7517)。
讨论中指出,包内对 reparse point 没有任何处理:reparse|junction 在 packages/sandbox/ 下的出现次数是 0,而同一包对自己的不相交性检查其实已经用 realpathSync.native 解析别名——即「解析别名」这件事在发布路径上没做(#7517)。
编号复现:按版本不同,结果不同
复现前先确认版本,因为 0.1.7-alpha.1 之后行为变了。 步骤如下:
- 准备区外靶标目录与一个文件,确认它没有 Low 标签、也没有 capability SID:
icacls C:\dsh-out,预期没有Mandatory Label行(#7517)。 - 在工作区连接后,于受限会话内建 junction 指向区外目录:
cmd /c mklink /J "<工作区>\probe-j" "C:\dsh-out"
- 经 junction 删一个区外文件:
Remove-Item "<工作区>\probe-j\某文件"。在0.1.5-rc.2上预期删除成功、区外文件随后不存在;同一 junction 下新建/追加则被拒(#7517)。 - 在
0.1.7-alpha.1及以后重做第 2、3 步:据讨论中用户的实机复测,会话内的mklink /J输出Access is denied.、junction 不存在,区外靶标也未被删除(#7517)。 - 单独测「会话前就存在的 junction」这一格:先在非受限侧
mklink /J <工作区>\j <区外目录>,再开workspace-write会话。讨论中的实测里,授权后区外目录没有新增 SID、也没有出现 Low 标签,即继承传播没有穿过该重解析点(#7517)。
版本边界要如实说明:#7517 的报告针对
0.1.5-rc.2,而堵删除逃逸的提交链(d5ad3baeb5→36e632751e→3d5ba3b83f)只进到dsh-v0.1.7-alpha.1及以后。源码分析方也强调:在0.1.5-rc.2那个构建里根本没有 world 拒绝与 Low 标签,当时的「环境权限」路径本身就能删——junction 是演示逃逸的手段,不一定是产生逃逸的原因(#7517)。
修复与规避:源码级建议 + 插件守卫 + 残留清理
官方尚未确认这条已修,「0.1.7 建不了 junction」是用户实测结果而非官方结论。 可用的做法分三层:
- 源码级方向(供维护者评估,未落地):从
GRANT_MASK去掉0x40,让删除权只能来自对象自身继承的DELETE;或在授权时对工作区内已存在的、解析到授权根之外的 reparse point 直接报错拒绝。需要注意tests/runner.spec.ts里有一条断言Remove-Item与Rename-Item都成功的用例,重命名可能依赖父目录权限,改之前应把它拆成 delete-only / rename-only 分别评估(#7517)。 - 插件守卫:
@argszero/cordis-plugin-reparse-escape-guard挂在tools/pre-execute上,把工具声明的路径操作数与沙箱自己的可写根推导对比;若「读起来在工作区内、解析后落在所有可写根之外」,就在分发前拒绝并说明三条事实(as written / actually resolves to / the link)。它不替代沙箱执行,且对 shell 命令行里拼出来的路径只在开启shellFields时做尽力扫描(#7517)。 - 残留清理:
Get-ChildItem -Force -Attributes ReparsePoint -Recurse "<工作区>"(或dir /al)列出全部 junction 再人工处置。注意:删除 junction 只删链接本身,不会删目标(#7517)。 read-only天然免疫:它不带 write SID,任何地方都不存在 capability ACE 与0x40;但这是换模式,不是修复(#7517)。
排查注意事项
三条症状同属「Windows ACL/完整性沙箱」一族,但触发点不同,排查顺序要按证据走,不要凭感觉改 ACL。 十条要点:
- 先读报错文本再动手:
SetNamedSecurityInfoW ... Win32 5是授权失败;0xC0000142是控制台宿主创建失败;can't create pipe是令牌默认 DACL;MSYS2 全 127 是 Low 令牌——四类机制四种修法(#8485)。 - fail-closed 本身是对的:任何 Win32 失败都不 spawn 未受限子进程,别把「拒绝对话」当成 bug(#7804)。
- 判据是「继承链上有没有给调用者
WRITE_OWNER的 ACE」,与目录深度、盘符类型无关;工作区在数据盘上几乎必中(#7804)。 - 最小充分权限是
(WO)不是F:非系统盘 + 自己拥有那一格,(WO)即可,且免提权(#7804)。 /setowner不是等价替代:属主隐含权不含WRITE_OWNER,改完属主仍要补(WO)(#7804)。- 沙箱内的 agent 修不了自己的 ACL:受限令牌没有
WRITE_DAC,所有icacls都会Access is denied(#7804)。 - Low 标签是常驻改造、且会复发:切
danger-full-access不清标签,手工改 Medium 也会在下一次workspace-write授权时被盖回(#7735)。 - 编译链那类别顺着
exit code: 2查编译器:cc-rs吞掉了 stderr,先手工跑同一条cl.exe拿真实错误,再测CARGO_TARGET_DIR移出工作区是否恢复(#7735)。 - 旧工作区会残留沙箱编辑:能力 SID ACE、world 拒绝、Low 标签会留在「曾作为工作区根」的目录上,跨工作区累积;
icacls /remove处理不了未映射的 capability SID(静默处理 0 个文件),手工清理须用 .NET 按 SID 操作(#8409)。 - 修复状态以实测为准:截至
0.2.0-rc.2的社区报告,授权失败与标签副作用仍复现;0.1.7-alpha.1起 junction 建不出来是用户实测,官方未确认(#8409、#7517)。
排查这类系统级问题时,光靠命令行翻 icacls 输出很费眼。如果你更想把插件的安装、更新、卸载和日志集中在一个界面里管,与其在各处手敲命令,不如用 DSH Plugin Hub 这类可视化插件市场:它把插件装卸、更新确认、系统日志与诊断都收在一个面板里,排查环境问题时能少绕几步。

来源:Discussion #7504、Discussion #7804、Discussion #7771、Discussion #8409、Discussion #8485、Discussion #7517、Discussion #7735。
常见问题
这是沙箱给工作区写 ACL 授权时被 Windows 拒绝,错误码 5 就是 ERROR_ACCESS_DENIED,命令本体从未运行。根因是 grantWrite 要在一次 SetNamedSecurityInfoW 里同时写 DACL 和 SACL 的 Low 完整性标签,而写标签需要 WRITE_OWNER,当前令牌既不是目录属主也没有这一位。最省的修法是免提权执行 icacls <工作区> /grant "%USERNAME%:(OI)(CI)(WO)",回滚用 /remove:g。
不是盘的问题,是非系统盘的默认 ACL 不满足沙箱授权前提。数据盘新建目录继承的是 Authenticated Users 的 Modify 和 Users 的只读,当前用户没有显式 ACE,因此没有 WRITE_OWNER。把工作区挪到 C:\Users\<你>… 下即可,或在原地执行 icacls <工作区> /grant "%USERNAME%:(OI)(CI)(WO)" 后再开会话。
属主不是当前用户时,连 icacls /grant 本身都会因缺少 WRITE_DAC 被拒,需要一次提权操作。提权后执行 icacls <工作区> /grant "<你的账户>:(OI)(CI)F"(或先 /setowner 改成自己),之后会话即可正常授权;注意 /setowner 不会顺带给你 WRITE_OWNER,仍要补 (WO)。
这是可继承 Low 完整性标签的必然副作用:目录内每个文件都被物化成 Low,shell 在启动 Low 完整性程序前会插入发布者确认框。用 icacls "<工作区>" /setintegritylevel "(OI)(CI)Medium" 把标签改回 Medium 即可恢复,但下次以 workspace-write 打开该工作区会被重新盖回 Low。
早期版本能复现:#7517 报告在 0.1.5-rc.2 上,受限进程可在工作区内建 junction 指向区外目录、再经该 junction 删除区外文件。据该讨论中用户的实机复测,0.1.7-alpha.1 起会话内建 junction 已被拒绝、区外靶标也未被删除,但官方尚未确认修复,请自测后再依赖。检测残留 junction 用 Get-ChildItem -Force -Attributes ReparsePoint -Recurse。
相关术语
- WRITE_OWNER
- WRITE_OWNER 是 Windows 安全描述符上的一项权限,允许持有者修改对象的所有者以及 SACL(系统访问控制列表)。DSH 沙箱写 Low 完整性标签要改 SACL,因此必须有 WRITE_OWNER;对象属主的隐式权限只包含 READ_CONTROL 与 WRITE_DAC,并不隐含它。— https://github.com/deepseek-ai/deepseek-harness/discussions/7804
- grantWrite
- grantWrite 是 DSH Windows ACL 沙箱(@deepseek-ai/dsh-sandbox-windows-acl)给工作区目录授予写权限的函数,它用一次 SetNamedSecurityInfoW 同时写入 capability SID 允许 ACE、world 删除拒绝 ACE 和可继承的 Low 完整性标签。任何一步失败都会整体抛错,沙箱 fail-closed,不生成子进程。— https://github.com/deepseek-ai/deepseek-harness/discussions/7504
- Low 完整性标签(Mandatory Integrity Label)
- Low 完整性标签是写在对象 SACL 里的强制完整性级别标记(SID S-1-16-4096),属于 Windows 强制完整性控制(MIC)。Mark 为 Low 的进程不能写 Medium 对象(no-write-up),DSH 用它把受限子进程的删除能力限制在已授权目录内;副作用是同样被标记为 Low 的文件在资源管理器里双击会触发「无法验证发布者」确认框。— https://github.com/deepseek-ai/deepseek-harness/discussions/7735
- junction(目录联接)
- junction 是 Windows 的一种目录重解析点,让一个目录路径指向另一个本地目录,创建它不需要任何特权(区别于符号链接需要的 SeCreateSymbolicLinkPrivilege)。DSH 沙箱的路径边界按字符串前缀判断,junction 可让「看起来在工作区内」的路径实际解析到工作区外。— https://github.com/deepseek-ai/deepseek-harness/discussions/7517
来源
- #7504 — Windows: workspace-write sandbox fails with SetNamedSecurityInfoW Win32 5 when the workspace directory DACL lacks WRITE_OWNER· deepseek-ai(GitHub Discussions)
- #7804 — Windows:工作区位于非系统盘时 ACL 沙箱必然初始化失败(Win32 5 / grantWrite)· deepseek-ai(GitHub Discussions)
- #7771 — Windows:工作区目录所有者为 BUILTIN\Administrators 时,workspace-write 下所有命令直接失败· deepseek-ai(GitHub Discussions)
- #8409 — Windows ACL 沙箱三个授权缺陷:受保护 DACL 子目录 / desktop 根授权缓存 / 标准用户+非系统盘· deepseek-ai(GitHub Discussions)
- #8485 — DSH Windows ACL sandbox: two defects make workspace-write unusable· deepseek-ai(GitHub Discussions)
- #7517 — Workspace-write 沙箱缺陷:受限进程可通过工作区内的目录 Junction 删除工作区外文件· deepseek-ai(GitHub Discussions)
- #7735 — Windows:沙箱给工作区根目录盖 Low 完整性标签后,目录内的 .bat/.cmd/.exe 双击弹「无法验证发布者」· deepseek-ai(GitHub Discussions)