Skip to content

Commit e72cc1a

Browse files
authored
Merge pull request #678 from ex3ndr-bot/fix/mermaid-xss-668
fix(security): prevent XSS in Mermaid WebView
2 parents a55d63e + 7cbed9c commit e72cc1a

1 file changed

Lines changed: 27 additions & 12 deletions

File tree

packages/happy-app/sources/components/markdown/MermaidRenderer.tsx

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ export const MermaidRenderer = React.memo((props: {
104104
}
105105

106106
// For iOS/Android, use WebView
107+
// Pass mermaid content via JSON to prevent XSS from HTML interpolation
108+
const mermaidContent = JSON.stringify(props.content);
107109
const html = `
108110
<!DOCTYPE html>
109111
<html>
@@ -123,25 +125,38 @@ export const MermaidRenderer = React.memo((props: {
123125
align-items: center;
124126
width: 100%;
125127
}
126-
.mermaid {
127-
text-align: center;
128-
width: 100%;
129-
}
130-
.mermaid svg {
128+
#mermaid-container svg {
131129
max-width: 100%;
132130
height: auto;
133131
}
132+
.error {
133+
color: #ff6b6b;
134+
font-family: monospace;
135+
white-space: pre-wrap;
136+
}
134137
</style>
135138
</head>
136139
<body>
137-
<div id="mermaid-container" class="mermaid">
138-
${props.content}
139-
</div>
140+
<div id="mermaid-container"></div>
140141
<script>
141-
mermaid.initialize({
142-
startOnLoad: true,
143-
theme: 'dark'
144-
});
142+
(async function() {
143+
const content = ${mermaidContent};
144+
const container = document.getElementById('mermaid-container');
145+
146+
try {
147+
mermaid.initialize({
148+
startOnLoad: false,
149+
theme: 'dark'
150+
});
151+
152+
const { svg } = await mermaid.render('mermaid-diagram', content);
153+
container.innerHTML = svg;
154+
} catch (error) {
155+
container.innerHTML = '<div class="error">Diagram error: ' +
156+
(error.message || String(error)).replace(/</g, '&lt;').replace(/>/g, '&gt;') +
157+
'</div>';
158+
}
159+
})();
145160
</script>
146161
</body>
147162
</html>

0 commit comments

Comments
 (0)