MoonshotAI / MoonshotAI/kimi-code
web: 消息内容含 `[` + 大量 `\"` 转义时,打开该 session 渲染线程永久卡死(正则灾难性回溯)
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 7.5k
- Forks
- 1.2k
- Avg merge
- 11h 53m
- Merged PRs (30d)
- 350
Description
你运行的 Kimi Code 版本是?
v0.41.0
你使用的是哪个开放平台/订阅?
Kimi Code (kimi.com/code)
你使用的是哪个模型?
k3-256k
你的电脑平台是?
Darwin 25.6.0 arm64 arm
你遇到了什么问题?
在 web UI(kimi web)中打开某个特定 session 时,浏览器标签页永久卡死:主线程 100% CPU 硬锁死,DevTools 里最后一批请求全部停在 pending,页面始终不可交互。同一个 session 在 TUI(kimi --continue)和 kimi vis 里完全正常,其他 session 在 web UI 也正常——卡死由该 session 的内容触发,且 session 已完全退出、未在任何地方运行时依然复现。
与 #3519 不同:#3519 是「正在 streaming 的 session 打开时卡在 Loading」,本 issue 是内容触发的 ReDoS,session 完全空闲/退出状态下即可稳定复现。
复现步骤?
- 在任意 session 中粘贴下面的保证复现串(
[+ 60 组\";该正则为指数级回溯,55 组起卡死 >20s,浏览器里等同永久):
[\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"
真实场景中的触发源是复制日志时带入的内嵌转义 JSON(如飞书卡片 payload,特征相同:[ 紧跟大量 \")。脱敏后的真实触发片段(204 字符,约卡 0.7s,仅用于说明原理,单独用不足以观察到卡死):
[{\"tag\":\"div\",\"text\":{\"content\":\"Savings XXXX XXXX, XXXX:0, XXXXX:0000000X, XXXXXX:0000000, XX:00X\",\"tag\":\"lark_md\"}},{\"tag\":\"hr\"},{\"tag\":\"div\",\"text\":{\"content\":\"XXXX: 00.00
- 完全退出该 CLI session。
- 运行
kimi web,在 web UI 打开该 session。 - 标签页在首次渲染时卡死,永不恢复。
期望的行为是什么?
- 预期:session 正常渲染(慢一点也没关系)。
- 实际:渲染主线程死循环,标签页彻底卡死。DevTools 中会话状态类请求(
/status、/goal、/warnings、/skills、POST /fs:git_status)显示为pending——但这只是表象:服务端响应全部正常(已用 curl 验证,单个和并发突发全部 <1s 返回 200)。pending 只是因为渲染线程冻结后永远无法处理响应回调。
补充信息
根因
渲染线程卡死在 web bundle(assets/index-*.js,压缩后函数名 t6)里的一个正则匹配中:
// 输入框附件引用解析正则
/(?<![!\\])\[(?:\\.|[^\]\n])+\]\(kimi-code-composer:\/\/attachments\/([1-9]\d*)\)/g
内层 (?:\\.|[^\]\n])+ 存在歧义:每个反斜杠位置两个分支都能消费(\\. 吃 2 字符,[^\]\n] 只吃反斜杠本身)。当输入以 [ 开头、包含数百个 \" 且永远匹配不到结尾的 ](kimi-code-composer://attachments/...) 时,匹配尝试按指数级回溯 → 灾难性回溯(ReDoS)。
证据
- 对冻结的标签页发送 CDP
Debugger.pause,间隔 1.5s 采样 3 次——三次调用栈逐字节相同,定格在同一指令:
t6 @ index-*.js L307:C6916 <- matchAll(i7e) 内部
t7e @ index-*.js L307:C5247
iw @ index-*.js L322:C895
V7e @ index-*.js L326:C8514
t @ index-*.js L326:C12696
(anonymous) @ L754:C6816
bB / Z5 / get dirty / C / n2 / PB (响应式框架帧)
- 在 Node 中用该正则直接对该 session 的
wire.jsonl跑matchAll:挂死(>20s,手动杀掉)。用带超时的 worker 逐行二分定位到单条消息,再在消息内收缩得到上述 204 字符触发串(204 字符约耗时 0.7s;真实 7.5KB 消息等同永久)。 - 服务端已排除:受影响 session 的
GET /status|goal|warnings|skills与POST /fs:git_status用 curl 单独/并发调用全部 <1s 返回 200。
修复建议
消除内层分组的歧义即可消灭指数回溯,例如让单字符分支排除反斜杠:
/(?<![!\\])\[(?:\\.|[^\]\\\n])+\]\(kimi-code-composer:\/\/attachments\/([1-9]\d*)\)/g
或者限制扫描长度 / 改成手写扫描器。另外建议用上面的触发串加一个回归测试,成本很低。
临时绕过方案
受影响的 session 用 TUI(kimi --continue)或 kimi vis 查看,避免在 web UI 打开。任何「[ + 大量 \"」形态的粘贴内容(复制含转义 JSON 的日志时很常见)都会触发此 bug。
Contribution
- 我愿意自己提交修复此 bug 的 PR(请先等待维护者在本 issue 中批准)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at the attachment-reference regex in assets/index-*.js around L307, and reproduce the hang with the supplied escape-string input or the Node matchAll check. Trace the bundled code to its web source, add the supplied trigger as a regression test, and confirm the affected session renders normally in kimi web without the main thread stalling.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- frontend, performance, testing-qa, web-dev
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 76/100