import { createCanvas, registerFont, CanvasRenderingContext2D } from "canvas"; import DOMPurify from "dompurify"; import { useRestApi } from "#imports"; const rest = useRestApi().value; const allowedTags = ["h1", "h2", "h3", "h4", "h5", "h6", "em", "strong", "del", "a", "img", "ul", "ol", "li", "hr", "p"]; const allowedImageExtensions = ["png", "PNG", "jpg", "JPG", "jpeg", "JPEG", "gif", "GIF"]; export async function sanitize (html: string) { const safeHtml = remove_harmful_code(html); const htmlWithNoUserTracking = await remove_user_tracking(safeHtml); return htmlWithNoUserTracking; } function remove_harmful_code (html: string) { return DOMPurify.sanitize(html, { ALLOWED_TAGS: allowedTags }); } async function remove_user_tracking (html: string) { // Parse the description as HTML to easily manipulate it. const parser = new DOMParser(); const htmlDoc = parser.parseFromString(html, "text/html"); remove_all_external_links(htmlDoc); await replace_images_with_proxied_images(htmlDoc); return document_to_html(htmlDoc); } function remove_all_external_links (htmlDoc: Document) { const links = htmlDoc.querySelectorAll("a"); links.forEach((link) => { const href = link.getAttribute("href"); if (href && !href.startsWith("#")) { link.removeAttribute("href"); } }); } async function replace_images_with_proxied_images (htmlDoc: Document) { const images = htmlDoc.querySelectorAll("img"); for (let i = 0; i < images.length; i++) { const img = images[i]; const src = img.getAttribute("src"); if (src) { if (isAllowedImage(src)) { try { const imageDataSrc = await getImageDataUrl(src); img.setAttribute("src", imageDataSrc); } catch (e) { const imageDataUrl = createImageWithText(`Can't load proxied image: ${src}`, 1000, 50, 15); img.setAttribute("src", imageDataUrl); } } else { const imageDataUrl = createImageWithText(`Not allowed image extension. It must be: ${allowedImageExtensions.concat()}`, 1000, 50, 15); img.setAttribute("src", imageDataUrl); } } } } function document_to_html (descriptionHtml: Document) { const body = descriptionHtml.querySelector("body"); const serializer = new XMLSerializer(); let html = ""; if (body) { html = serializer.serializeToString(body); html = html .replace("
", "") .replace("", "") .replace("", ""); } return html; } // Returns true if the image is allowed to be displayed. function isAllowedImage (href: string): boolean { const extension = href.split(".").pop().trim(); return allowedImageExtensions.includes(extension); } // Returns a base64 string ready to be use in a "src" attribute in a "img" html tag, // like this `