Skip to content

Commit 6c0afea

Browse files
authored
Tighten parser attributes and image size constraints (#62)
1 parent a3df287 commit 6c0afea

3 files changed

Lines changed: 124 additions & 9 deletions

File tree

index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
<meta charset="UTF-8" />
55
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
66
<meta name="referrer" content="no-referrer">
7+
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' https: wss:; frame-src 'none'; object-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; upgrade-insecure-requests" />
78
<meta name="description" content="Physics Lab Web 物理实验室线上社区" />
89
<meta name="keywords" content="Physics Lab Web, 物理实验室, physics lab" />
910
<meta name="google" content="notranslate" />

src/services/advancedParser.ts

Lines changed: 98 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,58 @@ const md = new markdown({
1313
linkify: true,
1414
});
1515

16+
const SAFE_RICH_TAGS = [
17+
"a",
18+
"br",
19+
"span",
20+
"strong",
21+
"em",
22+
"code",
23+
"blockquote",
24+
"p",
25+
"pre",
26+
"hr",
27+
"ul",
28+
"ol",
29+
"li",
30+
"img",
31+
"h1",
32+
"h2",
33+
"h3",
34+
"h4",
35+
"h5",
36+
"h6",
37+
"table",
38+
"thead",
39+
"tbody",
40+
"tr",
41+
"th",
42+
"td",
43+
"sup",
44+
"sub",
45+
"del",
46+
"ins",
47+
];
48+
49+
const MAX_IMAGE_DIMENSION = 1600;
50+
51+
const SAFE_RICH_ATTRS = [
52+
"href",
53+
"internal",
54+
"target",
55+
"rel",
56+
"src",
57+
"alt",
58+
"title",
59+
"width",
60+
"height",
61+
"class",
62+
"data-user",
63+
"loading",
64+
"decoding",
65+
"referrerpolicy",
66+
];
67+
1668
// advancedParser.ts 修改以下部分
1769

1870
// 替换原有highlightjs引入
@@ -179,13 +231,41 @@ function processImageTags(html: string): string {
179231

180232
imgTags.forEach((tag) => {
181233
const src = tag.getAttribute("src");
182-
if (src && !isAllowedDomain(src)) {
234+
235+
if (!src || !isAllowedDomain(src)) {
183236
tag.remove();
184-
} else {
185-
tag.style.width = tag.getAttribute("width") || tag.style.width;
186-
tag.style.height = tag.getAttribute("height") || tag.style.height;
187-
tag.style.maxWidth = "100%";
237+
return;
188238
}
239+
240+
const width = tag.getAttribute("width");
241+
const height = tag.getAttribute("height");
242+
243+
const parsedWidth = width ? Number.parseInt(width, 10) : NaN;
244+
const parsedHeight = height ? Number.parseInt(height, 10) : NaN;
245+
246+
if (!Number.isFinite(parsedWidth) || !Number.isFinite(parsedHeight)) {
247+
tag.remove();
248+
return;
249+
}
250+
251+
if (
252+
parsedWidth <= 0 ||
253+
parsedHeight <= 0 ||
254+
parsedWidth > MAX_IMAGE_DIMENSION ||
255+
parsedHeight > MAX_IMAGE_DIMENSION
256+
) {
257+
tag.remove();
258+
return;
259+
}
260+
261+
tag.setAttribute("width", `${parsedWidth}`);
262+
tag.setAttribute("height", `${parsedHeight}`);
263+
tag.removeAttribute("style");
264+
tag.removeAttribute("srcset");
265+
tag.removeAttribute("sizes");
266+
tag.setAttribute("loading", "lazy");
267+
tag.setAttribute("decoding", "async");
268+
tag.setAttribute("referrerpolicy", "no-referrer");
189269
});
190270

191271
return doc.body.innerHTML;
@@ -265,8 +345,19 @@ function parse(text: string | string[], isInline: boolean = false) {
265345
result = processImageTags(result);
266346

267347
let clean = DOMPurify.sanitize(result, {
268-
ADD_TAGS: ["a", "br", "span", "img"], // 允许<a>>标签和<img>标签
269-
ADD_ATTR: ["href", "internal", "src", "width", "height", "maxWidht"], // 允许href和data-to属性以及img的src、width和height属性
348+
ALLOWED_TAGS: SAFE_RICH_TAGS,
349+
ALLOWED_ATTR: SAFE_RICH_ATTRS,
350+
ALLOW_DATA_ATTR: false,
351+
FORBID_TAGS: [
352+
"style",
353+
"script",
354+
"iframe",
355+
"object",
356+
"embed",
357+
"svg",
358+
"math",
359+
],
360+
FORBID_ATTR: ["style", "srcset", "sizes", "onerror", "onclick"],
270361
});
271362

272363
if (isInline) {

src/services/commonParser.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,26 @@
22

33
import DOMPurify from "dompurify";
44

5+
const SAFE_TEXT_TAGS = [
6+
"a",
7+
"br",
8+
"span",
9+
"strong",
10+
"em",
11+
"code",
12+
"blockquote",
13+
"p",
14+
];
15+
16+
const SAFE_TEXT_ATTRS = [
17+
"href",
18+
"internal",
19+
"target",
20+
"rel",
21+
"class",
22+
"data-user",
23+
];
24+
525
/**
626
* 将unity富文本解析为Html,样式写在index.html,含有Ruser
727
*
@@ -80,8 +100,11 @@ function parse(text: string | undefined, ignoreSize: boolean = false) {
80100
}
81101

82102
const clean = DOMPurify.sanitize(result, {
83-
ADD_TAGS: ["a", "br"], // 允许a标签
84-
ADD_ATTR: ["href", "internal"], // 允许href和data-to属性
103+
ALLOWED_TAGS: SAFE_TEXT_TAGS,
104+
ALLOWED_ATTR: SAFE_TEXT_ATTRS,
105+
ALLOW_DATA_ATTR: false,
106+
FORBID_TAGS: ["style", "script", "iframe", "object", "embed", "svg"],
107+
FORBID_ATTR: ["style", "srcset"],
85108
});
86109

87110
return processAnchorTags(clean);

0 commit comments

Comments
 (0)