From cfc76ef1cb7eb31c3c1b3b1a3aec30b4f4433d74 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Thu, 7 Aug 2025 22:49:39 +0530 Subject: [PATCH] fix: sanitize HTML for spans --- .../mermaid/src/rendering-util/createText.ts | 41 +++++++++++-------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/packages/mermaid/src/rendering-util/createText.ts b/packages/mermaid/src/rendering-util/createText.ts index 65129aa8a..9b9c9d574 100644 --- a/packages/mermaid/src/rendering-util/createText.ts +++ b/packages/mermaid/src/rendering-util/createText.ts @@ -2,9 +2,8 @@ // @ts-nocheck TODO: Fix types import { select } from 'd3'; import type { MermaidConfig } from '../config.type.js'; -import { getConfig, sanitizeText } from '../diagram-api/diagramAPI.js'; import type { SVGGroup } from '../diagram-api/types.js'; -import common, { hasKatex, renderKatexSanitized } from '../diagrams/common/common.js'; +import common, { hasKatex, renderKatexSanitized, sanitizeText } from '../diagrams/common/common.js'; import type { D3TSpanElement, D3TextElement } from '../diagrams/common/commonTypes.js'; import { log } from '../logger.js'; import { markdownToHTML, markdownToLines } from '../rendering-util/handle-markdown-text.js'; @@ -19,7 +18,15 @@ function applyStyle(dom, styleFn) { } } -async function addHtmlSpan(element, node, width, classes, addBackground = false) { +async function addHtmlSpan( + element, + node, + width, + classes, + addBackground = false, + // TODO: Make config mandatory + config: MermaidConfig = {} +) { const fo = element.append('foreignObject'); // This is not the final width but used in order to make sure the foreign // object in firefox gets a width at all. The final width is fetched from the div @@ -27,16 +34,12 @@ async function addHtmlSpan(element, node, width, classes, addBackground = false) fo.attr('height', `${10 * width}px`); const div = fo.append('xhtml:div'); - let label = node.label; - if (node.label && hasKatex(node.label)) { - label = await renderKatexSanitized( - node.label.replace(common.lineBreakRegex, '\n'), - getConfig() - ); - } + const sanitizedLabel = hasKatex(label) + ? await renderKatexSanitized(node.label.replace(common.lineBreakRegex, '\n'), config) + : sanitizeText(label, config); const labelClass = node.isNode ? 'nodeLabel' : 'edgeLabel'; const span = div.append('span'); - span.html(label); + span.html(sanitizedLabel); applyStyle(span, node.labelStyle); span.attr('class', `${labelClass} ${classes}`); @@ -59,9 +62,6 @@ async function addHtmlSpan(element, node, width, classes, addBackground = false) bbox = div.node().getBoundingClientRect(); } - // fo.style('width', bbox.width); - // fo.style('height', bbox.height); - return fo.node(); } @@ -184,9 +184,14 @@ function updateTextContentAndStyles(tspan: any, wrappedLine: MarkdownWord[]) { /** * Convert fontawesome labels into fontawesome icons by using a regex pattern * @param text - The raw string to convert + * @param config - Mermaid config * @returns string with fontawesome icons as svg if the icon is registered otherwise as i tags */ -export async function replaceIconSubstring(text: string) { +export async function replaceIconSubstring( + text: string, + // TODO: Make config mandatory + config: MermaidConfig = {} +): Promise { const pendingReplacements: Promise[] = []; // cspell: disable-next-line text.replace(/(fa[bklrs]?):fa-([\w-]+)/g, (fullMatch, prefix, iconName) => { @@ -196,7 +201,7 @@ export async function replaceIconSubstring(text: string) { if (await isIconAvailable(registeredIconName)) { return await getIconSVG(registeredIconName, undefined, { class: 'label-icon' }); } else { - return ``; + return ``; } })() ); @@ -239,7 +244,7 @@ export const createText = async ( // TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that? const htmlText = markdownToHTML(text, config); - const decodedReplacedText = await replaceIconSubstring(decodeEntities(htmlText)); + const decodedReplacedText = await replaceIconSubstring(decodeEntities(htmlText), config); //for Katex the text could contain escaped characters, \\relax that should be transformed to \relax const inputForKatex = text.replace(/\\\\/g, '\\'); @@ -249,7 +254,7 @@ export const createText = async ( label: hasKatex(text) ? inputForKatex : decodedReplacedText, labelStyle: style.replace('fill:', 'color:'), }; - const vertexNode = await addHtmlSpan(el, node, width, classes, addSvgBackground); + const vertexNode = await addHtmlSpan(el, node, width, classes, addSvgBackground, config); return vertexNode; } else { //sometimes the user might add br tags with 1 or more spaces in between, so we need to replace them with