diff --git a/src/components/MermaidDiagram.test.tsx b/src/components/MermaidDiagram.test.tsx
index cb861de0..fd85d6f6 100644
--- a/src/components/MermaidDiagram.test.tsx
+++ b/src/components/MermaidDiagram.test.tsx
@@ -1,5 +1,6 @@
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
+import { RUNTIME_STYLE_NONCE } from '../lib/runtimeStyleNonce'
import { MermaidDiagram } from './MermaidDiagram'
const mermaidMock = vi.hoisted(() => ({
@@ -50,6 +51,26 @@ describe('MermaidDiagram', () => {
expect(screen.getByTestId('mermaid-diagram-dialog-viewport').querySelector('svg')).not.toBeNull()
})
+ it('tags Mermaid SVG style elements with the runtime CSP nonce', async () => {
+ mermaidMock.render.mockResolvedValueOnce({
+ svg: '',
+ })
+
+ render(
+ B'}
+ source={'```mermaid\nflowchart LR\nA --> B\n```'}
+ />,
+ )
+
+ await waitFor(() => {
+ expect(screen.getByTestId('mermaid-diagram-viewport').querySelector('style')).not.toBeNull()
+ })
+
+ const style = screen.getByTestId('mermaid-diagram-viewport').querySelector('style')
+ expect(style?.getAttribute('nonce')).toBe(RUNTIME_STYLE_NONCE)
+ })
+
it('falls back to the original source when Mermaid cannot render', async () => {
mermaidMock.render.mockRejectedValueOnce(new Error('parse error'))
diff --git a/src/components/MermaidDiagram.tsx b/src/components/MermaidDiagram.tsx
index 6ca39809..0f76bc22 100644
--- a/src/components/MermaidDiagram.tsx
+++ b/src/components/MermaidDiagram.tsx
@@ -8,6 +8,7 @@ import {
DialogTitle,
DialogTrigger,
} from '@/components/ui/dialog'
+import { RUNTIME_STYLE_NONCE } from '@/lib/runtimeStyleNonce'
type MermaidApi = typeof import('mermaid')['default']
@@ -51,6 +52,18 @@ function initializeMermaid(mermaid: MermaidApi) {
initialized = true
}
+function withRuntimeStyleNonce(svg: string): string {
+ if (!svg.includes('