1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
|
import sanitizeHtml from 'sanitize-html';
import type { MarkdownRenderer } from 'vitepress';
import { createMarkdownRenderer } from 'vitepress';
import vitepressConfig from '../../docs/.vitepress/config';
import { adjustUrls, pathOutputDir } from './utils';
let markdown: MarkdownRenderer;
export async function initMarkdownRenderer(): Promise<void> {
markdown = await createMarkdownRenderer(
pathOutputDir,
vitepressConfig.markdown,
'/'
);
}
const htmlSanitizeOptions: sanitizeHtml.IOptions = {
allowedTags: [
'a',
'button',
'code',
'div',
'li',
'p',
'pre',
'span',
'strong',
'ul',
],
allowedAttributes: {
a: ['href', 'target', 'rel'],
button: ['class', 'title'],
div: ['class'],
pre: ['class', 'tabindex', 'v-pre'],
span: ['class', 'style'],
},
selfClosing: [],
};
function comparableSanitizedHtml(html: string): string {
return html
.replace(/>/g, '>')
.replace(/ /g, '')
.replace(/"/g, '"')
.replace(/'/g, "'");
}
/**
* Converts Markdown to an HTML string and sanitizes it.
* @param md The markdown to convert.
* @param inline Whether to render the markdown as inline, without a wrapping `<p>` tag. Defaults to `false`.
* @returns The converted HTML string.
*/
export function mdToHtml(md: string, inline: boolean = false): string {
const rawHtml = inline ? markdown.renderInline(md) : markdown.render(md);
const safeHtml: string = sanitizeHtml(rawHtml, htmlSanitizeOptions);
// Revert some escaped characters for comparison.
if (comparableSanitizedHtml(rawHtml) === comparableSanitizedHtml(safeHtml)) {
return adjustUrls(safeHtml);
}
console.debug('Rejected unsafe md:', md);
console.error('Rejected unsafe html:', rawHtml);
console.error('Rejected unsafe html:', comparableSanitizedHtml(rawHtml));
console.error('Expected safe html:', comparableSanitizedHtml(safeHtml));
throw new Error('Found unsafe html');
}
|