mirror of
https://github.com/alibaba/open-code-review.git
synced 2026-07-09 17:28:58 +00:00
fix(pages): address CodeQL XSS alerts in markdown rendering (#300)
* fix(pages): address CodeQL XSS alerts in markdown rendering Alert #4 (headingId.ts): replace the unreliable single-pass regex used to strip HTML tags (incomplete multi-character sanitization) with DOMPurify. This also fixes a pre-existing mismatch where headings containing HTML entities produced different anchor ids on the TOC vs renderer sides. Alert #5 (MarkdownRenderer.tsx): mermaid runs with securityLevel:'strict' and already sanitizes its own SVG output, so re-running DOMPurify over the whole SVG broke rendering (namespaces, inline <style>, foreignObject labels). Make securityLevel explicit and inject mermaid's trusted output directly, annotated with a codeql suppression comment. * docs(pages): clarify CodeQL XSS suppression justification in MarkdownRenderer The suppression comment claimed the mermaid SVG is 'not raw user input', which understates the trust boundary. The SVG is in fact derived from user-controlled mermaid code; safety relies on mermaid's securityLevel: 'strict' sanitizing the output via DOMPurify. Update the comment to state this accurately and flag that the boundary depends on that setting.
This commit is contained in:
parent
10f587594b
commit
9dfcffda07
2 changed files with 20 additions and 3 deletions
|
|
@ -10,6 +10,10 @@ import { generateHeadingId } from '../utils/headingId';
|
|||
// Initialize mermaid with dark theme
|
||||
mermaid.initialize({
|
||||
startOnLoad: false,
|
||||
// 'strict' makes mermaid sanitize its own SVG output (DOMPurify internally):
|
||||
// safe label HTML like <b>/<span> is kept, scripts/handlers are stripped.
|
||||
// This is why we can inject the returned SVG directly below without re-sanitizing.
|
||||
securityLevel: 'strict',
|
||||
theme: 'dark',
|
||||
themeVariables: {
|
||||
primaryColor: '#1a1a2e',
|
||||
|
|
@ -131,9 +135,16 @@ const MarkdownRenderer: React.FC<MarkdownRendererProps> = ({ content }) => {
|
|||
const id = `mermaid-diagram-${crypto.randomUUID()}`;
|
||||
const { svg } = await mermaid.render(id, code);
|
||||
if (cancelled) return;
|
||||
// Replace the <pre> with rendered SVG
|
||||
// Replace the <pre> with rendered SVG. The SVG is produced by mermaid with
|
||||
// securityLevel:'strict' (see initialize above), which already sanitizes its
|
||||
// output. Re-running DOMPurify over the whole SVG breaks it (namespaces,
|
||||
// inline <style>, foreignObject labels), so we inject mermaid's trusted
|
||||
// output directly.
|
||||
const wrapper = document.createElement('div');
|
||||
wrapper.className = 'mermaid-rendered';
|
||||
// codeql[js/xss-through-dom] -- svg is derived from user-controlled mermaid code, but mermaid
|
||||
// renders it with securityLevel:'strict' (see initialize above), which sanitizes the output via
|
||||
// DOMPurify (scripts/handlers stripped). The trust boundary relies on that setting staying 'strict'.
|
||||
wrapper.innerHTML = svg;
|
||||
pre.replaceWith(wrapper);
|
||||
} catch (e) {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,16 @@
|
|||
import DOMPurify from 'dompurify';
|
||||
|
||||
/**
|
||||
* Shared utility to generate heading IDs from text.
|
||||
* Used by both extractHeadings (DocsPage TOC) and MarkdownRenderer (heading renderer)
|
||||
* to ensure consistent anchor IDs.
|
||||
*/
|
||||
export function generateHeadingId(text: string): string {
|
||||
// Strip HTML tags first (from marked output), then strip markdown formatting chars
|
||||
const plain = text.replace(/<[^>]+>/g, '').replace(/[`*_\[\]()]/g, '').trim();
|
||||
// Strip HTML tags via DOMPurify (a single-pass regex is unreliable and can be
|
||||
// bypassed by nested tags). Keeps text content, decodes HTML entities so the
|
||||
// TOC side (raw markdown) and renderer side (marked HTML output) agree.
|
||||
const plain = DOMPurify.sanitize(text, { ALLOWED_TAGS: [], ALLOWED_ATTR: [] })
|
||||
.replace(/[`*_\[\]()]/g, '')
|
||||
.trim();
|
||||
return plain.toLowerCase().replace(/[^a-z0-9\u4e00-\u9fff]+/g, '-').replace(/^-|-$/g, '');
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue