Skip to content

Commit ef4fc0a

Browse files
committedFeb 22, 2025
feat(html_sanitizer): enforce strict style rules (closes #931)
1 parent 1d47df5 commit ef4fc0a

File tree

2 files changed

+74
-0
lines changed

2 files changed

+74
-0
lines changed
 

‎src/services/html_sanitizer.spec.ts

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { describe, expect, it } from "vitest";
2+
import html_sanitizer from "./html_sanitizer.js";
3+
import { trimIndentation } from "../../spec/support/utils.js";
4+
5+
describe("sanitize", () => {
6+
it("filters out position inline CSS", () => {
7+
const dirty = `<div style="z-index:999999999;margin:0px;left:250px;height:100px;display:table;background:none;position:fixed;top:250px;"></div>`;
8+
const clean = `<div></div>`;
9+
expect(html_sanitizer.sanitize(dirty)).toBe(clean);
10+
});
11+
12+
it("keeps inline styles defined in CKEDitor", () => {
13+
const dirty = trimIndentation`\
14+
<p>
15+
<span style="color:hsl(0, 0%, 90%);">
16+
Hi
17+
</span>
18+
19+
<span style="background-color:hsl(30, 75%, 60%);">
20+
there
21+
</span>
22+
</p>
23+
<figure class="table" style="float:left;height:800px;width:600px;">
24+
<table style="background-color:hsl(0, 0%, 90%);border-color:hsl(0, 0%, 0%);border-style:dotted;">
25+
<tbody>
26+
<tr>
27+
<td style="border:2px groove hsl(60, 75%, 60%);"></td>
28+
</tr>
29+
</tbody>
30+
</table>
31+
</figure>`;
32+
const clean = trimIndentation`\
33+
<p>
34+
<span style="color:hsl(0, 0%, 90%)">
35+
Hi
36+
</span>
37+
38+
<span style="background-color:hsl(30, 75%, 60%)">
39+
there
40+
</span>
41+
</p>
42+
<figure class="table" style="float:left;height:800px;width:600px">
43+
<table style="background-color:hsl(0, 0%, 90%);border-color:hsl(0, 0%, 0%);border-style:dotted">
44+
<tbody>
45+
<tr>
46+
<td style="border:2px groove hsl(60, 75%, 60%)"></td>
47+
</tr>
48+
</tbody>
49+
</table>
50+
</figure>`;
51+
expect(html_sanitizer.sanitize(dirty)) .toBe(clean);
52+
});
53+
});

‎src/services/html_sanitizer.ts

+21
Original file line numberDiff line numberDiff line change
@@ -141,13 +141,34 @@ function sanitize(dirtyHtml: string) {
141141
allowedTags = DEFAULT_ALLOWED_TAGS;
142142
}
143143

144+
const colorRegex = [/^#(0x)?[0-9a-f]+$/i, /^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$/, /^hsl\(\s*(\d{1,3})\s*,\s*(\d{1,3})%\s*,\s*(\d{1,3})%\s*\)$/];
145+
const sizeRegex = [/^\d+(?:px|em|%)$/];
146+
144147
// to minimize document changes, compress H
145148
return sanitizeHtml(dirtyHtml, {
146149
allowedTags,
147150
allowedAttributes: {
148151
"*": ["class", "style", "title", "src", "href", "hash", "disabled", "align", "alt", "center", "data-*"],
149152
input: ["type", "checked"]
150153
},
154+
allowedStyles: {
155+
"*": {
156+
"color": colorRegex,
157+
"background-color": colorRegex
158+
},
159+
"figure": {
160+
"float": [ /^\s*(left|right|none)\s*$/ ],
161+
"width": sizeRegex,
162+
"height": sizeRegex
163+
},
164+
"table": {
165+
"border-color": colorRegex,
166+
"border-style": [ /^\s*(none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset)\s*$/ ]
167+
},
168+
"td": {
169+
"border": [ /^\s*\d+(?:px|em|%)\s*(none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset)\s*(#(0x)?[0-9a-fA-F]+|rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)|hsl\(\s*(\d{1,3})\s*,\s*(\d{1,3})%\s*,\s*(\d{1,3})%\))\s*$/ ]
170+
}
171+
},
151172
allowedSchemes: ALLOWED_PROTOCOLS,
152173
nonTextTags: ["head"],
153174
transformTags

0 commit comments

Comments
 (0)