Repository navigation
Replies: 1 comment
|
关联:本条与 #10809 是同一问题(同一处 main.js:8886、同样 71 枚 dsh-auth-* 累积)。 两点交叉验证/更正:
@Gniy7Ga 麻烦帮忙 triage 一下这条 bug 报告(与 #10809 同因,这里补了行号级根因和 431 的更正):
补充说明:我这边已通过清理 Cookie 自行恢复,发帖是为了让根因进入修复流程,不是求助;本贴不是提问,请勿标记为答案。 |
0 replies
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.
Desktop Host:
dsh-auth-*Cookie 名包含随机端口导致无限累积,约 70 次启动后Cookie:头超 16 KB,Host 返回 431,应用以 “desktop welcome: Web authentication failed” 启动崩溃@deepseek-ai/dsh-desktop0.2.1-rc.3(涉及dsh-client-connection、dsh-host-webserver,同版本)deepseek-ai/deepseek-harness(packages/bundle/base、client-connection、host-webserver、desktop shell)1. 现象(Symptoms)
Windows x64 上启动 Desktop 端,弹窗崩溃,仅显示:
崩溃日志位于
%APPDATA%\@deepseek-ai\dsh-desktop\logs\crash-*-host.log,其中:source: host、phase: startup;(no error-level renderer console output was captured);误导性后果:用户与支持人员会去重装应用、删除
~/.dsh/.credentials.yaml、点击弹窗自带的 “Restart” 与 “Disable third-party plugins”——全部无效。因为故障点不在这些位置:崩溃发生时 Host 进程已成功启动并 ready,插件也已全部加载完毕。实测中该现象会自加速:一旦跨过阈值,日志出现每 5 秒一条崩溃记录(恢复窗口的 Restart 循环),且永远不会自行恢复。
2. 触发原理(Root Cause,精确到代码行)
2.1 正常握手流程
步骤 1 — Host 侧
WebServer绑定端口(@deepseek-ai/dsh-web-app/cordis.patch.yml:179):Desktop 下
webStartup.port = 0→ 每次启动由 OS 分配随机端口(实测 57738 / 54788 / 55984 / 61311 …)。步骤 2 — Host 通过 IPC 把 launch URL 发给壳:
ctx.connection.authenticatedUrl('http://127.0.0.1:<随机端口>'),即/?token=<launchToken>。步骤 3 — 壳先用 Node 全局
fetch(undici,不带任何 Cookie)换 Cookie:authenticateWebHost()→ 期望303 + set-cookie。这一步永远成功(因此错误信息不会出现在这里)。步骤 4 — 壳再用 Electron
net.fetch(带 Chromium Cookie 罐)访问同一 launch URL:connectDesktopWelcome()→GET /?token=…得 303 → 跟随Location: ./→GET /,此时必须携带第 3 步签发的 Cookie。2.2 缺陷一:Cookie 名包含易变端口(真正的缺陷)
@deepseek-ai/dsh-client-connection/lib/index.js:相关常量与默认值:
因为端口每次随机 → Cookie 名每次不同 → 新 Cookie 无法覆盖旧 Cookie,只能不断累积。
2.3 缺陷二:Chromium 不按端口隔离 Cookie,Node 默认头部上限 16 KB
GET 127.0.0.1:<新端口>/会把 Cookie 罐中所有127.0.0.1的 Cookie 全部塞进Cookie:请求头。http服务,默认maxHeaderSize = 16 KB,超限直接返回 431 Request Header Fields Too Large。if (!response.ok) throw new Error("desktop welcome: Web authentication failed")(app.asar/lib/main.js:8886),把 431 吞成了“认证失败”,既不重试、也不自愈、也不暴露状态码。于是:没有网络错误、没有认证错误、没有任何日志线索,只有一枚误导性的“认证失败”。
2.4 自加速特性(关键)
每次失败的重试仍然会签发一枚新 Cookie(303 在跟随重定向之前就已返回)。因此一旦跨过阈值,重启越多、情况越糟,永远无法自行恢复。
3. 触发条件(量化)
dsh-auth-<sha256("127.0.0.1:端口")>(base64url,52 字符)Max-Age = 30 天(cookieMaxAgeDays默认 30)maxHeaderSize = 16,384 B结论:任何在 30 天窗口内启动/重启约 70 次的用户都会命中。 这与实测数据完全一致(触发时 Cookie 罐内正好 71 枚、16,117 B)。
补充:
ProxyOverride正常绕过,且 Node 侧fetch全程成功);4. 实测复现与证据(Reproduction & Evidence)
在用户的真实安装上,使用同一 Electron 44.7.0 二进制(以
resources/default_app.asar装载自定义主进程),由桌面端同一入口拉起 Host,执行与Object.start完全相同的调用序列:authenticateWebHost(undici,无 Cookie)GET /?token=…net.fetch(launchUrl, {credentials:'include'})!ok→ 崩溃GET /(无 Cookie)关键复现数据:
清理后重新执行同一序列:
启动真实应用:5 个进程存活、主窗口正常出现、未产生新的崩溃日志。
4.1 给官方的最小复现脚本要点
无需用户环境即可复现:
session.cookies.set()预置 71 枚dsh-auth-*(Path=/、HttpOnly、SameSite=Strict),再执行net.fetch(launchUrl, {credentials:'include'});5. 建议修复方案(Fix Proposal)
方案 A(推荐,最小切口,并顺带修掉“持久 Cookie 永不生效”)
让 authority 不含易变端口,使同一次安装/同一 host 下 Cookie 名稳定 → 每次启动覆盖而非新增:
安全性不再依赖端口:真正的凭证是签名密钥(
client-connection/browser-session记录)+launchToken,端口不构成安全边界;isAuthenticated/authenticate只需与签发侧使用同一requestAudience,两侧一致即可。附带收益:Cookie 名稳定后,
Max-Age = 30 天的持久会话才真正可用。方案 B(兜底,防历史脏数据)
启动时清理不匹配当前 authority 的历史 Cookie(壳侧):
方案 C(必须做:可诊断性 / 自愈)
C1 — 壳侧错误信息带上状态码:
C2 — 识别 431 时给出明确指引(“浏览器 Cookie 数据超限或损坏,请清理 Cookie 后重启”),并自动清理一次 Cookie 后重试(自愈)。
C3 — Host 侧可考虑
createServer({ maxHeaderSize: 64 * 1024 })作为纵深防御,但不应替代方案 A。6. 用户侧临时规避(Workaround)
必须完全退出应用(
Network\Cookies被独占锁住),然后二选一:① 外科式(推荐:仅删
dsh-auth-*,保留其他站点/平台登录 Cookie)对 SQLite 库
%APPDATA%\@deepseek-ai\dsh-desktop\Network\Cookies执行:② 粗暴式
7. 期望行为(Expected Behavior)
8. 附:验证环境与方法说明
resources/default_app.asar+ 自定义app目录),以完全相同的二进制执行net.fetch/session.cookies调用,避免依赖推测;@deepseek-ai/dsh-desktop-host/lib/index.js以ELECTRON_RUN_AS_NODE=1拉起,参数与环境变量与生产一致;app.asar头部读取lib/main.js、dsh/node_modules/@deepseek-ai/*原始文件完成(行号为 0.2.1-rc.3 包内实际行号)。All reactions