Replies: 2 comments
|
影响不止如此,用Visual Studio打开项目后尝试构建还会出现MSBuild拒绝构建从互联网上下载的不安全的源码的报错,需要清理掉才可以正常构建 |
|
A short pointer from the provisioning side of this report, because its second section (the
Your isolation table settles the gate, and I have folded it into that plugin's README as the independent confirmation — What the plugin deliberately does not attempt is this report's headline half: the publisher-confirmation dialog for an inherited-Low |
Uh oh!
There was an error while loading. Please reload this page.
简短描述:
这个问题都是更新完0.17后才出现的,在安全设置里现在有两个未知账户,一个是0.16的,有所有权限,一个是0.17的,只有读取和写入权限,现在只能开full acces修改项目的代码,但这么做太危险了。
环境
0.1.7-rc.1(源码方式构建/运行,HEAD46a7f68);Node v24.20.0<project-root>、<workspace-B>(见~/.dsh/storages/workspace.json)<control-workspace>(最后一次 dsh 会话 2026-09-23 10:27)不出现本问题现象
更新到 0.1.7 之后,工作区根目录里的启动器(
启动续传.bat、启动监控.bat等)双击时弹出(文案大意):点「运行」后程序正常;但每次双击都要确认。目录内其它类型(
.py、.md)不弹,因为它们交给编辑器而不是由 shell 启动。已排除的常见原因
:$DATA外没有任何Zone.Identifier(方法已用临时文件自检)。属性页里也没有「解除锁定」可点。根因
d5ad3baeb5(fix(sandbox): confine Windows deletes with a Low integrity label,2026-09-19)为堵住cmd /c del的父目录删除逃逸,让每次授权都在一次SetNamedSecurityInfoW里给被授权根目录加一条可继承的 Low 强制完整性标签(S-1-16-4096、SYSTEM_MANDATORY_LABEL_NO_WRITE_UP、OI|CI)。目录标签一旦可继承,目录内每一个文件(含启动器)都会物化成 Low;而 shell 在启动 Low 完整性的可执行文件前会插入上面的「无法验证发布者」确认框。这是标签的必然副作用,与启动器本身无关。
实测证据(
icacls,同一台机器同一份 dsh):<project-root>(更新后授权)Mandatory Label\Low Mandatory Level:(OI)(CI)(NW)+Everyone:(CI)(DENY)(DC)<project-root>\启动续传.batMandatory Label\Low Mandatory Level:(I)(NW)(继承来的)<workspace-B>(更新后授权)<control-workspace>(更新前授权,无 Low 标签)Mandatory Label行时间线也对得上:0.1.7-rc.1 于 2026-09-23 发布 → 当晚更新 → 2026-09-24 08:34 / 10:09 两个工作区先后被授权(
~/.dsh/storages/workspace.json)→ 症状出现;<control-workspace>自 09-23 10:27 起没有再被授权,所以保持旧形态。复现
run.bat;workspace-write的工具调用(触发授权);icacls run.bat→ 出现Mandatory Label\Low Mandatory Level:(I)(NW);run.bat→ 弹「无法验证发布者」;影响面
.exe工具(例如随项目分发的tools\rakaly.exe)同样被波及。尝试的修复办法
本地提交
f6698853f3:fix(sandbox): exempt a granted root's launchers from the inherited Low label。做法:每次授权结尾(完整应用路径 与
hasExactGrant跳过路径都要跑——常驻工作区授权每次都走跳过路径,且授权之后新建的启动器会继承目录标签),给授权根顶层的.bat/.cmd/.exe各写一条显式、信息性的 Medium 标签,覆盖继承来的 Low:设计要点:
SYSTEM_MANDATORY_LABEL_NO_POLICY(策略位 0)——只记录身份,不附加强制限制,受限子进程对这些文件的写权限完全由能力 DACL 决定,与之前一致;目录上的 Low 标签、Everyone删除拒绝、能力 ACE 一律不动。0x2000(S-1-16-8192),S-1-16-16384是 System;我第一版测试常量写错,被单测直接抓出来。WRITE_OWNER(标签住在 SACL 里):本机隔离实验里,单独授TakeOwnership就能写、只授WRITE_DAC/READ_CONTROL/Modify一律Access is denied(详见下一节)。所以豁免必须放在授权路径内部执行——授权成功本身就说明该上下文对根目录持WRITE_OWNER,而那条 ACE 会继承到文件上,豁免才写得进去;工作区 DACL 只授 Modify 时,两者都会失败(见下一节的边界说明)。验证:
packages/sandbox/sandbox-windows-acl全包 14 文件 / 169 测试 PASS,其中新增tests/root-launcher-exemption.spec.ts5 项(apply 路径、跳过路径、非启动器不受影响、已有显式标签不被改写、无启动器时不写描述符)。tsc -b tsconfig.host.json、oxlint、verify-agent-note-format/verify-md-wrap/verify-md-links/verify-translation-pairing全通过;包 README 与2026-09-19那条 Agent Note 的中英双侧 +.i18n.yaml已同步。pnpm run build成功(构建记录指向f669885)。补丁的已知边缘(供评审)
revokeWrite只清目录标签,根目录顶层启动器上那条显式 Medium 会留下。这是有意的——它与调用方自己icacls打的标签形状相同,按形状删就会误伤调用方的设置(keep判定正是为此存在);代价是文件标签比授权长寿。tools\*.exe)仍是继承 Low,双击照旧确认一次。另一条相关缺陷:
workspace-write授权不生效(同一个WRITE_OWNER门)同一台机器上还有第二个症状,它和上面那个弹窗共用同一道门。
现象:会话按「工作区内写入」授权后,工作区里的写入仍被拒。dsh 的 Windows 沙箱是 fail-closed 的——provisioning 失败时命令在启动前就被整体拒绝,所以用户看到的是「权限设了没用/沙箱账户在工作区里没有权限」。
实测(直接调用 dsh 自己的 ACL 模块复现 provisioning 这一步):
根因:根目录的 DACL 里没有一条给当前用户的 ACE。
<project-root>(以及同类数据盘目录)上,用户的访问权来自继承的NT AUTHORITY\Authenticated Users:(OI)(CI)(M)——只有 Modify,没有WRITE_OWNER;所有者虽然是用户本人,但所有者隐式只带READ_CONTROL与WRITE_DAC,补不上这一位。合并应用同时写 DACL 与 SACL 标签,标签那一步要WRITE_OWNER,于是整个SetNamedSecurityInfoW返回 Win32 5,fail-closed 再把「写不进标签」放大成「这个工作区不能用」。模块 README 自己就写了「DACL 只授予 Modify 的目录现在会大声失败」——本机正是这一类,而且是 Windows 数据盘上最常见的形态:隔离实验(同一台机、同一账户、未提权;对象由该账户自己创建):
icacls <file> /setintegritylevelFullControlMandatory Label\Medium Mandatory Level:(NW))TakeOwnership(=WRITE_OWNER)ChangePermissions(=WRITE_DAC)Access is denied.ReadPermissions(=READ_CONTROL)Access is denied.ModifyAccess is denied.⇒ 这道门是
WRITE_OWNER,与完整性级别、与特权都无关:同一账户whoami /priv里没有任何安全相关特权,照样写得进去。这也推翻了我们自己的第一个猜想(「需要提权 /SeSecurityPrivilege」):.NET Set-Acl报SeSecurityPrivilege是那个 API 的副作用(它把 SACL 一起重写),原生SetNamedSecurityInfoW/icacls只要对象上的WRITE_OWNER。上一版结论之所以在某些运行里成立,是因为提权令牌启用了BUILTIN\Administrators,而工作区 DACL 里恰有一条BUILTIN\Administrators:(I)(F)——那次拿到的是WRITE_OWNER,不是特权。失败会留下半成品:这次 apply 整体回滚,于是当时
<project-root>的 DACL 里只剩一条旧版本遗留的能力 ACE(掩码0x1301FF)、标签完全没有;<workspace-B>连能力 ACE 都没有。扫描%TEMP%下 302 个dsh-*临时目录,没有一个带 Low 标签。当天稍晚复核时<workspace-B>的能力 ACE 已经是现行掩码0x110156(说明其间有过一次成功的授权),而<project-root>仍是0x1301FF——同机两个工作区、同一份代码,却停在不同代际的描述符上,正好把这条漂移照出来了。两个症状的关系:能写标签的那次 provision(提权运行,拿到
Administrators:(F))把 Low 标签落到目录上,目录里的启动器随之继承 → 弹窗;此后非提权的每次 provision 都在WRITE_OWNER上失败 → 授权看起来「不生效」。而由于磁盘上那条能力 ACE 的掩码是旧值0x1301FF(当前代码的GRANT_MASK = 0x110156,少了读/执行位),hasExactGrant永不命中,于是每次 provision 都要走完整 apply、每次都要WRITE_OWNER——跳过路径本可以完全避开这次写。边界(按设计,非本次提权引入):受限的是写,不是读/网络;受限子进程(
WRITE_RESTRICTED+LUA_TOKEN+DISABLE_MAX_PRIVILEGE,可见特权只剩 1 个)在<project-root>内可写,而<workspace-B>、C:\Users\Public、C:\Windows\Temp、桌面一律 Win32 5。工作区被标 Low 之后,同用户其它低完整性进程(例如浏览器保护模式)也能写工作区——这是 Low 方案本身的设计代价。English summary
Symptom. After updating to
0.1.7, double-clicking a project's own launchers inside a workspace (.bat/.cmd/.exe) shows the shell's Open File - Security Warning ("the publisher could not be verified") every time.Cause.
d5ad3baeb5(2026-09-19) makes every write grant stamp the granted root with an inheritable Low integrity label (S-1-16-4096,SYSTEM_MANDATORY_LABEL_NO_WRITE_UP,OI|CI). Every child of that root — the launchers included — therefore materializes Low, and the shell interposes its publisher confirmation before launching a Low-integrity executable. It is not Mark of the Web (a fullZone.Identifierscan is empty), nor file content/encoding/signature. A workspace granted before the update keeps the old descriptor shape and does not prompt at all.Fix proposed (commit
f6698853f3). At the end of every grant — the full apply and the exact-ACE skip, since the skip is what a standing workspace grant takes on every provision — re-assert an explicit informational Medium label (S-1-16-8192,SYSTEM_MANDATORY_LABEL_NO_POLICY, no inheritance) on the granted root's top-level.bat/.cmd/.exe. Policy 0 keeps the confined child's write authority exactly as the capability DACL grants it; the directory label, the world delete deny and the capability ACE stay untouched, and an already-explicit caller label is left alone. Covered bytests/root-launcher-exemption.spec.ts; the package suite (169 tests), typecheck, lint, doc gates and a full build pass. Note the Medium RID is0x2000(S-1-16-8192);S-1-16-16384is System. Writing a label needsWRITE_OWNERon the object (isolated below), which is why the pass belongs inside the grant path: the grant's own success proves the caller holds it, and that ACE is what inherits to the launchers.Second, related defect — the same
WRITE_OWNERgate. Aworkspace-writegrant did not take effect: writes inside the workspace were denied, and because the sandbox is fail-closed the command was rejected before it ran, so it presents as "the option I picked does nothing / the sandbox account has no rights here". Reproduced with the module itself:grantWrite(<project-root>)→SetNamedSecurityInfoW failed (Win32 5). Root cause: no ACE on the workspace root names the user at all — their access comes from the inheritedNT AUTHORITY\Authenticated Users:(OI)(CI)(M), i.e. Modify with noWRITE_OWNER, and ownership does not supply it (owner-implicit rights areREAD_CONTROL+WRITE_DAC). The merged apply writes the DACL and the SACL label, and the label step needsWRITE_OWNER, so the whole call fails with Win32 5 and fail-closed turns "could not stamp the label" into "this workspace is unusable". The module README already flags a Modify-only directory as a loud failure — this is exactly that case, and it is the ordinary shape of a folder on a data drive. Isolation, same machine/account, unprivileged, on files the account owns:FullControl→ label written;TakeOwnershiponly → label written;ChangePermissionsonly,ReadPermissionsonly,Modifyonly →Access is denied. So the gate isWRITE_OWNERand nothing else: the same account'swhoami /privlists no security privileges at all, which also retires our own first hypothesis (elevation /SeSecurityPrivilege)..NET Set-Acl'sSeSecurityPrivilegeerror is an artefact of that API rewriting the SACL; nativeSetNamedSecurityInfoW/icaclsonly needsWRITE_OWNER. Elevation appeared to fix it because an elevated token carriesBUILTIN\Administrators, and that workspace's DACL hasBUILTIN\Administrators:(F)— WRITE_OWNER, not a privilege. The failed apply is atomic and rolled back, leaving a half-state:<project-root>kept only a stale capability ACE (0x1301FFagainst the currentGRANT_MASK = 0x110156, i.e. the old mask still carries read/execute) and no label at all,<workspace-B>had neither, and none of the 302%TEMP%\dsh-*directories carried a Low label. A later check the same day found<workspace-B>already holding a current-mask ACE — some run did provision successfully — while<project-root>still held0x1301FF: two workspaces, one machine, one build, on different descriptor generations. And because that stale mask makeshasExactGrantnever match, every provision re-applies the descriptor and therefore demandsWRITE_OWNERevery single time — the skip path would have avoided the write entirely. Provisioning-only again: the confined child (WRITE_RESTRICTED+LUA_TOKEN+DISABLE_MAX_PRIVILEGE) still cannot write<workspace-B>,C:\Users\Public,C:\Windows\Tempor the desktop.Known edges of the patch.
revokeWriteclears the directory label only, so a file-level Medium label outlives the grant (deliberate — it is indistinguishable from a caller's ownicaclslabel, and thekeepverdict exists to protect that). A launcher created mid-session stays Low until the next grant. Only the root's top level is scanned. And since the exemption writes a file label, it needsWRITE_OWNERon each launcher too: a workspace whose DACL grants the caller only Modify fails the grant itself, so the exemption cannot run either — the README bullet aboutWRITE_OWNERarguably needs to cover the launcher files, not just the directory.All reactions