Skip to content

fix: 引擎直切 503(#84)与 onclick 属性 XSS(#99) - #116

Merged
ReSerendipity merged 4 commits into
mainfrom
fix/issue-97-99-84
Sep 22, 2026
Merged

ReSerendipity merged 4 commits into
mainfrom
fix/issue-97-99-84

Conversation

@ReSerendipity

Copy link
Copy Markdown
Owner

内容

#84 / #99 两个 issue 的修复路径落地(#97 经核实已在 main 由 91a0846 + PR #98 修复,本 PR 补验证证据,不在本 PR 内)。

#84 引擎直切 12GB 卡必 503(46c57a8)

  • load.py 新增 _unload_loaded_engines_for_load():routes 直连的专用加载器在预检前先全量卸载驻留引擎(委托 unload_model(),RLock 同线程重入同 switch_engine 阶段④先例),并按基线复用 _wait_vram_freed 核验真实回收;无驻留引擎时幂等空操作。
  • load_voxcpm2 原先只手工摘 voxcpm 两槽位(漏 enhancer/门面/persona 缓存/整个 IndexTTS),统一替换。
  • load_indextts2 / load_indextts20 新增 auto_unload=True;switch_engine 热待机路径传 False 保住双引擎常驻语义。
  • InsufficientVRAMError 文案区分「已清场仍不足」。

#99 onclick 属性 XSS(e5c4edf)

  • lora_manager.html / history.html 三处 innerHTML 拼 onclick 的接线改为 createElement + textContent + dataset + addEventListener,数据经闭包传递;同构造点的 name/description/path 裸插值一并收进 textContent;SVG 图标为纯静态字面量。

验证

  • pytest:test_engine_switch.py(含 4 条新回归)+ test_dependency_consistency.py + test_check_pin_crossconflicts.py 共 34 passed
  • 前端:render_pages + jsdom 冒烟 + 31 个模板内联脚本语法解析全绿
  • check_pin_crossconflicts.py 直跑:95 个钉版包对 PyPI requires_dist 实测,交叉冲突 0
  • GPU 真机切换序列(加载 voxcpm2 → 切 indextts2 → 切 indextts20 → 切回)待桌面显存空闲时人工复验

routes 的 load 端点把 voxcpm2/indextts2/indextts20 直接指到专用加载器,
绕过 switch_engine 的 M-R1 显存记账与卸载阶段,预检又是裸比较:
VoxCPM2 已加载时(占用 ~9GB),直切 IndexTTS 2.5 必然
503 INSUFFICIENT_VRAM(issue #84 三段证据)。

修复(issue 选项 B 为主、C 的可诊断性吸收):
- load.py 新增 _unload_loaded_engines_for_load():加载前卸载全部驻留
  引擎(委托 unload_model(),RLock 同线程重入与 switch_engine 阶段4
  同款),随后按基线复用 _wait_vram_freed 核验真实回收(延迟导入避免
  与 switch.py 的模块级循环依赖);无驻留引擎时为幂等空操作。
- load_voxcpm2 原先只手工摘 voxcpm 两槽位(漏 enhancer/门面/persona
  缓存/整个 IndexTTS),统一替换为该助手。
- load_indextts2 / load_indextts20 新增 keyword-only auto_unload=True;
  switch_engine 热待机路径显式传 False 保住双引擎常驻语义,传统路径
  已卸载、传 True 只是空操作。
- InsufficientVRAMError 文案区分"已清场仍不足"与既有场景,不再误导。

验证:tests/test_engine_switch.py 新增 4 条(清场接线/空操作幂等/
热待机跳过/报错文案),连同既有用例 32 passed;GPU 真机切换序列
因本机桌面应用占用 8.6/12.2GB 显存暂不可跑,留待空闲时人工复验
(加载 voxcpm2 -> 切 indextts2 -> 切 indextts20 -> 切回,观察
[模型加载] 清场日志与 2xx)。

Signed-off-by: ReSerendipity <zengyangc@outlook.com>
原先两处把用户可控字符串只做 JS 引号转义就拼进 innerHTML 的 onclick
属性——JS 字符串转义不等于 HTML 属性转义,文件名含双引号即可闭合属性
注入 <img onerror=...>;服务化部署(TTS_API_AUTH_ENABLED=1 + 0.0.0.0)
下即存储型 XSS(issue #99 分诊表 3 处)。

- lora_manager.html:卡片改 createElement + textContent + dataset +
  addEventListener;同步把同源未转义的 name/description/path 插值一并
  收进 textContent(超出 issue 点名的 onclick 两处,属同一构造点的
  同类注入面);SVG 图标为纯静态字面量经 innerHTML 注入。
- history.html:play/download/hide/delete 四按钮同样改事件闭包传参,
  escName 拼接路径整体移除;e.currentTarget 替代原先内联 onclick 的
  this 语义。
- 行为保持不变:按钮文案/回调/参数/stopPropagation 时序一致;
  syncLoraCardStates 依赖的 .lora-status-badge 与 dataset.path 保留。

验证:tests/frontend 冒烟链全绿(render_pages 渲染 + jsdom 结构断言 +
31 个模板内联脚本语法解析);加载/卸载 LoRA 与历史播放的真机点验需人工。

Signed-off-by: ReSerendipity <zengyangc@outlook.com>
Signed-off-by: ReSerendipity <zengyangc@outlook.com>
#99 的分诊扫描漏掉了第 4 个注入点:updateLoraInfo() 把选中检查点的
name / path / base_model / r / lora_alpha / steps 裸拼进 innerHTML——
这些字段源自检查点目录名与 adapter_config.json,均用户可控。

发现过程:PR 分支的 Playwright UI 验证里,对恶意命名检查点点击[启用]
后页面实际弹出了 alert(1)(img src=x onerror 在 info 面板执行),
复现 4/4 次点击。本提交把信息面板改为 DOM API + textContent,与卡片
修法一致;视觉效果不变(label: value 行)。

验证:tests/frontend Playwright(系统 Edge)真页面驱动——mock 恶意
检查点后全文档 img[src=x] 计数 0、info 面板以文本呈现恶意名。
switch_engine 现在按热待机与否向 load_indextts2/load_indextts20 传
auto_unload(#84),TestMissingWeights 的零参 fake_loader 桩会抛
TypeError 并被异常分支误判成需要回滚。桩签名加 **_kwargs 透传,
M-R9 升级断言与热待机「不回滚」断言均保持原语义。

验证:test_release_robustness.py + test_engine_switch.py 共 20 passed。

Signed-off-by: ReSerendipity <zengyangc@outlook.com>
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.

1 participant