server/utils/sanitization.ts

72 lines
1.9 KiB
TypeScript
Raw Permalink Normal View History

import { stringifyEntitiesLight } from "stringify-entities";
import xss, { type IFilterXSSOptions } from "xss";
2024-05-03 02:44:49 +02:00
export const sanitizedHtmlStrip = (html: string) => {
return sanitizeHtml(html, {
whiteList: {},
});
};
2023-10-17 00:03:29 +02:00
export const sanitizeHtml = async (
html: string,
extraConfig?: IFilterXSSOptions,
) => {
const sanitizedHtml = xss(html, {
whiteList: {
a: ["href", "title", "target", "rel", "class"],
p: ["class"],
br: ["class"],
b: ["class"],
i: ["class"],
em: ["class"],
strong: ["class"],
del: ["class"],
code: ["class"],
u: ["class"],
pre: ["class"],
ul: ["class"],
ol: ["class"],
li: ["class"],
blockquote: ["class"],
2024-04-07 07:30:49 +02:00
},
stripIgnoreTag: false,
escapeHtml: (unsafeHtml) =>
stringifyEntitiesLight(unsafeHtml, {
escapeOnly: true,
}),
...extraConfig,
});
2023-10-17 00:03:29 +02:00
2024-04-07 07:30:49 +02:00
// Check text to only allow h-*, p-*, u-*, dt-*, e-*, mention, hashtag, ellipsis, invisible classes
const allowedClasses = [
"h-",
"p-",
"u-",
"dt-",
"e-",
"mention",
"hashtag",
"ellipsis",
"invisible",
];
2023-10-17 00:03:29 +02:00
2024-04-07 07:30:49 +02:00
return await new HTMLRewriter()
.on("*[class]", {
element(element) {
const classes = element.getAttribute("class")?.split(" ") ?? [];
2023-10-17 00:03:29 +02:00
2024-04-07 07:30:49 +02:00
for (const className of classes) {
if (
!allowedClasses.some((allowedClass) =>
className.startsWith(allowedClass),
)
) {
element.removeAttribute("class");
}
}
},
})
.transform(new Response(sanitizedHtml))
.text();
2023-10-17 00:03:29 +02:00
};