Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"crypto-js": "^4.2.0",
"dayjs": "^1.11.13",
"dexie": "^4.0.10",
"dompurify": "^3.3.1",
"eslint-linter-browserify": "9.26.0",
"eventemitter3": "^5.0.1",
"fast-xml-parser": "^5.3.6",
Expand Down
16 changes: 16 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion src/app/service/service_worker/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { FaviconDAO } from "@App/app/repo/favicon";
import { onRegularUpdateCheckAlarm } from "./regular_updatecheck";
import { cacheInstance } from "@App/app/cache";
import { InfoNotification } from "./utils";
import { sanitizeHTML } from "@App/pkg/utils/sanitize";

// service worker的管理器
export default class ServiceWorkerManager {
Expand Down Expand Up @@ -115,7 +116,7 @@ export default class ServiceWorkerManager {
.then((resp: { data: { [key: string]: any; notice: string; version: string } }) => {
const data = resp.data;
systemConfig
.getCheckUpdate()
.getCheckUpdate({ sanitizeHTML })
.then((items) => {
const isRead = items.notice !== data.notice ? false : items.isRead;
systemConfig.setCheckUpdate({ ...data, isRead: isRead });
Expand Down
18 changes: 13 additions & 5 deletions src/pages/popup/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Discord, DocumentationSite, ExtVersion, ExtServer } from "@App/app/const";
import { sanitizeHTML } from "@App/pkg/utils/sanitize";
import { Alert, Badge, Button, Card, Collapse, Dropdown, Menu, Switch, Tooltip } from "@arco-design/web-react";
import {
IconBook,
Expand Down Expand Up @@ -270,7 +271,7 @@ function App() {
const checkScriptEnableAndUpdate = async () => {
const [isEnableScript, checkUpdate] = await Promise.all([
systemConfig.getEnableScript(),
systemConfig.getCheckUpdate(),
systemConfig.getCheckUpdate({ sanitizeHTML }),
]);
if (!hookMgr.isMounted) return;
setIsEnableScript(isEnableScript);
Expand Down Expand Up @@ -374,13 +375,16 @@ function App() {
]).then(([resp]: [{ data: { notice: string; version: string } } | null | undefined, any]) => {
let newCheckUpdateState = 0;
if (resp?.data) {
let notice = "";
if (typeof resp.data.notice === "string") notice = sanitizeHTML(resp.data.notice);
const version = resp.data.version;
setCheckUpdate((items) => {
if (resp.data.version === items.version) {
if (version === items.version) {
newCheckUpdateState = 2;
return items;
}
const isRead = items.notice !== resp.data.notice ? false : items.isRead;
const newCheckUpdate = { ...resp.data, isRead };
const isRead = items.notice !== notice ? false : items.isRead;
const newCheckUpdate = { version, notice, isRead };
systemConfig.setCheckUpdate(newCheckUpdate);
return newCheckUpdate;
});
Expand Down Expand Up @@ -482,7 +486,11 @@ function App() {
<Alert
style={{ display: showAlert ? "flex" : "none" }}
type="info"
content={<div dangerouslySetInnerHTML={{ __html: checkUpdate.notice || "" }} />}
content={
<div
dangerouslySetInnerHTML={{ __html: checkUpdate.notice /* notice is already sanitized by dompurify */ }}
/>
}
/>
<Collapse
bordered={false}
Expand Down
6 changes: 4 additions & 2 deletions src/pkg/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -393,12 +393,14 @@ export class SystemConfig {
});
}

getCheckUpdate() {
return this._get<Parameters<typeof this.setCheckUpdate>[0]>("check_update", {
async getCheckUpdate(opts?: { sanitizeHTML?: (html: string) => string }) {
const result = await this._get<Parameters<typeof this.setCheckUpdate>[0]>("check_update", {
notice: "",
isRead: false,
version: ExtVersion,
});
if (typeof opts?.sanitizeHTML === "function") result.notice = opts.sanitizeHTML(result.notice);
return result;
}

setEnableScript(enable: boolean) {
Expand Down
24 changes: 24 additions & 0 deletions src/pkg/utils/sanitize.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import DOMPurify from "dompurify";

// 允许的安全 CSS 属性白名单
const ALLOWED_CSS_PROPERTIES = ["color", "font-size", "font-weight", "font-style"];

// 过滤不安全的 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]);
}
}
}
});
Comment on lines +6 to +16
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.

// 对 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"],
});
Comment on lines +20 to +23
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.
}
Comment on lines +18 to +24
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.
Loading