diff --git a/src/services/html_sanitizer.spec.ts b/src/services/html_sanitizer.spec.ts new file mode 100644 index 000000000..7f5888380 --- /dev/null +++ b/src/services/html_sanitizer.spec.ts @@ -0,0 +1,53 @@ +import { describe, expect, it } from "vitest"; +import html_sanitizer from "./html_sanitizer.js"; +import { trimIndentation } from "../../spec/support/utils.js"; + +describe("sanitize", () => { + it("filters out position inline CSS", () => { + const dirty = `
`; + const clean = `
`; + expect(html_sanitizer.sanitize(dirty)).toBe(clean); + }); + + it("keeps inline styles defined in CKEDitor", () => { + const dirty = trimIndentation`\ +

+ + Hi + + + + there + +

+
+ + + + + + +
+
`; + const clean = trimIndentation`\ +

+ + Hi + + + + there + +

+
+ + + + + + +
+
`; + expect(html_sanitizer.sanitize(dirty)) .toBe(clean); + }); +}); diff --git a/src/services/html_sanitizer.ts b/src/services/html_sanitizer.ts index 3acfde469..35e04c0b7 100644 --- a/src/services/html_sanitizer.ts +++ b/src/services/html_sanitizer.ts @@ -141,6 +141,9 @@ function sanitize(dirtyHtml: string) { allowedTags = DEFAULT_ALLOWED_TAGS; } + 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*\)$/]; + const sizeRegex = [/^\d+(?:px|em|%)$/]; + // to minimize document changes, compress H return sanitizeHtml(dirtyHtml, { allowedTags, @@ -148,6 +151,24 @@ function sanitize(dirtyHtml: string) { "*": ["class", "style", "title", "src", "href", "hash", "disabled", "align", "alt", "center", "data-*"], input: ["type", "checked"] }, + allowedStyles: { + "*": { + "color": colorRegex, + "background-color": colorRegex + }, + "figure": { + "float": [ /^\s*(left|right|none)\s*$/ ], + "width": sizeRegex, + "height": sizeRegex + }, + "table": { + "border-color": colorRegex, + "border-style": [ /^\s*(none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset)\s*$/ ] + }, + "td": { + "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*$/ ] + } + }, allowedSchemes: ALLOWED_PROTOCOLS, nonTextTags: ["head"], transformTags