缺陷 #2
openQA验收 #1: 会议管理与话题管理功能验收(含 Markdown/BFF 跨端验证)
TPC-LEG-001:历史 HTML 转 Markdown 后脚本文本残留为数学块
0%
Description
用例¶
TPC-LEG-001:历史 HTML 转 Markdown 的安全转换。
复现步骤¶
- 创建仅用于 QA 的话题 QA-TPC-20260922-1001。
- 将历史 HTML 形式的段落、列表和 script 标签写入该 QA 话题。
- 在话题编辑页触发历史 HTML 转 Markdown,并保存。
- 打开话题详情和 BFF 输出检查转换结果。
预期结果¶
script 标签及其脚本文本应被完整丢弃,不执行、不展示,也不进入转换后的 Markdown。
实际结果¶
script 标签本身被移除且没有执行,但脚本文本 window.QA_LEGACY_XSS=1 被保留,并转换成 $$...$$ 数学块继续显示和存储。
影响¶
不会直接执行脚本,但不可信脚本文本会污染话题内容并在前端展示,历史内容迁移结果不符合安全净化预期。
证据¶
- /Users/gexiaowei/Documents/work/mindsetech/ndb/qa-evidence/tpc-legacy-html-editor.png
- /Users/gexiaowei/Documents/work/mindsetech/ndb/qa-evidence/tpc-legacy-converted-detail.png
Files
RA Updated by Redmine Admin about 2 months ago
已完成前端修复并做回归验证(未修改后端、未提交/推送代码)。
变更范围:
- src/components/MarkdownEditor/index.vue
- src/components/MarkdownEditor/legacyHtml.mjs
- src/components/MarkdownEditor/legacyHtml.test.mjs
修复行为:历史 HTML 在 html2md 前先移除 script/style/iframe/object/embed/template/noscript 标签及其内容,保留普通标题、列表与正文,避免脚本文本被转换成 Markdown 数学块。
验证:
- Node 定向/相关测试 36/36 通过(含 4 个历史 HTML 清洗测试)
- oxfmt --check 通过;oxlint 通过;git diff --check 通过
- pnpm run build:staging 通过(仅现有 Node DEP0205 与 Tailwind sourcemap 警告)
- Ego Browser TaskSpace 13 在实际 Vite 源模块上执行历史样例:QA_LEGACY_XSS=false、display:none=false,Legacy QA HTML/Item A/Item B 均保留。
边界:未改动数据库历史记录;未在真实登录态下打开一条现存历史会议记录。
RA Updated by Redmine Admin about 2 months ago
QA 独立复测结论:PASS
TPC-LEG-001 回归证据¶
执行日期:2026-09-22
用例目标¶
历史 HTML 转换为 Markdown 前,应完整移除危险元素及其内容;正常标题、段落、列表与行内格式必须保留。
Node 定向测试¶
命令:
node --test src/components/MarkdownEditor/legacyHtml.test.mjs src/views/meetings/meetingFormContract.test.mjs src/views/topics/topicDetailContract.test.mjs
结果:17 tests,17 pass,0 fail。
其中 legacyHtml.test.mjs 覆盖:
- script/style 元素及内容移除;
- iframe/object/template/noscript/embed 移除;
- 未闭合危险元素后的内容丢弃;
- 正常段落、列表与行内格式保留。
浏览器实际源模块输入¶
在 Ego Browser TaskSpace 14 中打开本地 Vite 应用,并从实际开发服务器动态导入:
/src/components/MarkdownEditor/legacyHtml.mjs?qa=redmine-2-retest
输入:
<h2>正常标题</h2><script>window.QA_LEGACY_XSS=1</script><p>正常段落</p><ul><li>保留列表 A</li><li><strong>保留列表 B</strong></li></ul>
实际输出:
<h2>正常标题</h2><p>正常段落</p><ul><li>保留列表 A</li><li><strong>保留列表 B</strong></li></ul>
断言:
scriptTextRemoved=trueheadingPreserved=trueparagraphPreserved=truelistPreserved=true
结论:Pass。