Repository navigation
Replies: 6 comments 2 replies
|
你的现场记录非常干净(exit code、空输出、 1.
|
| 变量 | Desktop 侧 | 浏览器侧 |
|---|---|---|
| 宿主 | Desktop app | dsh web |
| 版本 | 0.2.0-rc.2 |
0.2.1-alpha.2 |
| profile | desktop |
web |
你自己在环境表里就写清了这一点(:37 CLI 0.2.0-rc.2、:40 npm 全局 0.2.1-alpha.2)—— 所以"同机器、同 Windows build"只固定了 OS,没有固定版本,也没有固定 profile。
⇒ 由此不能推出"宿主特有"。它同样可以是版本差异或 profile 差异。
2. ⭐ 沙箱本身不是差异源(这点可以确定)
git diff --stat dsh-v0.2.0-rc.2 origin/master -- packages/sandbox/sandbox-windows-acl/src
→ 空
sandbox-windows-acl/src 在两个版本之间逐字节相同。 所以限制令牌、授权、低标签、SetNamedSecurityInfoW 那整层没有变化 ⇒ 你不要往沙箱实现里找,这个结论我支持你。但"不是沙箱实现"不等于"是 Desktop 宿主"。
3. ⭐⭐ 真正的候选差异:Windows 的 console host 换过了
packages/subprocess/subprocess-local/src/index.ts 在 rc.2 → master 之间新增了一行(rc.2 里没有):
// Windows' own console host (the standby pool runs Server 2022 and
// Windows 10 22H2) renders the persistent-pwsh prompt tail in a form the
// readiness path cannot match; the console host node-pty ships emits it
// verbatim, see [Agent Note](../../../../.agents/notes/implemented/bug-fix/2026-09-28-pty-bundled-console-host.md).
...(platform === 'win32' ? { useConptyDll: true } : {}),⇒ 在 win32 上,master/alpha.2 用 node-pty 自带的 OpenConsole;rc.2 用 Windows 自己的 console host。
同一次改动的另一处注释把这个区分写得更直白(subprocess-local/src/terminal.ts,rc.2 → master):
- // windows"), and its bare kill() delegates to a console-list agent that
+ // windows"), and its bare kill() reaches the console host by a per-host
+ // path: Windows' own host goes through a console-list agent that fails when
+ // the parent has no console, while the bundled OpenConsole runs a native
+ // kill plus conout teardown.(另有一处纯措辞改动:terminal.ts 把 conhost 改写成 the console host。)
为什么这条落在你的故障轴上:packages/sandbox/sandbox-windows-acl/README.md:127 那条已记录的边界说的就是 console host:
Console isolation is unavailable — children created with
CREATE_NO_WINDOW/CREATE_NEW_CONSOLEdie during DLL initialization withSTATUS_DLL_INIT_FAILED(0xC0000142); children share the host console, and pipe-based stdio redirection is unaffected.
⇒ 同一台机器上唯一与"host console / DLL init"直接相关的版本差异,就是这一行。 你的 A/B 两侧恰好一侧有它、一侧没有。
4. ⭐ 决定性实验:只改一个变量
把 npm 上的 0.2.0-rc.2 装成 CLI(0.2.0-rc.2 正是当前 latest/next),在同一台机器上用 dsh web + workspace-write 跑一条 Write-Output "probe":
- 若也 0xC0000142 ⇒ 差异在版本(console host 那一行是首要嫌疑),与 Desktop 宿主无关 —— 你的原结论要撤回;
- 若正常 ⇒ 版本这一轴被排除,这时"宿主特有"才站得住,下一步再切 profile 这一轴。
建议再补一格:在 Desktop 里换一个非 desktop 的 profile(或新建一个干净 profile)跑同一条命令,把第二、三轴分开。一次只动一个变量,否则结论仍然不可判。
5. 「连 runner 的失败报告都没有」是设计使然,不是另一个故障
你写"not even the sandbox's own runner-failure report"。这一条在源码里是预期的:
packages/sandbox/sandbox-windows-acl/src/runner.ts:210-227→ 子进程正常退出时 runner 如实镜像其退出码(:221 process.exitCode = exitCode)windows-acl-run:这个诊断签名只在 runner 自身失败时打印(:40-43),而packages/sandbox/sandbox-local/src/index.ts:241的fatalSignatures: ['windows-acl-run: ']据此判定
⇒ 所以 0xC0000142 会呈现为"命令失败 + 零输出",永远不带那条签名。你的期望(要可读原因)是合理的产品诉求,但"没有报告"本身不构成独立的异常证据。
6. 一个跨帖可用的判别:先确认是否与子进程类型无关
另有一条报告(#9493)在同一模式下做了 5 个子进程 × 2 模式 的对照,结论是 workspace-write 下连 cmd.exe 都在 DLL 初始化阶段死亡、而 read-only 全部正常 ⇒ 故障与具体 shell 无关。
⇒ 建议你也跑一格 cmd.exe(cmd /c echo probe):若 Desktop 侧同样"任何子进程都死",那你的现场与该帖同源,两帖应当合并成一条(同一根因、两个宿主),而不是各报一条。这对维护者的价值高得多。
7. 你环境表里一处值得肯定
你写 Desktop host port | OS-assigned 19387 —— 这个措辞是对的(apps/desktop-host/src/index.ts:30 用的是 --port 0,由 OS 分配;19387 只是它这次分到的值)。之前有报告把 19387 当成硬编码端口,那是个误判。请保持你现在的写法。
8. 建议的报告口径
- 可确定:
sandbox-windows-acl/src在0.2.0-rc.2与最新线之间逐字节相同 ⇒ 沙箱实现层不是差异源(这条你已经无意中证明了); - 可确定:
workspace-write下任何子进程死在 DLL 初始化、零输出;danger-full-access立即恢复;确定性复现; - 不能确定(请改):「Desktop 宿主特有」—— 三变量未分离,且存在一个更可检验的候选差异(
useConptyDll,见第 3 节); - 下一步:按第 4 节做单变量实验,并把结果贴回线程。
9. 边界
- 我核的是
origin/master=d743267388(=dsh-v0.2.1-alpha.2)与dsh-v0.2.0-rc.2之间的git diff,以及sandbox-windows-aclREADME/源码。useConptyDll与0xC0000142之间的因果我没有独立验证(需要 Windows 受限令牌实验),按规范标为候选假设。 - 你那些环境值(kernel
26100.9445、nightly feed 解析、runtime.json)我未核实 —— 那些依赖你的安装现场。
|
@PerryLink 冒昧 @ 一下 —— 我 10-15 又补了一轮实验(见上方最后一条评论): 排除了版本、宿主、沙箱 runner、 只剩一个我够不到的地方:DSH 自身调用 如果方便,能否指点一下生产路径上实际用的是哪组参数?我照着测就能继续往下缩。 |
|
你问的那组参数,源码里是逐字写着契约的。 也就是说生产路径上是: 那个 那两个 SID 是可以自己算的它们不是运行期随机生成的,是确定性派生的。 所以你可以直接对自己那个工作区路径和 temp 路径各算一次,得到和 DSH 生产路径一模一样的两个 SID,然后手工把 ACL 摆成那个形状再跑 runner。这样参数组合就完全对齐了。 有一件事顺便说清: 令牌是怎么构造的
abi.DISABLE_MAX_PRIVILEGE | abi.LUA_TOKEN | abi.WRITE_RESTRICTED // = 0x1 | 0x4 | 0x8 = 13限制列表( 另外两件两种模式都做的事:降到 Low 完整性级别( 你说的"曾经供给成功、现在必崩",这个对比有参考价值而且它指向一个很具体的地方。写 ACL 的那一步是一次调用同时写 DACL 和 LABEL( 跳过 apply 的条件是三条全中:精确的 ACE、精确的 deny、精确的 label。所以:
这正好能解释"曾经成功、现在必崩":成功那次写下的东西还在就能继续跳过;一旦被改动或清理掉,就再也回不去了。建议你把工作区根目录(以及它的祖辈)当前的 ACE 和标签都 dump 一次,和"能用的那台机器"对比——重点看标签有没有,那是这条路上最容易被忽略的一项。 不过要提醒一句:这解释的是授权阶段失败(Win32 5 那类)。你报的是子进程初始化死的 版本说明我核的是 另外,我上一条提到的 |
|
@PerryLink 按你的思路做了一格验证,结果是阴性的,供参考: 我清掉了工作区上的 ACE(
所以工作区 ACL 状态似乎不是本故障的成因,你提的跳过逻辑这条可以排除了。 我这边从外部能测的已经测完(版本/宿主/runner/AclSandbox API/全新路径 ACL/ |
|
阴性结果收到,那条轴我同意划掉——你清掉 ACE 之后 DSH 能把 ACL 写回去、而子进程照样崩,说明授权那一层是通过的,跳过逻辑确实不是成因。 你确认的那件事也有用:rc.2 里没有 你要的那段代码在这里。 DSH 侧的调用点
const temp = this.materializeAclGrant(sessionId, policy.workspaceRoot)
return [
...this.windowsAclRunnerInvocation(),
'--workspace', policy.workspaceRoot,
'--temp', temp.dir,
'--mode', policy.mode,
'--write-sid', workspaceWriteSid(policy.workspaceRoot),
'--temp-write-sid', temp.writeSid,
]上面 ...this.windowsAclRunnerInvocation(),
'--workspace', policy.workspaceRoot,
'--temp', tmpdir(), // ← 就用环境里的临时目录
'--mode', policy.mode,
] // ← 没有那两个 SID 参数相关字段: ⭐ 由此可以算出:两种模式之间只差这四项这是我觉得对你下一步最有用的一件事。你既然已经从外部把别的都排掉了,那就把这四项逐个对齐:
再加上一项不在参数里、但在令牌上的:令牌默认 DACL 加的那条 ACE 的 SID,read-only 取 注意第 4 项和第 5 项的区别:限制列表是交叉校验写访问用的,而默认 DACL 那条是"新建对象自己的 DACL"。两者都叫 SID,但作用在访问检查的不同阶段——你之前把它们混在一起讲过一次,这里值得分开。 一个可以立刻做的判别如果你要一刀切开第 4 项和第 5 项:手工构造一个 read-only 的令牌(限制列表只有 logon + Everyone、默认 DACL 给 Everyone),但用 workspace-write 的那组 argv(带私有 temp 和两个 SID)跑一次。反过来再跑一次。
这两格能把四个变量砍到两个,而且你已经有从零写 ACL 的能力,做起来成本不高。 我这边能确认的边界我核的是
|
|
@PerryLink 实测结果在桌面端 没有任何逐次提权,就是普通 围栏没有被放宽(四项实测)只确认"不崩了"不够,所以我测了围栏:
四项全部符合 它的生效点:Default DACL,不是 spawn 标志插件 对照你上一条给的四个差异,这提示第 4/5 项的权重并不对称:
这也和 所以你上一条"活口在令牌那一侧"的判断是对的,只是位置在 Default DACL 的内容,而不在限制列表。 对修复方向的含义如果采纳, 必须说明的局限
所以它只是临时方案,不是修复建议。 但它把根因范围压到了 Default DACL 这一条, English summary (optional)Closing the loop: a community plugin restores the production path, and it pins the fault to the token's Default DACL rather than the restricting list.
Confinement verified, not relaxed: write-inside allowed; write-outside denied; write to The plugin merges an If adopted, the ACE selection in Caveats: the plugin is AI-generated and unreviewed (0 stars), it replaces the shipped |
Uh oh!
There was an error while loading. Please reload this page.
Summary
In DeepSeek Harness Desktop for Windows (
0.2.0-rc.2), when the session filepolicy is
workspace-write, every shell command fails. The PowerShell childdies during process initialization with Win32
0xC0000142(STATUS_DLL_INIT_FAILED)and produces no output at all — not even the sandbox's own runner-failure report.
Switching the same session to
danger-full-accessmakes commands work immediately.Switching back reproduces the failure. It is 100% reproducible.
The same sandbox mechanism works correctly on the same machine, same Windows
build, under the browser mode (
dsh web,0.2.1-alpha.2) withworkspace-write:there, writes outside the workspace are correctly refused with a normal
Access is denied, and writes inside the workspace succeed.This looks like a Desktop-host-specific defect in the restricted-token sandbox, not a
sandbox-implementation defect.
Environment
26100.9445), x640.2.0-rc.2(resources/runtime/primary-runtime/runtime.json→desktopVersion)resources/runtime/cli/bin/dsh.cmd --version)0.2.0-rc.224.21.0, pnpm11.7.0, Python3.12.14nightly—https://download.deepseek.com/dsh-desk/feeds/win-x64/nightly.ymlresolves toversion: 0.2.0-rc.2,releaseDate: 2026-09-29(i.e. this is the newest Windows build offered)dsh webfrom the npm global install,@deepseek-ai/dsh@0.2.1-alpha.2, Node24.21.0, port308019387desktopprofile; browser →webprofile; both shareDSH_HOME=~/.dshSteps to reproduce
Open the Harness Desktop app on Windows.
Set the session file policy to
workspace-write(the default in my session).Ask the agent to run any command, e.g.:
Observe: the command returns no output, with exit code
3221225794(
0xC0000142).Change the same session's file policy to
danger-full-access.Repeat step 3. → prints
probe, exit code0.Change the policy back to
workspace-writeand repeat. → fails again identically.Confirmed over 6+ attempts across two separate Desktop sessions; deterministic.
Evidence
E1 — failure code and total absence of output
Even a command that never touches the file system (
Write-Output "probe") fails,so this cannot be an ACL/permission denial. A denial would surface as the sandbox's
documented
Access is denieddialect; instead the process appears never to reachexecution, and the sandbox's own runner-failure path (documented as
windows-acl-run: <detail>+ exit 127) never reports.E2 — control group: identical sandbox code succeeds under the browser mode
Same machine, same Windows build, same user,
workspace-write:dsh web, 3080)Access is deniedThe browser mode reaching the normal denial path proves the restricted-token
mechanism itself is functional on this host and this Windows build. Only the Desktop
host fails, and it fails before any permission decision is made.
E3 — sandbox implementation is byte-identical between the two builds
Comparing the Desktop app's bundled copies (extracted from
resources/app.asar) withthe npm global install (
0.2.1-alpha.2):0.2.0-rc.20.2.1-alpha.2dsh-sandbox-windows-acl/lib/types-Cl_DXjhk.jsdsh-sandbox-windows-acl/lib/runner.jsdsh-sandbox-windows-acl/README.mddsh-sandbox-local/README.mdSo this is not a packaging omission or a code divergence between the two versions.
E4 — ruled out: missing native components
@deepseek-ai/node-addon-systemis Linux-only (Landlock launcher + POSIX flock;its only optionalDependencies are darwin/linux). Not involved on Windows.
lib/types/ffi.d.ts). Its binary is present inboth installations, e.g.
node_modules/@koromix/koffi-win32-x64/win32_x64/koffi.node(1,042,944 B).node-ptyprebuilds forwin32-x64are present in both.E5 — observation that may point at the cause
Inspecting the token of the confined child process as observed from within the failing
session (
[System.Security.Principal.WindowsIdentity]::GetCurrent().Groups, 11 groups total):present:
S-1-1-0(Everyone),S-1-5-32-545(BUILTIN\Users),S-1-5-4(INTERACTIVE),S-1-5-11(Authenticated Users)not observed: any
S-1-5-5-*entry — i.e. no logon session SID (logon SID).dsh-sandbox-windows-acl/README.mddocuments the keep-alive requirement:The failure code we observe matches that sentence exactly.
Caveat — this is a hypothesis, not a proven root cause. The read above is a live
inspection from inside the session, not a dump of the sandbox's constructed token, and
WindowsIdentity.Groupsdoes not expose every token attribute. It is offered only as alead. Please treat it as unconfirmed.
What we already ruled out (so reviewers can skip these)
Write-Output "probe"touches no file, andthe sandbox's documented denial dialect (
Access is denied) never appears. Thebundled ACL diagnostic skill/script (
assets/diagnose-windows-sandbox-acl/) targetsfile-permission denials and did not apply here.
nightlyfeed currently serves0.2.0-rc.2, the sameversion installed.
Suggestion for maintainers
The most useful next step seems to be comparing how the restricted token's keep-alive
group is constructed when the host is the Desktop process versus when it is a
command-line
dsh web. Two specific things worth checking:ELECTRON_RUN_AS_NODEchild of the Electronmain process (
resources/runtime/cli/bin/dsh.cmdshows this launch form) — derives aparent token whose group set differs from a
cmd-launched host in a way that dropsthe logon SID from the restricted token;
CREATE_NO_WINDOW/CREATE_NEW_CONSOLEarebeing passed on the Desktop path. The README's documented limitation says children
created that way die during DLL initialization with exactly
0xC0000142.Known workaround for other users hitting this: keep the Desktop session on
danger-full-access, or run command-heavy work from browser mode, which is unaffected.中文版
摘要
Windows 版 DeepSeek Harness 桌面端(
0.2.0-rc.2) 在会话文件策略为workspace-write时,所有 shell 命令都失败:PowerShell 子进程在进程启动阶段以 Win32
0xC0000142(STATUS_DLL_INIT_FAILED)死亡,没有任何输出,连沙箱自己的runner 失败报告都没有。
把同一会话切到
danger-full-access,命令立刻恢复正常;切回workspace-write立即复现。100% 稳定复现。
同一台机器、同一个 Windows 版本的浏览器模式(
dsh web,0.2.1-alpha.2)在workspace-write下沙箱工作完全正常:工作区外写入被规范地拒绝(Access is denied),工作区内写入成功。
因此这更像是桌面端宿主特有的缺陷,而不是沙箱实现本身的缺陷。
环境
26100.9445),x640.2.0-rc.2(runtime.json→desktopVersion)0.2.0-rc.224.21.0、pnpm11.7.0、Python3.12.14nightly;nightly.yml当前给出的就是0.2.0-rc.2(2026-09-29),即最新 Windows 构建@deepseek-ai/dsh@0.2.1-alpha.2,端口308019387复现步骤
workspace-writeWrite-Output "probe"3221225794(0xC0000142)danger-full-access,重复第 3 步 → 正常输出probe,退出码0workspace-write重复 → 同样失败已在两个独立的桌面端会话中重复 6 次以上,行为确定。
关键证据
E1 — 完全无输出。 连
Write-Output "probe"这种完全不碰文件系统的命令也失败,因此不可能是 ACL / 权限拒绝。权限拒绝会以沙箱文档所述的
Access is denied呈现;实际是进程根本没跑到执行阶段,沙箱自身的 runner 失败路径
(文档记载为
windows-acl-run: <detail>+ 退出码 127)也从未报告。E2 — 对照组。 同机、同 Windows 版本、同用户、同为
workspace-write:浏览器模式工作区内写入成功、工作区外写入被规范拒绝;桌面端两种情况都在进程启动阶段死亡。
浏览器模式能走到正常的拒绝路径,证明受限令牌机制在这台机器上是可用的。
E3 — 沙箱实现两边字节一致。 桌面端(自
app.asar提取)与 npm 全局安装对比:types-Cl_DXjhk.js(64,309 B)、runner.js(7,807 B)SHA256 完全相同。所以不是打包漏文件,也不是版本间代码分叉。
E4 — 排除原生组件缺失。
node-addon-system是纯 Linux 组件(Landlock + POSIX flock,optionalDependencies 只有 darwin/linux),与 Windows 无关;Windows 沙箱走 koffi FFI,
其
koffi.node(1,042,944 B)两边都在;node-pty的win32-x64预编译两边都在。E5 — 一个可能的线索(未确证)。 在失败会话内观察受限子进程令牌的组
(共 11 个组):有
Everyone、BUILTIN\Users、INTERACTIVE、Authenticated Users;未观察到任何
S-1-5-5-*,即没有登录会话 SID(logon SID)。而
dsh-sandbox-windows-acl/README.md明确记载:我们观察到的错误码与这句话完全吻合。
但必须说明:这只是推测,不是已确证的根因。 上述读取是在会话内做的实时观察,
并非沙箱构造出的令牌转储,且
WindowsIdentity.Groups不会暴露令牌的全部属性。仅供排查参考。
已排除项(供维护者跳过)
0.2.0-rc.2给维护者的建议
最有价值的下一步似乎是对比受限令牌保活组的构造方式在两种宿主下的差异:
ELECTRON_RUN_AS_NODE子进程形式由 Electron 主进程拉起的(见
resources/runtime/cli/bin/dsh.cmd),其父令牌的组集合是否与命令行启动的宿主不同,从而导致受限令牌丢失登录 SID;
CREATE_NO_WINDOW/CREATE_NEW_CONSOLE之类的进程创建标志——README 记载的这种创建方式恰好会以
0xC0000142在 DLL 初始化阶段死亡。给其他遇到此问题的用户的临时方案:桌面端会话保持
danger-full-access,或把需要执行命令的工作放到浏览器模式(不受影响)。
All reactions