Skip to content

🔒 使用 DOMPurify 清理公告通知 HTML 内容#1274

Merged
CodFrm merged 2 commits intomainfrom
fix/1273
Mar 3, 2026
Merged

🔒 使用 DOMPurify 清理公告通知 HTML 内容#1274
CodFrm merged 2 commits intomainfrom
fix/1273

Conversation

@CodFrm
Copy link
Member

@CodFrm CodFrm commented Mar 3, 2026

概述 Descriptions

close #1273

变更内容 Changes

  • 使用 DOMPurify 对服务端下发的公告 HTML 进行白名单过滤,防止潜在的 UI 注入风险
  • 新增 src/pkg/utils/sanitize.ts 封装清理逻辑
  • 允许的标签:b, i, a, br, p, strong, em, span
  • 允许的 CSS 属性:color, font-size, font-weight, font-style
  • 其它标签、属性和危险的 CSS 属性(如 positionbackground)会被自动过滤

  • Sanitize server-provided notice HTML with DOMPurify whitelist filtering to prevent potential UI injection risks
  • Add src/pkg/utils/sanitize.ts to encapsulate sanitization logic
  • Allowed tags: b, i, a, br, p, strong, em, span
  • Allowed CSS properties: color, font-size, font-weight, font-style
  • All other tags, attributes, and dangerous CSS properties (e.g. position, background) are automatically stripped

截图 Screenshots

N/A

使用 DOMPurify 对服务端下发的公告 HTML 进行白名单过滤,防止潜在的
UI 注入风险。只允许基础标签和安全的 CSS 属性(颜色、字体相关)。

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

该 PR 针对弹窗里“更新公告/通知”由服务端下发并以 dangerouslySetInnerHTML 渲染的场景,引入 DOMPurify 白名单清理,降低扩展 UI 被远程 HTML 注入的安全风险(对应 issue #1273)。

Changes:

  • 新增 src/pkg/utils/sanitize.ts:封装基于 DOMPurify 的 HTML 清理与 style 属性白名单过滤
  • Popup 公告渲染前对 checkUpdate.notice 做清理后再注入
  • 增加 dompurify 依赖并更新锁文件

Reviewed changes

Copilot reviewed 3 out of 4 changed files in this pull request and generated 4 comments.

File Description
src/pkg/utils/sanitize.ts 新增 DOMPurify 清理逻辑与 CSS 白名单 hook
src/pages/popup/App.tsx 公告内容渲染前调用 sanitizeHTML
package.json 添加 dompurify 运行时依赖
pnpm-lock.yaml 锁定新增依赖及其可选依赖
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

Comment on lines +6 to +16
// 过滤不安全的 CSS 属性,只保留白名单中的属性
DOMPurify.addHook("afterSanitizeAttributes", (node) => {
if (node instanceof HTMLElement && node.hasAttribute("style")) {
const { style } = node;
for (let i = style.length - 1; i >= 0; i--) {
if (!ALLOWED_CSS_PROPERTIES.includes(style[i])) {
style.removeProperty(style[i]);
}
}
}
});
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

这里在模块顶层调用 DOMPurify.addHook 会产生全局副作用:任何后续对 DOMPurify 的使用都会被这个 style 白名单逻辑影响,并且在热重载/多实例场景可能重复注册 hook。更稳妥的做法是创建专用的 DOMPurify 实例(例如基于当前 window 创建实例)并只在该实例上注册 hook,或至少加一次性初始化保护。

Copilot uses AI. Check for mistakes.
Comment on lines +18 to +24
// 对 HTML 进行清理,只保留安全的标签和属性
export function sanitizeHTML(html: string): string {
return DOMPurify.sanitize(html, {
ALLOWED_TAGS: ["b", "i", "a", "br", "p", "strong", "em", "span"],
ALLOWED_ATTR: ["href", "target", "style"],
});
}
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

新增的 sanitizeHTML 属于安全关键逻辑,但目前在 src/pkg/utils/ 下没有对应单测覆盖。建议补充 sanitize.test.ts:至少覆盖(1)移除 <script>/事件处理属性(2)过滤非白名单标签与属性(3)style 仅保留白名单 CSS 属性,且能正确剔除如 position/background/top 等。

Copilot generated this review using guidance from repository custom instructions.
style={{ display: showAlert ? "flex" : "none" }}
type="info"
content={<div dangerouslySetInnerHTML={{ __html: checkUpdate.notice || "" }} />}
content={<div dangerouslySetInnerHTML={{ __html: sanitizeHTML(checkUpdate.notice || "") }} />}
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

这里在组件每次 render 时都会调用一次 DOMPurify 清理,DOMPurify 相对开销较大,可能导致 popup 频繁重渲染时的性能抖动。建议用 useMemo 基于 checkUpdate.notice 缓存清理后的字符串(或在拿到 notice 时就预先清理并存储)。

Copilot uses AI. Check for mistakes.
Comment on lines +20 to +23
return DOMPurify.sanitize(html, {
ALLOWED_TAGS: ["b", "i", "a", "br", "p", "strong", "em", "span"],
ALLOWED_ATTR: ["href", "target", "style"],
});
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

sanitizeHTML 允许 target 但未同时允许/补齐 rel="noopener noreferrer"。当公告里存在 <a target="_blank"> 时可能引入反向 tabnabbing 风险(新页面可通过 window.opener 操作原扩展页面)。建议:要么不允许 target,由外层统一用安全方式打开链接;要么在 DOMPurify hook 中对 A 标签设置 rel=noopener noreferrer(并将 rel 加入允许属性)。

Copilot uses AI. Check for mistakes.
Copy link
Collaborator

@cyfung1031 cyfung1031 left a comment

Choose a reason for hiding this comment

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

@CodFrm 你直接改 JSON 吧。内容在ScriptCat扩充做Render
资讯下载量也会较小
这些 DOMPurify不太可靠

@CodFrm
Copy link
Member Author

CodFrm commented Mar 3, 2026

@CodFrm 你直接改 JSON 吧。内容在ScriptCat扩充做Render 资讯下载量也会较小 这些 DOMPurify不太可靠

什么意思?改JSON?

@cyfung1031
Copy link
Collaborator

cyfung1031 commented Mar 3, 2026

@CodFrm 你直接改 JSON 吧。内容在ScriptCat扩充做Render 资讯下载量也会较小 这些 DOMPurify不太可靠

什么意思?改JSON?

打錯字。改用JSON

由「接收服务端下发的公告 HTML 」改成 「接收服务端下发的公告 JSON 」
把 JSON物件在本地端做解析,显示到 React 元件

而不是直接下载 HTML内容 直接放在 React

@cyfung1031
Copy link
Collaborator

cyfung1031 commented Mar 3, 2026

@CodFrm 你直接改 JSON 吧。内容在ScriptCat扩充做Render 资讯下载量也会较小 这些 DOMPurify不太可靠

什么意思?改JSON?

打錯字。改用JSON

由「接收服务端下发的公告 HTML 」改成 「接收服务端下发的公告 JSON 」 把 JSON物件在本地端做解析,显示到 React 元件

而不是直接下载 HTML内容 直接放在 React

呀。现在已经是 json
只是 json 里放了 html "notice"

把这个 notice 做成物件吧

例如

{
  "notice": {
    "line1": {
      "message": "第一行文字",
      "size": 12,
      "color": "red"
    },
    "line2": {
      "message": "第二行文字",
      "size": 10,
      "color": "#000"
    }
  }
}

@cyfung1031
Copy link
Collaborator

或者你想简单点
在 server 那边做 html -> json
再在 SC extension 做 rendering

可以用这个

https://github.com/lemonadejs/html-to-json

把 type 不合适的都删掉,只保留 div, span, b, p, br 等

@cyfung1031
Copy link
Collaborator

@CodFrm 你直接改 JSON 吧。内容在ScriptCat扩充做Render 资讯下载量也会较小 这些 DOMPurify不太可靠


我收回前言

这个 DOMPurify 看来也不错
就这样吧

@CodFrm CodFrm merged commit 4617709 into main Mar 3, 2026
1 of 3 checks passed
@CodFrm
Copy link
Member Author

CodFrm commented Mar 3, 2026

你改到sw环境去了,好像出问题了

@cyfung1031
Copy link
Collaborator

你改到sw环境去了,好像出问题了

呀。它可能要用 document 来做处理。。。 sw 不能用sanitizeHTML

cyfung1031 added a commit that referenced this pull request Mar 3, 2026
CodFrm pushed a commit that referenced this pull request Mar 4, 2026
* fix #1274

* Update index.ts

* Array.includes -> Set.has
CodFrm added a commit that referenced this pull request Mar 14, 2026
* 🔒 使用 DOMPurify 清理公告通知的 HTML 内容 #1273

使用 DOMPurify 对服务端下发的公告 HTML 进行白名单过滤,防止潜在的
UI 注入风险。只允许基础标签和安全的 CSS 属性(颜色、字体相关)。


* code update

---------

Co-authored-by: cyfung1031 <[email protected]>
CodFrm pushed a commit that referenced this pull request Mar 14, 2026
* fix #1274

* Update index.ts

* Array.includes -> Set.has
CodFrm added a commit that referenced this pull request Mar 16, 2026
* additional test for responseType=document

* 🐛 修复 脚本设置-授权管理 控制无效的问题 (#1267)

* 🐛 修复 脚本设置-授权管理 控制无效的问题

* 将校验逻辑放到confirm

* 修复GM cookie权限判断

* 删除directDeny

* buildCacheKey

* 整理代码

* Update src/app/service/service_worker/permission_verify.ts

Co-authored-by: Copilot <[email protected]>

---------

Co-authored-by: Copilot <[email protected]>

* 🔒 使用 DOMPurify 清理公告通知 HTML 内容 (#1274)

* 🔒 使用 DOMPurify 清理公告通知的 HTML 内容 #1273

使用 DOMPurify 对服务端下发的公告 HTML 进行白名单过滤,防止潜在的
UI 注入风险。只允许基础标签和安全的 CSS 属性(颜色、字体相关)。


* code update

---------

Co-authored-by: cyfung1031 <[email protected]>

* 🐛 修复 include *?* 表达式处理问题 #1271 (#1272)

* 🐛 修复 include *?* 表达式处理问题 #1271

* Added error handling to avoid crash

* update globSplit

* update globSplit

* update globSplit

---------

Co-authored-by: cyfung1031 <[email protected]>

* fix #1274 (#1275)

* fix #1274

* Update index.ts

* Array.includes -> Set.has

* 🐛 修复与隐身窗口检查权限冲突导致反复重启的问题

* 📄 docs: update Chrome Web Store URLs to new domain (#1279)

Google migrated the Chrome Web Store from `chrome.google.com/webstore`
to `chromewebstore.google.com`. This updates all CWS URLs across
README files to use the new domain.

The old URLs currently redirect, but will eventually stop working.

* ✅ 添加 Playwright E2E 测试及 GM API 功能测试 (#1283)

* ✅ 添加 Playwright E2E 测试

- 新增 22 个 E2E 测试覆盖 Options、Popup、Install、Editor、Settings 页面
- 配置 Playwright 使用 --headless=new 模式加载扩展
- 在 CI workflow 中添加 E2E 测试 job

* ✅ 添加 GM API E2E 测试

新增 gm-api.spec.ts 测试三类 GM API:
- GM_ 同步 API (gm_api_test.js): 29 项测试
- GM.* 异步 API (gm_api_async_test.js): 29 项测试
- Content 注入测试 (inject_content_test.js): 11 项测试

实现要点:
- 两阶段浏览器启动:Phase 1 启用 userScriptsAccess,Phase 2 重启运行测试
- 自动审批权限确认弹窗(cookie 等需要用户授权的 API)
- 通过剪贴板注入脚本代码到 Monaco 编辑器
- 替换 jsdelivr CDN 为 unpkg 提升资源加载速度
- 去除 @require/@resource 的 SRI hash 避免校验失败

更新 utils.ts 中 installScriptByCode 增加保存失败的 fallback 检测

* 🐛 修复 GM API E2E 测试 CI 兼容性

- Phase 1 添加 --headless=new 参数,修复 CI 无 X server 环境
- 添加 eslint-disable 注释消除 Playwright use() 的误报
- prettier 格式化修正

* 🐛 修复 E2E 测试 CI 兼容性问题

- vitest.config.ts: 排除 e2e/ 目录避免 Vitest 误跑 Playwright 测试
- eslint.config.mjs: 为 e2e/ 目录关闭 react-hooks/rules-of-hooks 规则
- e2e/options.spec.ts: 菜单正则加 /i 标志修复英文环境大小写匹配
- prettier 格式化修正

* 🚑 修复其他扩展注入 chrome.runtime 导致环境误判的问题 #1280 (#1281)

* 🐛 修复其他扩展注入 chrome.runtime 导致环境误判的问题 #1280

移除 isContent 运行时检测,改为通过 CustomEventMessage.envTag 在构建入口确定环境,
避免其他扩展(如大学搜题酱)向页面注入 chrome.runtime 对象导致 inject 环境被误判为 content 环境。

* Update src/app/service/content/gm_api/gm_api.ts

Co-authored-by: Copilot <[email protected]>

* update

* 删除不必要的isconnect

* 将 typecheck 集成到 lint/lint-fix 脚本中

* 修复引用

* 删除test-results

---------

Co-authored-by: cyfung1031 <[email protected]>
Co-authored-by: Copilot <[email protected]>

* ⚙️ 优化 CI 流水线和测试配置

- 缓存 Playwright 浏览器避免重复下载
- 测试失败时上传截图/视频/报告等调试产物
- Playwright CI 环境启用 HTML+list 双 reporter、失败截图和视频
- 各工具链配置屏蔽 .claude 目录

* ✅ 修复 e2e 测试 service worker 超时并优化等待策略

- gm-api.spec.ts: Phase 2 重启 context 后等待 service worker 注册完成
  再交给 fixtures,避免 extensionId fixture 用 10s 全局超时等待失败
- gm-api.spec.ts: 用事件驱动 Promise 替换 500ms 轮询循环,
  console 结果一出现立即继续
- utils.ts: installScriptByCode 用 DOM 事件等待替代固定延迟:
  click 后等光标出现,粘贴后等 .view-lines 内容变化

---------

Co-authored-by: wangyizhi <[email protected]>
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Michael Lip <[email protected]>
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.

[BUG] security vulnerability

3 participants