Add dom purify (#1609)

Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
Tilman Vatteroth 2021-11-02 08:15:33 +01:00 committed by GitHub
parent 994d22eb35
commit 84ee1d9cd9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 103 additions and 81 deletions

View file

@ -0,0 +1,25 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { Document } from 'domhandler'
import render from 'dom-serializer'
import DOMPurify from 'dompurify'
import { parseDocument } from 'htmlparser2'
const customTags = ['app-linemarker', 'app-katex', 'app-gist', 'app-youtube', 'app-vimeo', 'app-asciinema']
/**
* Sanitizes the given {@link Document document}.
*
* @param document The dirty document
* @return the sanitized Document
*/
export const domPurifierNodePreprocessor = (document: Document): Document => {
const sanitizedHtml = DOMPurify.sanitize(render(document), {
ADD_TAGS: customTags
})
return parseDocument(sanitizedHtml)
}

View file

@ -11,6 +11,7 @@ import convertHtmlToReact from '@hedgedoc/html-to-react'
import type { Document } from 'domhandler'
import { NodeToReactTransformer } from '../utils/node-to-react-transformer'
import { LineIdMapper } from '../utils/line-id-mapper'
import { domPurifierNodePreprocessor } from './dom-purifier-node-preprocessor'
/**
* Renders markdown code into react elements
@ -40,9 +41,13 @@ export const useConvertMarkdownToReactDom = (
return useMemo(() => {
const html = markdownIt.render(markdownCode)
return convertHtmlToReact(html, {
transform: (node, index) => htmlToReactTransformer.translateNodeToReactElement(node, index),
preprocessNodes: preprocessNodes
preprocessNodes: (document: Document): Document => {
const processedDocument = preprocessNodes ? preprocessNodes(document) : document
return domPurifierNodePreprocessor(processedDocument)
}
})
}, [htmlToReactTransformer, markdownCode, markdownIt, preprocessNodes])
}

View file

@ -9,7 +9,7 @@ import { Logger } from '../../utils/logger'
const log = new Logger('reveal.js > Comment Node Preprocessor')
const revealCommandSyntax = /^\s*\.(\w*):(.*)$/g
const dataAttributesSyntax = /\s*([\w-]*)=(?:"((?:[^"\\]|\\"|\\)*)"|'([^']*)')/g
const dataAttributesSyntax = /\s*(data-[\w-]*|class)=(?:"((?:[^"\\]|\\"|\\)*)"|'([^']*)')/g
/**
* Travels through the given {@link Document}, searches for reveal command comments and applies them.

View file

@ -14,7 +14,7 @@ export const legacySlideshareShortCode: MarkdownIt.PluginSimple = (markdownIt) =
name: 'legacy-slideshare-short-code',
regex: finalRegex,
replace: (match: string) => {
return `<a target="_blank" rel="noopener noreferrer" href="https://www.slideshare.net/${match}">https://www.slideshare.net/${match}</a>`
return `<a href="https://www.slideshare.net/${match}">https://www.slideshare.net/${match}</a>`
}
})
}

View file

@ -9,7 +9,7 @@ import { isTag } from 'domhandler'
import type MarkdownIt from 'markdown-it'
import mathJax from 'markdown-it-mathjax'
import React from 'react'
import { ComponentReplacer } from '../component-replacer'
import { ComponentReplacer, DO_NOT_REPLACE } from '../component-replacer'
import './katex.scss'
/**
@ -18,23 +18,24 @@ import './katex.scss'
* @param node the node to check
* @return The given node if it is a KaTeX block element, undefined otherwise.
*/
const getNodeIfKatexBlock = (node: Element): Element | undefined => {
const containsKatexBlock = (node: Element): Element | undefined => {
if (node.name !== 'p' || !node.children || node.children.length === 0) {
return
}
return node.children.filter(isTag).find((subnode) => {
return subnode.name === 'app-katex' && subnode.attribs?.inline === undefined
return isKatexTag(subnode, false) ? subnode : undefined
})
}
/**
* Checks if the given node is a KaTeX inline element.
* Checks if the given node is a KaTeX element.
*
* @param node the node to check
* @return The given node if it is a KaTeX inline element, undefined otherwise.
* @param expectedInline defines if the found katex element is expected to be an inline or block element.
* @return {@code true} if the given node is a katex element.
*/
const getNodeIfInlineKatex = (node: Element): Element | undefined => {
return node.name === 'app-katex' && node.attribs?.inline !== undefined ? node : undefined
const isKatexTag = (node: Element, expectedInline: boolean) => {
return node.name === 'app-katex' && (node.attribs?.['data-inline'] !== undefined) === expectedInline
}
const KaTeX = React.lazy(() => import(/* webpackChunkName: "katex" */ '@matejmazur/react-katex'))
@ -46,18 +47,18 @@ export class KatexReplacer extends ComponentReplacer {
public static readonly markdownItPlugin: MarkdownIt.PluginSimple = mathJax({
beforeMath: '<app-katex>',
afterMath: '</app-katex>',
beforeInlineMath: '<app-katex inline>',
beforeInlineMath: '<app-katex data-inline="true">',
afterInlineMath: '</app-katex>',
beforeDisplayMath: '<app-katex>',
afterDisplayMath: '</app-katex>'
})
public replace(node: Element): React.ReactElement | undefined {
const katex = getNodeIfKatexBlock(node) || getNodeIfInlineKatex(node)
if (katex?.children && katex.children[0]) {
const mathJaxContent = ComponentReplacer.extractTextChildContent(katex)
const isInline = katex.attribs?.inline !== undefined
return <KaTeX block={!isInline} math={mathJaxContent} errorColor={'#cc0000'} />
if (!(isKatexTag(node, true) || containsKatexBlock(node)) || node.children?.[0] === undefined) {
return DO_NOT_REPLACE
}
const latexContent = ComponentReplacer.extractTextChildContent(node)
const isInline = !!node.attribs?.['data-inline']
return <KaTeX block={!isInline} math={latexContent} errorColor={'#cc0000'} />
}
}

View file

@ -35,14 +35,7 @@ export class LinkReplacer extends ComponentReplacer {
}
const url = node.attribs.href.trim()
// eslint-disable-next-line no-script-url
if (url.startsWith('data:') || url.startsWith('javascript:') || url.startsWith('vbscript:')) {
return <span>{node.attribs.href}</span>
}
const isJumpMark = url.substr(0, 1) === '#'
const id = url.substr(1)
try {