Repository navigation
dsh help me find some bugs about itself #7055
yanglaofish
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.
Upstream issue draft: dsh-win32-process — Win32 child env block keeps case-duplicate keys (
no_proxy+NO_PROXY)Title
Windows: spawned child environment block contains case-duplicate entries (
no_proxyandNO_PROXY), breaking PowerShellEnv:/Start-Process("duplicate key" ArgumentException)Summary
On Windows, every subprocess spawned by the harness receives an environment block that contains both the lowercase and uppercase spellings of proxy variables (
no_proxy/NO_PROXY,http_proxy/HTTP_PROXY,https_proxy/HTTPS_PROXY). The duplicate entries are produced bydsh-win32-process's customCreateProcessWpath (encodeWindowsEnvironment), which sorts case-insensitively but does not de-duplicate folded keys. Node's own libuvchild_processpath collapses these (verified empirically), which is why the defect only shows on the custom Windows spawner.Contrary to appearing like "stale residue" from a removed proxy plugin, the duplicates are re-generated on every spawn:
dsh-subprocessimportsproxyEnvironmentForChild()fromdsh-http-proxyand overlays both casings into every child environment (NODE_USE_ENV_PROXY=1is injected by that same function and appears in every spawned child, proving per-spawn injection).Environment
dsh-plugin-desktop@2.0.10) on Windows, Node v24.11.1@deepseek-ai/dsh-win32-process@0.1.5-rc.2@deepseek-ai/dsh-http-proxy@0.1.5-rc.2@deepseek-ai/dsh-subprocess@0.1.5-rc.2http://proxyhk.huawei.com:8080)Impact
Get-ChildItem Env:→ArgumentException: 已添加项。字典中的关键字:“NO_PROXY”(duplicate key, case-insensitive dictionary)Start-Process/Start-Job→ same "已添加项" error when copying the environmentStart-Process).Reproduction
On a Windows DSH where a proxy plugin publishes proxy variables (e.g.
dsh-proxy-pro, or the built-in proxy), run any spawned tool (pwsh/bash) and check its raw environment block:Expected: one
no_proxyentry. Actual (excerpt):Inside that child, run
Get-ChildItem Env:(PowerShell) → duplicate-key ArgumentException.Root-cause chain
1. Dual-case publication into
process.env—@deepseek-ai/dsh-http-proxy(by design)POLICY_ENV_NAMES(lib/index.js:29-33) publishes both casings per field:writeProxyEnv(lib/index.js:364-375) assigns every name intoprocess.env. On Windows, Node'sprocess.envis case-insensitive (reads collide), but its key map retains both spellings (verified: after writingno_proxythenNO_PROXY,Object.keys(process.env)contains both keys; the last value wins). So the host env map carries two spellings of every proxy variable.proxyEnvironmentForChild()(lib/index.js:491-503) additionally returns an overlay with both casings plusNODE_USE_ENV_PROXY: "1".2. Verbatim copy into the spawn request —
@deepseek-ai/dsh-subprocesschildEnvironment()(lib/index.js:51-55) copiesObject.entries(process.env)verbatim (both spellings), then overlaysproxyEnvironmentForChild()(both spellings again +NODE_USE_ENV_PROXY).3. Missing case-fold de-dup in the raw env block —
@deepseek-ai/dsh-win32-process← the defectencodeWindowsEnvironment(lib/index.js:319-327):The comparator already folds case (
toUpperCase), sono_proxy/NO_PROXYsort adjacently — but both entries are then serialized into the UTF-16LE block passed toCreateProcessW(..., 1028, environment, ...)(lib/index.js:616). Windows keeps both pairs verbatim → the child sees duplicate env keys.Why direct Node tests don't reproduce it: libuv's spawn materialization collapses case-duplicates (verified empirically with
spawnSync('cmd', ['/c','set'], { env: {...process.env, 'no_proxy': 'X', 'NO_PROXY': 'Y'} })→ child shows onlyNO_PROXY). Only the customCreateProcessWpath indsh-win32-processpreserves both.Why removing the
dsh-http-proxyplugin row did not helpThe plugin row can be unmounted, but the package is still consumed as a library by two always-running components:
dsh-subprocessimportsproxyEnvironmentForChildat every spawn (its import line 2).dsh-proxy-propeer-depends on@deepseek-ai/dsh-http-proxyand callsinstallProxyFromEnvironment.They share ONE module instance: the profile's
node_modules/@deepseek-ai/dsh-http-proxyis a junction into the app'snode_modules/@deepseek-ai/dsh-http-proxy(same file, same SHA-256; Node resolves by realpath). So a singleactivepolicy state drives both the dispatcher and the per-spawn overlay. Whendsh-proxy-proinstalls a policy, it simultaneously activates the overlay thatdsh-subprocessreads on every spawn — the two are not separable from the plugin side.Proof it is live, not residue:
NODE_USE_ENV_PROXY=1appears in every spawned child, and it is produced only byproxyEnvironmentForChild(writeProxyEnv never writes it). A dead plugin cannot inject that into every new spawn; the shared instance is kept active bydsh-proxy-pro's install call.Proposed fix (one location)
Prefer the uppercase spelling when a folded pair exists (matches what libuv's collapse keeps):
Alternatively/also: on Windows,
dsh-http-proxy'swriteProxyEnvcould publish a single casing (Windows env is case-insensitive; the dual-case design exists for POSIX consumers' lowercase-first precedence and is meaningless there — but the spawner de-dup alone is sufficient and fixes every producer).Notes
中文摘要
no_proxy和NO_PROXY(以及 http(s)_proxy 两拼写),PowerShell 的Get-ChildItem Env:/Start-Process报"已添加项,字典中的关键字:"NO_PROXY""。dsh-win32-process的encodeWindowsEnvironment用toUpperCase折叠做排序比较,但排序后不折叠去重,两个拼写原样写入传给CreateProcessW的 UTF-16LE 环境块。Node 原生 libuv spawn 会折叠,所以只有 DSH 自定义 Windows spawn 会复现。dsh-subprocess每次 spawn 时调用dsh-http-proxy的proxyEnvironmentForChild()现写(NODE_USE_ENV_PROXY=1只有该函数注入,每个子进程都有,证明是现生成的);dsh-proxy-pro也 peer 依赖该库发布 env。encodeWindowsEnvironment里按折叠键去重(保留大写拼写);或让dsh-http-proxy在 Windows 上只发布一种拼写。All reactions