Skip to content

fix(tui): guest 验证码 SDK 报错不再终止 TUI - #43

Merged
TriDefender merged 1 commit into
TriDefender:masterfrom
LX2000WASD:fix/tui-survive-guest-errors
Sep 6, 2026
Merged

TriDefender merged 1 commit into
TriDefender:masterfrom
LX2000WASD:fix/tui-survive-guest-errors

Conversation

@LX2000WASD

@LX2000WASD LX2000WASD commented Sep 6, 2026 •

Copy link
Copy Markdown
Contributor

背景

v4.5.5 TUI 模式下反复崩溃(实际使用中间隔 2m47s / 14m37s / 27m15s / 29m15s):

zcode-proxy: tui crashed: ReferenceError: moveBy is not defined
    at tE (https://g.alicdn.com/captcha-frontend/FeiLin/1.5.1/feilin008….js:1:169658)

追查后确认这不是单点缺陷,而是四个可独立修复的问题叠加。崩溃链条:

  1. FeiLin 的 tE 是个 2 秒心跳,会自我重新武装(feilin008.js:170382 tV=setInterval(tE,2e3))
  2. 它 arm 的定时器错误地落在宿主车道,happyDOM.close() 收不走 → 不朽定时器
  3. 30 秒后墓碑 delete 掉 moveBy,心跳下次触发即 ReferenceError
  4. TUI 的 uncaughtException 处理器对一切 exit(1) → 代理进程死亡

本 PR 切断第 4 环,与其余三个 PR 互不依赖,可单独合入。


问题

Bun 下 happy-dom 的 VM 隔离不生效,Aliyun/FeiLin 脚本直接运行在宿主 realm。因此从它们回调里逃逸的异常,会以进程级 uncaughtException 到达,与我们自己的故障无从区分。

serve 模式早已划清这条界线 —— captcha-happy.ts 记录后继续运行,单次求解失败由池重试。TUI 的处理器却对一切 exit(1),把一个可恢复故障放大成了整个代理进程死亡。

同一条异常在两种模式下的命运(实测):

模式 结果
serve exitCode=0,打印 [captcha-guest-uncaught] 后继续服务
TUI exitCode=1,进程死亡

改动

新增 src/runtime/guest-error.ts,按抛出帧的来源判定归属:仅当栈 / sourceURL / message 中出现 SDK 的 CDN 主机才算 guest。

判据锚定在 URL authority 上并要求分隔符收尾,因此不会被绕过:

输入 判定
https://g.alicdn.com/…/feilin008….js guest
https://o.alicdn.com/…/AliyunCaptcha.js guest
https://captcha.ap-southeast-1.aliyuncs.com/x.js guest
https://evil-alicdn.com.attacker.net/x.js 宿主
https://cdn.example.com/p?ref=alicdn.com 宿主

同时跟随 cause 链(带自引用保护,循环不会死循环)。

取证不足一律判为宿主故障 —— 无栈、非 Error 对象、undefined 全部走致命路径。沉默地吞掉真实崩溃比误杀更糟。

TUI 据此放行 guest 报错并写入日志面板,其余仍照旧 cleanup() + exit(1)。


验证

在 stock captcha-happy(不含其余三个 PR)上注入现场那条栈:

TUI 存活 = true
日志面板 = "ReferenceError: moveBy is not defined (tE (feilin008….js:1:169658))"

真实内部故障仍然退出:

setTimeout(() => { throw new Error("genuine internal fault"); }, 10)
→ TUI 退出 = true
  • bun x tsc --noEmit 0 错误
  • bun test 739 pass / 0 fail(上游基线 732,本 PR 新增 7 条分类器测试)

现场报告(v4.5.5,多次复现,间隔十几到二十几分钟):

    zcode-proxy: tui crashed: ReferenceError: moveBy is not defined
        at tE (https://g.alicdn.com/captcha-frontend/FeiLin/1.5.1/feilin008….js:1:169658)

Bun 下 happy-dom 的 VM 隔离不生效,Aliyun/FeiLin 脚本直接运行在宿主 realm,
因此从它们的回调里逃逸的异常会以进程级 uncaughtException 到达,与我们自己的
故障无从区分。serve 模式早已划清这条界线(captcha-happy.ts 记录后继续运行,
单次求解失败由池重试);TUI 的处理器却对一切 exit(1),把一个可恢复故障放大成
了整个代理进程死亡。

新增 runtime/guest-error.ts 按抛出帧的来源判定归属:仅当栈/sourceURL/message
中出现 SDK 的 CDN 主机才算 guest。判据锚定在 URL authority 上并要求分隔符
收尾,所以 `evil-alicdn.com.attacker.net` 与 `?ref=alicdn.com` 都不算证据;
同时跟随 cause 链(带自引用保护)。取证不足一律判为宿主故障——沉默地吞掉真实
崩溃比误杀更糟。

TUI 据此放行 guest 报错并写入日志面板,其余仍照旧 cleanup + exit(1)。

验证:把现场那条栈注入 stock captcha-happy,TUI 存活且面板出现
"ReferenceError: moveBy is not defined (tE (feilin008….js))";真实内部故障
仍然退出。

@TriDefender TriDefender left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已验证并合入:CI 绿 + 本地 worktree 复跑 tsc 0 错 / 739 pass(与声明一致)。分类器测试覆盖 lookalike 域名与自引用 cause 链,"取证不足一律判宿主"的保守方向正确。独立于 #44–#46,先合止血。

非阻塞 follow-up:isGuestOriginError 把 message 里的 CDN URL 也算 guest 证据——我们自己抛的、错误消息恰好带 CDN URL 的故障会被误吞。建议后续收紧为 stack / sourceURL 两类硬证据(现有测试不受影响)。

@TriDefender
TriDefender merged commit 18ce636 into TriDefender:master Sep 6, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants