fix(extra) : preserve rich text colors
CI / Verify (push) Successful in 1m4s
CI / Build immutable images and deploy (push) Successful in 2m28s

This commit is contained in:
2026-09-05 13:28:56 +07:00 Unverified
parent 6a8a19f186
commit 5314996ef3
2 changed files with 25 additions and 3 deletions
+8
View File
@@ -15,6 +15,14 @@ describe("rich text sanitization", () => {
);
});
it("normalizes browser-generated RGB text colors to safe hex colors", () => {
expect(sanitizeRichTextHtml(
'<p><span style="color: rgb(0, 255, 0);">green</span> <span style="color: rgb(255, 204, 0)">yellow</span></p>',
)).toBe(
'<p><span style="color: #00FF00">green</span> <span style="color: #FFCC00">yellow</span></p>',
);
});
it("removes executable markup, unsafe links, and unsupported styles", () => {
expect(sanitizeRichTextHtml(
'<script>alert(1)</script><p onclick="bad()">Safe <span style="background:red;color:red">text</span><a href="javascript:bad()">link</a></p>',
+17 -3
View File
@@ -22,6 +22,18 @@ const HTML_COMMENT = /<!--[\s\S]*?-->/g;
const TAG = /<\/?([a-zA-Z][a-zA-Z0-9-]*)([^>]*)>/g;
const ATTRIBUTE = /([a-zA-Z_:][a-zA-Z0-9_:.-]*)\s*=\s*("[^"]*"|'[^']*'|[^\s"'=<>`]+)/g;
const HEX_COLOR = /^#[0-9a-fA-F]{6}$/;
const RGB_COLOR = /^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$/i;
function normalizeRichTextColor(value: string): string | null {
const color = value.trim();
if (HEX_COLOR.test(color)) return color.toUpperCase();
const rgb = color.match(RGB_COLOR);
if (!rgb) return null;
const channels = rgb.slice(1).map(Number);
if (channels.some((channel) => channel > 255)) return null;
return `#${channels.map((channel) => channel.toString(16).padStart(2, "0")).join("")}`.toUpperCase();
}
export function sanitizeRichTextUrl(value: string): string {
const url = value.trim();
@@ -75,9 +87,11 @@ export function sanitizeRichTextHtml(value: string): string {
if (name === "span") {
const style = attributeValue(rawAttributes, "style");
const color = style.match(/(?:^|;)\s*color\s*:\s*(#[0-9a-fA-F]{6})\s*(?:;|$)/)?.[1];
return color && HEX_COLOR.test(color)
? `<span style="color: ${color.toUpperCase()}">`
const color = normalizeRichTextColor(
style.match(/(?:^|;)\s*color\s*:\s*([^;]+?)\s*(?:;|$)/i)?.[1] ?? "",
);
return color
? `<span style="color: ${color}">`
: "<span>";
}