forked from github/cinny
Allow using filenames in codeblocks - If there is a dot in the language name, we instead treat the first line after ``` as the filename and everything after the last dot as the language - we use a custom "data-label" attribute on the code block to specify the name of the file (so only compatible with cinny from this point onwards)
169 lines
3.4 KiB
TypeScript
169 lines
3.4 KiB
TypeScript
import sanitizeHtml, { Transformer } from 'sanitize-html';
|
|
|
|
const MAX_TAG_NESTING = 100;
|
|
|
|
const permittedHtmlTags = [
|
|
'font',
|
|
'del',
|
|
'h1',
|
|
'h2',
|
|
'h3',
|
|
'h4',
|
|
'h5',
|
|
'h6',
|
|
'blockquote',
|
|
'p',
|
|
'a',
|
|
'ul',
|
|
'ol',
|
|
'sup',
|
|
'sub',
|
|
'li',
|
|
'b',
|
|
'i',
|
|
'u',
|
|
'strong',
|
|
'em',
|
|
'strike',
|
|
's',
|
|
'code',
|
|
'hr',
|
|
'br',
|
|
'div',
|
|
'table',
|
|
'thead',
|
|
'tbody',
|
|
'tr',
|
|
'th',
|
|
'td',
|
|
'caption',
|
|
'pre',
|
|
'span',
|
|
'img',
|
|
'details',
|
|
'summary',
|
|
];
|
|
|
|
const urlSchemes = ['https', 'http', 'ftp', 'mailto', 'magnet'];
|
|
|
|
const permittedTagToAttributes = {
|
|
font: ['style', 'data-mx-bg-color', 'data-mx-color', 'color'],
|
|
span: [
|
|
'style',
|
|
'data-mx-bg-color',
|
|
'data-mx-color',
|
|
'data-mx-spoiler',
|
|
'data-mx-maths',
|
|
'data-mx-pill',
|
|
'data-mx-ping',
|
|
'data-md',
|
|
],
|
|
div: ['data-mx-maths'],
|
|
blockquote: ['data-md'],
|
|
h1: ['data-md'],
|
|
h2: ['data-md'],
|
|
h3: ['data-md'],
|
|
h4: ['data-md'],
|
|
h5: ['data-md'],
|
|
h6: ['data-md'],
|
|
pre: ['data-md', 'class'],
|
|
ol: ['start', 'type', 'data-md'],
|
|
ul: ['data-md'],
|
|
a: ['name', 'target', 'href', 'rel', 'data-md'],
|
|
img: ['width', 'height', 'alt', 'title', 'src', 'data-mx-emoticon'],
|
|
code: ['class', 'data-md', 'data-label'],
|
|
strong: ['data-md'],
|
|
i: ['data-md'],
|
|
em: ['data-md'],
|
|
u: ['data-md'],
|
|
s: ['data-md'],
|
|
del: ['data-md'],
|
|
};
|
|
|
|
const transformFontTag: Transformer = (tagName, attribs) => ({
|
|
tagName,
|
|
attribs: {
|
|
...attribs,
|
|
style: `background-color: ${attribs['data-mx-bg-color']}; color: ${attribs['data-mx-color']}`,
|
|
},
|
|
});
|
|
|
|
const transformSpanTag: Transformer = (tagName, attribs) => ({
|
|
tagName,
|
|
attribs: {
|
|
...attribs,
|
|
style: `background-color: ${attribs['data-mx-bg-color']}; color: ${attribs['data-mx-color']}`,
|
|
},
|
|
});
|
|
|
|
const transformATag: Transformer = (tagName, attribs) => ({
|
|
tagName,
|
|
attribs: {
|
|
...attribs,
|
|
rel: 'noreferrer noopener',
|
|
target: '_blank',
|
|
},
|
|
});
|
|
|
|
const transformImgTag: Transformer = (tagName, attribs) => {
|
|
const { src } = attribs;
|
|
if (typeof src === 'string' && src.startsWith('mxc://') === false) {
|
|
return {
|
|
tagName: 'a',
|
|
attribs: {
|
|
href: src,
|
|
rel: 'noreferrer noopener',
|
|
target: '_blank',
|
|
},
|
|
text: attribs.alt || src,
|
|
};
|
|
}
|
|
return {
|
|
tagName,
|
|
attribs: {
|
|
...attribs,
|
|
},
|
|
};
|
|
};
|
|
|
|
export const sanitizeCustomHtml = (customHtml: string): string =>
|
|
sanitizeHtml(customHtml, {
|
|
allowedTags: permittedHtmlTags,
|
|
allowedAttributes: permittedTagToAttributes,
|
|
disallowedTagsMode: 'discard',
|
|
allowedSchemes: urlSchemes,
|
|
allowedSchemesByTag: {
|
|
a: urlSchemes,
|
|
},
|
|
allowedSchemesAppliedToAttributes: ['href'],
|
|
allowProtocolRelative: false,
|
|
allowedClasses: {
|
|
code: ['language-*'],
|
|
},
|
|
allowedStyles: {
|
|
'*': {
|
|
color: [/^#(?:[0-9a-fA-F]{3}){1,2}$/],
|
|
'background-color': [/^#(?:[0-9a-fA-F]{3}){1,2}$/],
|
|
},
|
|
},
|
|
transformTags: {
|
|
font: transformFontTag,
|
|
span: transformSpanTag,
|
|
a: transformATag,
|
|
img: transformImgTag,
|
|
},
|
|
nonTextTags: ['style', 'script', 'textarea', 'option', 'noscript', 'mx-reply'],
|
|
nestingLimit: MAX_TAG_NESTING,
|
|
});
|
|
|
|
export const sanitizeText = (body: string) => {
|
|
const tagsToReplace: Record<string, string> = {
|
|
'&': '&',
|
|
'<': '<',
|
|
'>': '>',
|
|
'"': '"',
|
|
"'": ''',
|
|
};
|
|
return body.replace(/[&<>'"]/g, (tag) => tagsToReplace[tag] || tag);
|
|
};
|