mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-09 10:36:43 +02:00
fix: sanitize HTML for spans
This commit is contained in:
@@ -2,9 +2,8 @@
|
|||||||
// @ts-nocheck TODO: Fix types
|
// @ts-nocheck TODO: Fix types
|
||||||
import { select } from 'd3';
|
import { select } from 'd3';
|
||||||
import type { MermaidConfig } from '../config.type.js';
|
import type { MermaidConfig } from '../config.type.js';
|
||||||
import { getConfig, sanitizeText } from '../diagram-api/diagramAPI.js';
|
|
||||||
import type { SVGGroup } from '../diagram-api/types.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 type { D3TSpanElement, D3TextElement } from '../diagrams/common/commonTypes.js';
|
||||||
import { log } from '../logger.js';
|
import { log } from '../logger.js';
|
||||||
import { markdownToHTML, markdownToLines } from '../rendering-util/handle-markdown-text.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');
|
const fo = element.append('foreignObject');
|
||||||
// This is not the final width but used in order to make sure the foreign
|
// 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
|
// 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`);
|
fo.attr('height', `${10 * width}px`);
|
||||||
|
|
||||||
const div = fo.append('xhtml:div');
|
const div = fo.append('xhtml:div');
|
||||||
let label = node.label;
|
const sanitizedLabel = hasKatex(label)
|
||||||
if (node.label && hasKatex(node.label)) {
|
? await renderKatexSanitized(node.label.replace(common.lineBreakRegex, '\n'), config)
|
||||||
label = await renderKatexSanitized(
|
: sanitizeText(label, config);
|
||||||
node.label.replace(common.lineBreakRegex, '\n'),
|
|
||||||
getConfig()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const labelClass = node.isNode ? 'nodeLabel' : 'edgeLabel';
|
const labelClass = node.isNode ? 'nodeLabel' : 'edgeLabel';
|
||||||
const span = div.append('span');
|
const span = div.append('span');
|
||||||
span.html(label);
|
span.html(sanitizedLabel);
|
||||||
applyStyle(span, node.labelStyle);
|
applyStyle(span, node.labelStyle);
|
||||||
span.attr('class', `${labelClass} ${classes}`);
|
span.attr('class', `${labelClass} ${classes}`);
|
||||||
|
|
||||||
@@ -59,9 +62,6 @@ async function addHtmlSpan(element, node, width, classes, addBackground = false)
|
|||||||
bbox = div.node().getBoundingClientRect();
|
bbox = div.node().getBoundingClientRect();
|
||||||
}
|
}
|
||||||
|
|
||||||
// fo.style('width', bbox.width);
|
|
||||||
// fo.style('height', bbox.height);
|
|
||||||
|
|
||||||
return fo.node();
|
return fo.node();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,9 +184,14 @@ function updateTextContentAndStyles(tspan: any, wrappedLine: MarkdownWord[]) {
|
|||||||
/**
|
/**
|
||||||
* Convert fontawesome labels into fontawesome icons by using a regex pattern
|
* Convert fontawesome labels into fontawesome icons by using a regex pattern
|
||||||
* @param text - The raw string to convert
|
* @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
|
* @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<string> {
|
||||||
const pendingReplacements: Promise<string>[] = [];
|
const pendingReplacements: Promise<string>[] = [];
|
||||||
// cspell: disable-next-line
|
// cspell: disable-next-line
|
||||||
text.replace(/(fa[bklrs]?):fa-([\w-]+)/g, (fullMatch, prefix, iconName) => {
|
text.replace(/(fa[bklrs]?):fa-([\w-]+)/g, (fullMatch, prefix, iconName) => {
|
||||||
@@ -196,7 +201,7 @@ export async function replaceIconSubstring(text: string) {
|
|||||||
if (await isIconAvailable(registeredIconName)) {
|
if (await isIconAvailable(registeredIconName)) {
|
||||||
return await getIconSVG(registeredIconName, undefined, { class: 'label-icon' });
|
return await getIconSVG(registeredIconName, undefined, { class: 'label-icon' });
|
||||||
} else {
|
} else {
|
||||||
return `<i class='${sanitizeText(fullMatch).replace(':', ' ')}'></i>`;
|
return `<i class='${sanitizeText(fullMatch, config).replace(':', ' ')}'></i>`;
|
||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
);
|
);
|
||||||
@@ -239,7 +244,7 @@ export const createText = async (
|
|||||||
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
|
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
|
||||||
|
|
||||||
const htmlText = markdownToHTML(text, config);
|
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
|
//for Katex the text could contain escaped characters, \\relax that should be transformed to \relax
|
||||||
const inputForKatex = text.replace(/\\\\/g, '\\');
|
const inputForKatex = text.replace(/\\\\/g, '\\');
|
||||||
@@ -249,7 +254,7 @@ export const createText = async (
|
|||||||
label: hasKatex(text) ? inputForKatex : decodedReplacedText,
|
label: hasKatex(text) ? inputForKatex : decodedReplacedText,
|
||||||
labelStyle: style.replace('fill:', 'color:'),
|
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;
|
return vertexNode;
|
||||||
} else {
|
} else {
|
||||||
//sometimes the user might add br tags with 1 or more spaces in between, so we need to replace them with <br/>
|
//sometimes the user might add br tags with 1 or more spaces in between, so we need to replace them with <br/>
|
||||||
|
Reference in New Issue
Block a user