[Bug Report] Windows:非提权启动时 workspace-write 沙箱 provisioning 必然失败,且留下残破 ACL 导致永久无法自愈 #8115
xiechara64-coder
started this conversation in
General
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
[Bug Report] Windows:非提权启动时
workspace-write沙箱 provisioning 必然失败,且留下残破 ACL 导致永久无法自愈摘要
Windows 10/11 上,若 dsh 以非提权(过滤令牌)方式启动,
workspace-write沙箱在 provisioning 阶段即失败:由于失败发生在 provisioning,任何命令都执行不了(连
Get-Date都会失败)——工具调用在命令真正启动前就被沙箱配置挡下。更麻烦的是,失败的
grantWrite会在 workspace root 上留下残破的安全描述符:三段编辑只落地了第一段(且是降级形态)。而grantWrite的幂等跳过条件要求三段全部齐备,因此跳过永远不成立 → 之后每次 provisioning 都重走mergeAndApply并同样失败。该 workspace 从此无法自愈,即使之后用足够权限重启 dsh 也无效;本机最终是手工授予特权 + 提权重启才恢复的。复现步骤(Windows 10 build 19041 · dsh 0.1.7-rc.2 · npx 安装)
workspace-write,审批策略ask):Get-Date)。预期:沙箱 provisioning 成功,命令正常执行。
实际:每条命令都失败于
SetNamedSecurityInfoW failed (Win32 5): grantWrite(D:\DSH),且工具结果里给出的 escalation 路径(
retry with sandbox_permissions …)无法救场——因为失败在 provisioning,不在命令自身的文件操作上。环境
19041(20H2 / 2004),NTFS 固定卷D:@deepseek-ai/dsh0.1.7-rc.2(npx启动)EnableLUA=1、ConsentPromptBehaviorAdmin=5(默认配置,未做任何策略改动)D:\DSH,位于 NTFS 固定卷,owner 即启动用户故障时刻的令牌状态:
令牌中完全没有
SeRelabelPrivilege,也没有SeSecurityPrivilege。根因(源码定位,0.1.7-rc.2)
grantWrite在一次SetNamedSecurityInfoW调用里施加三段编辑:FILE_DELETE_CHILD拒绝;完整性标签存放在对象的 SACL 中,写入它需要
SeRelabelPrivilege(或SeSecurityPrivilege)。过滤 / 非提权令牌两者皆无 → 整个调用返回ERROR_ACCESS_DENIED (5),且因为是单次原子调用,三段编辑一段都不落地。dsh-sandbox-windows-acl自身的文档注释把这个问题当成已被 owner 权限解决:该论断对非受限令牌成立,但对非提权令牌不成立:owner 与
WRITE_DAC都满足,标签写入仍然失败,因为缺的是特权而非 DACL 权限。这应该就是缺口所在。实测佐证(同一非提权令牌下):
Set-Acl添加 ACE)→ 成功icacls <dir> /setintegritylevel (OI)(CI)L→ Access is denied即失败被精确孤立在标签 / SACL 那一段编辑上。
为什么无法自愈(与 #423 的区别)
grantWrite的跳过条件要求三段同时成立:失败后实测的 workspace ACL:
只有第 1 段以残缺形态落地,第 2 段与第 3 段完全缺失 → 跳过条件永久不可满足 → 每次 provisioning 都重跑并同样失败。provider 的
workspaceGrants内存缓存只在单个 server 生命周期内有效。与 #423 的差异:#423 的前提是 root 上已存在精确 ACE(
grantWrite成功过、跳过正常命中),问题在"后来新增的子目录拿不到补授"。本报告的前提是 root 上的 ACE 从一开始就是残缺的、grantWrite从未成功过,因此跳过永远不命中。两者症状(file access denied/ 永久失效)相似,但触发条件与代码路径不同——#423 是"跳过了该补的",本报告是"根本没建成、于是永远重试"。与 #3434 的差异:#3434 处理的是授权成功时首次 eager 传播阻塞事件循环的性能问题。本报告是授权本身失败,属于另一种失败模式。
影响
workspace-write策略就完全不可用。Win32 5外没有任何可操作诊断,产品内也没有任何关于特权要求的提示。Workaround(本机已验证有效)
完成后
grantWrite通过,三段编辑齐全,命令执行恢复正常且不再弹审批。(另一种临时绕过:把会话切到
danger-full-access,它会完全跳过materializeAclGrant——但代价是失去该防护。)建议修复(供讨论)
提前检测并明确报错(成本近乎为零,且不影响性能)。
Windows 上以
workspace-write启动时,先校验令牌是否持有(或可获取)SeRelabelPrivilege/SeSecurityPrivilege;若否,在启动阶段就抛出可操作的错误,指明缺失的特权与两条解法(授予特权 + 提权重启,或改用danger-full-access),而不是从深处 provisioning 调用里冒出一个裸Win32 5。不要让残缺授权永久锁死(但必须绕开 # Bug + fix: Windows 大工作区首次 workspace-write 授权同步遍历 ACL 整树,事件循环冻结数分钟 #3434 的性能陷阱)。
建议不要"重放
grantWrite"——据 # Bug + fix: Windows 大工作区首次 workspace-write 授权同步遍历 ACL 整树,事件循环冻结数分钟 #3434 实测,SetNamedSecurityInfoW携带OI|CI会触发整树 eager 传播(4,332 文件 2,773.7 ms;182,053 文件 >120 s),重放会重新引入该冻结。更合适的做法是让跳过判定容错:把"ACE 存在但缺W/ 缺 deny / 缺标签"视为"需要重做",并且只在该情况下才重新 apply——这样一次特权就绪的重启即可修复,而正常路径仍是 O(1) 跳过。修正文档。 README 里"a Full-control workspace satisfies both"在非提权令牌下是误导,应补充
SeRelabelPrivilege要求与"提权重启"这一步。这一点对 Linux/macOS 用户尤其不直观。评估降级路径。 如果唯一失败的是标签那段,是否可以在显式告警的前提下安全降级(保留受限令牌与 deny ACE、跳过 Low 标签),而不是 fail-closed 到整个沙箱不可用?对"宁可要可用性"的部署这会很有价值。
验证材料
E1 · 失败现象(任意命令,非提权启动):
E2 · 令牌缺特权(
whoami /priv,见"环境"节)——无SeRelabelPrivilege/SeSecurityPrivilege。E3 · 失败后 workspace ACL 残缺:
E4 · 失败被孤立在 SACL 编辑:同一非提权令牌下,仅写 DACL 成功;
/setintegritylevel报Access is denied。E5 · 授予特权 + 提权重启后恢复:
grantWrite通过,三段编辑齐全;纯 PowerShell cmdlet 不再需要审批即可执行;工作区外的原生程序访问改由沙箱正常拦截(表现为[sandbox: file access denied under workspace-write mode])。E6 · 无自愈:以非提权方式重启 dsh 后依旧
Win32 5;即便在提权环境中成功授权过一次,若之后以非提权方式启动,仍会尝试重新 apply 并失败。如需完整的
icacls全量输出、whoami /all原文或启动日志,我可以随时提供。相关讨论:#423(capability ACE 永不补授)、#3434(首次授权整树遍历冻结)。本报告与二者触发条件不同,已在正文说明。
All reactions