diff --git a/lib/crewai/src/crewai/flow/visualization/assets/interactive.js b/lib/crewai/src/crewai/flow/visualization/assets/interactive.js index 10788727f..fb7658619 100644 --- a/lib/crewai/src/crewai/flow/visualization/assets/interactive.js +++ b/lib/crewai/src/crewai/flow/visualization/assets/interactive.js @@ -64,6 +64,37 @@ function highlightPython(code) { return Prism.highlight(code, Prism.languages.python, "python"); } +function escapeHtml(text) { + if (text === null || text === undefined) { + return ""; + } + const str = String(text); + const div = document.createElement("div"); + div.textContent = str; + return div.innerHTML; +} + +function sanitizeHtml(html) { + if (typeof DOMPurify !== "undefined") { + return DOMPurify.sanitize(html, { + ALLOWED_TAGS: [ + "div", "span", "pre", "code", "ul", "li", "button", "svg", "line", + "rect", "path", "polyline", "i", "label", "select", "option" + ], + ALLOWED_ATTR: [ + "class", "style", "id", "data-code", "data-node-id", "data-trigger-items", + "data-trigger-group", "data-router-paths", "data-condition-label", + "data-lucide", "viewBox", "fill", "stroke", "stroke-width", + "stroke-dasharray", "stroke-linecap", "stroke-linejoin", + "x", "y", "x1", "y1", "x2", "y2", "width", "height", "rx", + "points", "d", "onmouseover", "onmouseout" + ], + ALLOW_DATA_ATTR: true, + }); + } + return html; +} + class NodeRenderer { constructor(nodes, networkManager) { this.nodes = nodes; @@ -1428,7 +1459,7 @@ class DrawerManager { content += this.renderSourceCode(metadata); } - this.elements.content.innerHTML = content; + this.elements.content.innerHTML = sanitizeHtml(content); this.attachContentEventListeners(nodeName); // Initialize Lucide icons in the newly rendered drawer content @@ -1448,9 +1479,9 @@ class DrawerManager {