"use client"; /** * Single MermaidDiagram for the unified markdown engine (dark theme only — * the old RichMarkdownRenderer's light-theme branch was dead code behind a * hardcoded `isDarkMode = true` and is deleted, not carried over). * * `mermaid` stays a dynamic import so neither chat nor content bundles pay * for it unless a diagram is actually rendered. */ import React, { useEffect, useState } from 'react'; import type { MermaidConfig } from 'mermaid'; import { AlertCircleIcon } from '../../icons-v2-generated'; /** * SECURITY SSOT for the mermaid renderer — the ONLY place these knobs are * written. `./__tests__/mermaid-security.test.ts` imports THIS constant and * spreads it into its own `mermaid.initialize`, so the fixture and the * component can no longer drift: flipping `securityLevel` to `'loose'` here * fails the suite (verified by doing exactly that). * * This renderer sits on the CHAT path, so the diagram source is MODEL output * — untrusted by construction — and the rendered SVG goes through * `dangerouslySetInnerHTML`. The pre-unification renderer used `'loose'`, * which permits raw HTML inside labels and enables mermaid's `click` * interaction directive: a node label like `A[""]` * would have produced LIVE HTML. * * - `securityLevel: 'strict'` encodes HTML tags in text and disables click * handlers. * - `htmlLabels: false` renders labels as SVG `` instead of a * foreignObject HTML subtree, so there is no HTML surface at all. Verified: * no authored diagram in this repo or in the consuming hub's markdown uses * HTML labels (not even `
`), so 'antiscript' (which still allows tags) * is not needed. (`htmlLabels` is set at the ROOT — `flowchart.htmlLabels` * is deprecated in mermaid 11 and the root value takes precedence.) * - `secure` is the allowlist of config keys a `%%{init}%%` directive in the * diagram SOURCE may NOT override. mermaid's default list covers * `securityLevel` but NOT `htmlLabels`, so model-authored source could * otherwise re-enable HTML labels while `securityLevel` stayed locked. * Adding `htmlLabels` (plus `secure` itself and the resource limits) closes * that hole. * * jsdom NOTE: with `htmlLabels` unlocked, a `%%{init: {"htmlLabels": true}}%%` * directive was observed to make `mermaid.render` never settle under jsdom * (>60s, against a passing two-render control). This was NOT reproduced in a * real browser and may well be an artifact of jsdom having no layout — do not * read it as a confirmed browser DoS. Either way the render below is wrapped * in a timeout so a non-settling render surfaces the error state instead of * sitting on "Rendering diagram…" forever. */ export const MERMAID_SECURITY_OPTIONS = { htmlLabels: false, securityLevel: 'strict', secure: [ 'securityLevel', 'htmlLabels', 'secure', 'startOnLoad', 'maxTextSize', 'maxEdges', // `themeCSS` is RAW CSS that mermaid emits into a `