在受限模式下dsh的pwsh工具在Windows上存在问题 #1789
Replies: 5 comments
|
补充一个立即可用的 workaround 和一个值得确认的事实: 为什么我们的插件在受限模式下没踩这个坑:Node.js(含 临时绕过(在 pwsh 受限模式下需要联网时): # 用 Node 的 fetch 代替 Invoke-WebRequest(OpenSSL,不受影响)
node -e "fetch('https://api.github.com').then(r=>r.json()).then(d=>console.log(d.current_user_url))"
# 或使用 OpenSSL 构建的 curl(例如 Git for Windows 自带的 curl)
& 'C:\Program Files\Git\mingw64\bin\curl.exe' -s https://api.github.com值得确认的修复方向: 我们在 Windows 上做过大量 dsh 插件实测(node/pnpm/dsh CLI 全链路),如果官方需要受限模式下的复现矩阵(哪些客户端受影响、哪些不受),可以补一份。 |
|
这个问题我也出现过 他自己判定是Windows的SChannel有问题 单独我使用GLM5.3和5.2没有类似问题 🤔 只有DS API+DSH我复现了这个问题 |
|
这个问题的修复方向或许不应是把受限模式整体升级为 full access,而是重新收窄 Windows 受限令牌的作用范围。 |
|
这个看下来 vibe 的答案好像挺可靠的 至少唬住我了 |
|
Confirmed this on Windows 11 (DSH 0.1.0-rc.6, pwsh 7.6.3): under workspace-write the ACL-restricted token breaks schannel TLS, exactly as you describe - curl.exe and .NET HttpClient both fail with SEC_E_NO_CREDENTIALS while Python/OpenSSL works fine. The CreateRestrictedToken (dwFlags=13) root cause analysis looks correct, and it does contradict the sandbox doc's 'network not restricted' claim. As a stopgap I added an offline probe to https://github.com/boyin111-1/dsh-doctor (check 13, Windows only): it detects whether the current token is restricted (whoami /groups deny-only) and runs a real curl.exe HTTPS handshake. If it hits SEC_E_NO_CREDENTIALS it reports #1789 with workarounds (use danger-full-access for network commands, or a non-schannel client like Python/OpenSSL). It runs before dsh boots, so you can confirm the environment is affected without launching a session. This is really a dsh-sandbox-windows-acl bug - the sandbox should either stop claiming network is unrestricted, or the restricted token shouldn't strip schannel credentials. Hope it gets fixed in-tree; the probe is just to make the failure diagnosable in the meantime. |
Uh oh!
There was an error while loading. Please reload this page.
在 Windows 上,以 DSH 沙箱的 workspace-write 文件生效模式运行命令时,所有使用 Windows schannel 安全包的 TLS 客户端(例如基于 Schannel 编译的 curl.exe、.NET HttpClient、Invoke-WebRequest)都会连接失败,并且报错:
而在full acess模式下,此类联网工具没有问题;具体而言,其发生原因指向 @deepseek-ai/dsh-sandbox-windows-acl 创建的受限令牌(CreateRestrictedToken,dwFlags = 13 = DISABLE_MAX_PRIVILEGE | LUA_TOKEN | WRITE_RESTRICTED)。因此,schannel 无法获取 TLS 凭据。而沙箱自身的文档声称它只限制写入,并明确写着 “reads, network, and process visibility are NOT [restricted]” —— 这次的 schannel 故障与该文档描述的边界相矛盾(此段为deepseek v4 flash自行排查发现)。
以下为DeepSeek 给出的分析报告
环境
0.1.0-rc.6dsh-sandbox-windows-acl、dsh-pwsh-sandbox、dsh-pwsh-local、dsh-sandbox-local、dsh-tool-pwsh均为0.1.0-rc.6复现步骤
在 Windows 上启动一个沙箱文件生效模式为
workspace-write的 DSH 会话。调用
pwsh工具(或任何经由 Windows ACL 受限令牌 runner 执行的 shell 工具),运行:观察下面的失败。
预期行为
curl.exe应成功完成 HTTPS 请求(HTTP 200),因为:Test-NetConnection example.com -Port 443→True);curl.exe -4 http://neverssl.com→ HTTP 200);TLSv1.3)。实际行为
.NET 同样失败:
对照实验(同一台机器、同一个可执行文件)
workspace-write(受限令牌)danger-full-access(完整令牌)curl.exe -4 https://example.comSEC_E_NO_CREDENTIALS(约 0.04 秒本地失败)curl.exe -4 http://neverssl.com(纯 HTTP)ssl握手(OpenSSL,非 schannel)Test-NetConnection example.com -Port 443(TCP)TrueTrueResolve-DnsName ... -Server 8.8.8.8)在失败与成功的运行之间,唯一变化的变量是沙箱令牌:同一个
curl.exe二进制、同一个网络、同一个 URL。这把问题精确地定位到沙箱的受限令牌构造,而非网络或 curl 本身。根因分析
Windows 沙箱后端是
@deepseek-ai/dsh-sandbox-windows-acl,它构造受限令牌并在其下启动子进程:createRestrictedToken(currentToken, 13, ...)——dwFlags = 13 = DISABLE_MAX_PRIVILEGE (0x1) | LUA_TOKEN (0x4) | WRITE_RESTRICTED (0x8)(见dsh-sandbox-windows-acl/lib/types-CNjZgO4h.js的createRestrictedToken)。CreateProcessAsUserW在该令牌下启动(spawnSandboxed/spawnSandboxedInherited)。setTokenDefaultDaclGrant将 FILE_ALL_ACCESS 的 ACE 合并进令牌的默认 DACL,使新建管道能通过写入 pass-2 检查——但它只处理文件对象的访问,并未处理 schannel 所需的凭据/安全包资源。在该受限令牌下,schannel 的
AcquireCredentialsHandle返回SEC_E_NO_CREDENTIALS,于是沙箱内所有基于 schannel 的 HTTPS 客户端全部失效。自带 TLS 栈的客户端(Python/OpenSSL、自带 TLS 的 Node)不受影响——这正是"网络被封锁"的表象只出现在 schannel 客户端身上的原因。与文档声明的沙箱边界矛盾
dsh-sandbox-windows-acl/lib/types-CNjZgO4h.js(约第 1371 行)写道:dsh-pwsh-sandbox/dsh-tool-pwsh的描述也告诉用户沙箱只限制文件写入。实际上受限令牌同时破坏了 schannel TLS,因此文档边界在 Windows 上是不成立的。这是一个静默且令人困惑的故障:对用户来说表现为"没有网络",而沙箱的拒绝分类签名("access is denied"、"permission denied"、"read-only file system")都不匹配SEC_E_NO_CREDENTIALS,于是该失败被当作普通命令失败上报,而不是沙箱限制。附加观察(可能单独成 issue)
example.com)解析出 IPv6 地址,而本机 IPv6 出站路径不可达。不带-4的curl会先在 IPv6 尝试上挂起 10–17 秒,之后才暴露真正的 schannel 错误,掩盖了根因。使用curl -4会立刻显示真实错误。DENIAL_SIGNATURES/RUNNER_FAILURE_RULES不包含 schannel 的SEC_E_NO_CREDENTIALS,因此工具层无法将其归类为沙箱拒绝(denied: false),即使danger-full-access能解决问题,也不会给出升级提示。建议的修复方向
CreateRestrictedToken下 schannel 需要哪些资源/凭据存储(很可能是用户/机器证书存储或 CNG 密钥存储的访问权限,也可能涉及\Device\CNG/Cryptography注册表键),并在受限令牌中授予/保留最小必要访问权限,使AcquireCredentialsHandle成功,同时仍强制执行写限制。danger-full-access(或非 schannel 客户端),直到令牌授权修复为止。SEC_E_NO_CREDENTIALS(及相关的SEC_E_*schannel 错误)加入沙箱拒绝签名,使该失败能被归类并报告为沙箱限制,而不是一个莫名其妙的命令失败。相关源文件
packages/sandbox/sandbox-windows-acl(dsh-sandbox-windows-acl):受限令牌构造、DACL 授权、CreateProcessAsUserW启动。packages/sandbox/sandbox-local(dsh-sandbox-local):平台 runner 选择(win32使用windows-acl链)。packages/sandbox/sandbox(dsh-sandbox):writableRoots、升级词汇、拒绝标记。packages/tool/pwsh+packages/sandbox/pwsh-sandbox(dsh-tool-pwsh、dsh-pwsh-sandbox):工具层描述目前声称沙箱仅限制文件写入。All reactions