Refactor markdown/sanitize html code

pull/3323/head
Chocobozzz 2020-11-17 14:34:09 +01:00
parent 9afb5c10e5
commit 9ff36c2d70
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
7 changed files with 67 additions and 70 deletions

View File

@ -1,5 +1,6 @@
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { LinkifierService } from './linkifier.service' import { LinkifierService } from './linkifier.service'
import { SANITIZE_OPTIONS } from '@shared/core-utils/renderer/html'
@Injectable() @Injectable()
export class HtmlRendererService { export class HtmlRendererService {
@ -25,27 +26,7 @@ export class HtmlRendererService {
// Convert possible markdown to html // Convert possible markdown to html
const html = this.linkifier.linkify(text) const html = this.linkifier.linkify(text)
return this.sanitizeHtml(html, { return this.sanitizeHtml(html, SANITIZE_OPTIONS)
allowedTags: [ 'a', 'p', 'span', 'br', 'strong', 'em', 'ul', 'ol', 'li' ],
allowedSchemes: [ 'http', 'https' ],
allowedAttributes: {
'a': [ 'href', 'class', 'target', 'rel' ]
},
transformTags: {
a: (tagName, attribs) => {
let rel = 'noopener noreferrer'
if (attribs.rel === 'me') rel += ' me'
return {
tagName,
attribs: Object.assign(attribs, {
target: '_blank',
rel
})
}
}
}
})
} }
private async loadSanitizeHtml () { private async loadSanitizeHtml () {

View File

@ -1,6 +1,13 @@
import * as MarkdownIt from 'markdown-it' import * as MarkdownIt from 'markdown-it'
import { buildVideoLink } from 'src/assets/player/utils' import { buildVideoLink } from 'src/assets/player/utils'
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import {
COMPLETE_RULES,
ENHANCED_RULES,
ENHANCED_WITH_HTML_RULES,
TEXT_RULES,
TEXT_WITH_HTML_RULES
} from '@shared/core-utils/renderer/markdown'
import { HtmlRendererService } from './html-renderer.service' import { HtmlRendererService } from './html-renderer.service'
type MarkdownParsers = { type MarkdownParsers = {
@ -25,21 +32,6 @@ type MarkdownParserConfigs = {
@Injectable() @Injectable()
export class MarkdownService { export class MarkdownService {
static TEXT_RULES = [
'linkify',
'autolink',
'emphasis',
'link',
'newline',
'list'
]
static TEXT_WITH_HTML_RULES = MarkdownService.TEXT_RULES.concat([ 'html_inline', 'html_block' ])
static ENHANCED_RULES = MarkdownService.TEXT_RULES.concat([ 'image' ])
static ENHANCED_WITH_HTML_RULES = MarkdownService.TEXT_WITH_HTML_RULES.concat([ 'image' ])
static COMPLETE_RULES = MarkdownService.ENHANCED_WITH_HTML_RULES.concat([ 'block', 'inline', 'heading', 'paragraph' ])
private markdownParsers: MarkdownParsers = { private markdownParsers: MarkdownParsers = {
textMarkdownIt: null, textMarkdownIt: null,
textWithHTMLMarkdownIt: null, textWithHTMLMarkdownIt: null,
@ -48,13 +40,13 @@ export class MarkdownService {
completeMarkdownIt: null completeMarkdownIt: null
} }
private parsersConfig: MarkdownParserConfigs = { private parsersConfig: MarkdownParserConfigs = {
textMarkdownIt: { rules: MarkdownService.TEXT_RULES, html: false }, textMarkdownIt: { rules: TEXT_RULES, html: false },
textWithHTMLMarkdownIt: { rules: MarkdownService.TEXT_WITH_HTML_RULES, html: true, escape: true }, textWithHTMLMarkdownIt: { rules: TEXT_WITH_HTML_RULES, html: true, escape: true },
enhancedMarkdownIt: { rules: MarkdownService.ENHANCED_RULES, html: false }, enhancedMarkdownIt: { rules: ENHANCED_RULES, html: false },
enhancedWithHTMLMarkdownIt: { rules: MarkdownService.ENHANCED_WITH_HTML_RULES, html: true, escape: true }, enhancedWithHTMLMarkdownIt: { rules: ENHANCED_WITH_HTML_RULES, html: true, escape: true },
completeMarkdownIt: { rules: MarkdownService.COMPLETE_RULES, html: true } completeMarkdownIt: { rules: COMPLETE_RULES, html: true }
} }
constructor (private htmlRenderer: HtmlRendererService) {} constructor (private htmlRenderer: HtmlRendererService) {}

View File

@ -5,6 +5,7 @@ import { join } from 'path'
import { VideoChannelModel } from '@server/models/video/video-channel' import { VideoChannelModel } from '@server/models/video/video-channel'
import { MVideoBlacklistLightVideo, MVideoBlacklistVideo } from '@server/types/models/video/video-blacklist' import { MVideoBlacklistLightVideo, MVideoBlacklistVideo } from '@server/types/models/video/video-blacklist'
import { MVideoImport, MVideoImportVideo } from '@server/types/models/video/video-import' import { MVideoImport, MVideoImportVideo } from '@server/types/models/video/video-import'
import { SANITIZE_OPTIONS, TEXT_WITH_HTML_RULES } from '@shared/core-utils'
import { AbuseState, EmailPayload, UserAbuse } from '@shared/models' import { AbuseState, EmailPayload, UserAbuse } from '@shared/models'
import { SendEmailOptions } from '../../shared/models/server/emailer.model' import { SendEmailOptions } from '../../shared/models/server/emailer.model'
import { isTestInstance, root } from '../helpers/core-utils' import { isTestInstance, root } from '../helpers/core-utils'
@ -20,14 +21,7 @@ const markdownItEmoji = require('markdown-it-emoji/light')
const MarkdownItClass = require('markdown-it') const MarkdownItClass = require('markdown-it')
const markdownIt = new MarkdownItClass('default', { linkify: true, breaks: true, html: true }) const markdownIt = new MarkdownItClass('default', { linkify: true, breaks: true, html: true })
markdownIt.enable([ markdownIt.enable(TEXT_WITH_HTML_RULES)
'linkify',
'autolink',
'emphasis',
'link',
'newline',
'list'
])
markdownIt.use(markdownItEmoji) markdownIt.use(markdownItEmoji)
@ -39,27 +33,7 @@ const toSafeHtml = text => {
const html = markdownIt.render(textWithLineFeed) const html = markdownIt.render(textWithLineFeed)
// Convert to safe Html // Convert to safe Html
return sanitizeHtml(html, { return sanitizeHtml(html, SANITIZE_OPTIONS)
allowedTags: [ 'a', 'p', 'span', 'br', 'strong', 'em', 'ul', 'ol', 'li' ],
allowedSchemes: [ 'http', 'https' ],
allowedAttributes: {
a: [ 'href', 'class', 'target', 'rel' ]
},
transformTags: {
a: (tagName, attribs) => {
let rel = 'noopener noreferrer'
if (attribs.rel === 'me') rel += ' me'
return {
tagName,
attribs: Object.assign(attribs, {
target: '_blank',
rel
})
}
}
}
})
} }
const Email = require('email-templates') const Email = require('email-templates')

View File

@ -1,3 +1,7 @@
export * from './abuse'
export * from './i18n'
export * from './logs' export * from './logs'
export * from './miscs' export * from './miscs'
export * from './plugins' export * from './plugins'
export * from './renderer'
export * from './users'

View File

@ -0,0 +1,21 @@
export const SANITIZE_OPTIONS = {
allowedTags: [ 'a', 'p', 'span', 'br', 'strong', 'em', 'ul', 'ol', 'li' ],
allowedSchemes: [ 'http', 'https' ],
allowedAttributes: {
a: [ 'href', 'class', 'target', 'rel' ]
},
transformTags: {
a: (tagName, attribs) => {
let rel = 'noopener noreferrer'
if (attribs.rel === 'me') rel += ' me'
return {
tagName,
attribs: Object.assign(attribs, {
target: '_blank',
rel
})
}
}
}
}

View File

@ -0,0 +1,2 @@
export * from './markdown'
export * from './html'

View File

@ -0,0 +1,23 @@
export const TEXT_RULES = [
'linkify',
'autolink',
'emphasis',
'link',
'newline',
'list'
]
export const TEXT_WITH_HTML_RULES = TEXT_RULES.concat([
'html_inline',
'html_block'
])
export const ENHANCED_RULES = TEXT_RULES.concat([ 'image' ])
export const ENHANCED_WITH_HTML_RULES = TEXT_WITH_HTML_RULES.concat([ 'image' ])
export const COMPLETE_RULES = ENHANCED_WITH_HTML_RULES.concat([
'block',
'inline',
'heading',
'paragraph'
])