Skip to content

fix(security): 转义 voicebox / step-audio-editx 成功页的请求可控插值(真反射型 XSS) - #153

Merged
ReSerendipity merged 1 commit into
mainfrom
triage/codeql-small-fixes
Sep 23, 2026
Merged

ReSerendipity merged 1 commit into
mainfrom
triage/codeql-small-fixes

Conversation

@ReSerendipity

Copy link
Copy Markdown
Owner

修的是两条真反射型 XSS(CodeQL #107 / #108,py/reflective-xss high)

routes/generate/step_audio_editx/edit.py:196 与 routes/generate/voicebox/convert.py:186
直接把请求可控值插进 HTML 文本与属性,无任何转义:

插值 来源
edit_type / edit_info 表单原文(Form(...))
source_path 的 basename 上传文件名派生
tau、target_path basename 表单值 / persona 名
audio_filename 进 data-audio-filename、src、href、download 四处属性上下文

basename() 只去目录,不去 < " —— 所以这不是理论风险。修法:统一 html.escape(..., quote=True)
(quote=True 才覆盖属性上下文),数字类插值(elapsed/duration)不变。

验证与边界(说清楚,不夸大)

  • 已核:改动后 80 条既有测试通过(test_fe_be_consistency / test_a11y_static /
    test_engine_capability_contract / test_voice_clone_consent),且确认导入解析到本
    worktree 副本
    (integrated_app.__file__ 实测在此路径下),不是主仓另一分支那份。
  • ruff check / format 通过,ast 可解析。
  • 没有新增路由级 XSS 断言:这两个成功路径需要 stub 引擎才有办法打,仓内目前只有
    契约/能力级覆盖。列为后续项(不假装测过)。
  • 未改 URL 编码语义:/api/audio/{filename} 只做 HTML 转义,不做 urllib.parse.quote;
    文件名由应用生成,现状不含需要 URL 编码的字符,避免顺手改行为。

同批复核但未改的 5 条(判定与依据在 PR 描述与 docs 里,走 dismiss)

  • js/xss-through-dom 3 条(indextts20_clone.html:140、indextts2_clone.html:175、
    indextts2_duration.html:211):告警行是 previewAudio.src = URL.createObjectURL(file),
    只能产同源 blob: URL,不可能是 javascript:;同函数里的文件名走的是 textContent(DOM 文本赋值,非 HTML 解析)→ 误报。
  • py/url-redirection build(deps): bump github/codeql-action from 3 to 4 #61(routes/tabs.py:292):RedirectResponse(url=f"/?tab={tab_name}")
    目标是固定相对路径,用户串落在 query 值里,不构成开放重定向;该文件另有 frozenset 白名单
    显式拦 tab_name="../../config.yaml" → 误报。
  • py/bad-tag-filter feat(ui): 模型切换器/语音创建表单/健康监控与样式增强;后端路由支撑 #62(engines/voxcpm2/design.py:82):这段正则去标签的对象是送进模型的
    文本提示
    (并受 300 字符硬限),不是 XSS 出口;全仓 Jinja 模板里 |safe 仅 1 处
    (partials/progress_bar.html,自有内容),渲染面由 autoescape 覆盖 → 规则误用。

CodeQL #107/#108 (py/reflective-xss, high) 复核为真:两处 HTMLResponse 的 f-string 把
edit_type / edit_info(表单原文)、上传文件名派生的 basename、tau、target basename 直接
插进 HTML,audio_filename 还进了 4 处属性上下文;basename() 不去 < 与引号,属可实现
的反射型 XSS。统一改为 html.escape(..., quote=True),数字插值与 URL 语义不变。

同批复核的另 5 条判为误报(blob: URL / textContent / 固定相对重定向 / 模型输入去标签),
判定与依据写在 PR 描述,走 CodeQL dismiss,不在本 commit 改。

验证:80 条既有测试通过,且确认导入解析到本 worktree 副本(不是主仓另一分支那份);
ruff check/format 通过、ast 可解析。未加路由级 XSS 断言(需 stub 引擎),列为后续项。

Signed-off-by: ReSerendipity <zengyangc@outlook.com>
@ReSerendipity
ReSerendipity enabled auto-merge (squash) September 23, 2026 16:07
@ReSerendipity
ReSerendipity merged commit 3d549a8 into main Sep 23, 2026
30 checks passed
ReSerendipity added a commit that referenced this pull request Sep 23, 2026
新增 §7(现状以该节为准,§6 是 09-21 快照):

- 本批 dismiss 16 条(routes/model.py 的 11 条 stack-trace 按 #96 绑 mitigated;
  3 条 blob:/textContent 判误报;tabs.py 相对重定向 1 条;voxcpm2/design.py 去标签
  属规则误用 1 条),每条在 CodeQL 上写了 280 字符内的绑定理由。open 76 → 56(实测分布
  36 path-injection + 18 stack-trace + 2 reflective-xss,后者等 #153 合并消解)。
- 剩余 40 条逐条给判定:19 条已缓解可下批收口;17 条 path-injection 无强校验
  (generation 3 / persona_metadata 6 / resolve_persona_ref 2 / voxcpm2 design+script 6);
  stack-trace 里确认 5 处 settings.py 把 str(exc) 原文放进响应,13 处待读。
- 写明一条口径:html.escape 只防 XSS,不防信息泄露,stack-trace 这族不能拿它交差。
- §7.3 衔接 issue #97(锁集已按"退"路自洽,但 4.52.x 的 16 条公告代价未见 pip-audit
  命中/豁免说明)与 #99(3 处真 XSS 的 incomplete-sanitization 整族已归零,修法未复核)。
- SECURITY_REMEDIATION_TRACKER.md 补指向本表的反向链接(此前该链接依赖已关闭的 #81)。

Signed-off-by: ReSerendipity <zengyangc@outlook.com>
@ReSerendipity
ReSerendipity deleted the triage/codeql-small-fixes branch September 24, 2026 11:56
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