mirror of
https://github.com/BoostIo/Boostnote
synced 2025-12-13 17:56:25 +00:00
fixing sanitization of inline html like (<kbd>) #1992
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
import sanitizeHtml from 'sanitize-html'
|
||||
import { escapeHtmlCharacters } from './utils'
|
||||
import url from 'url'
|
||||
|
||||
module.exports = function sanitizePlugin (md, options) {
|
||||
options = options || {}
|
||||
@@ -25,7 +26,7 @@ module.exports = function sanitizePlugin (md, options) {
|
||||
const inlineTokens = state.tokens[tokenIdx].children
|
||||
for (let childIdx = 0; childIdx < inlineTokens.length; childIdx++) {
|
||||
if (inlineTokens[childIdx].type === 'html_inline') {
|
||||
inlineTokens[childIdx].content = sanitizeHtml(
|
||||
inlineTokens[childIdx].content = sanitizeInline(
|
||||
inlineTokens[childIdx].content,
|
||||
options
|
||||
)
|
||||
@@ -35,3 +36,86 @@ module.exports = function sanitizePlugin (md, options) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const tag_regex = /<([A-Z][A-Z0-9]*)\s*((?:\s*[A-Z][A-Z0-9]*(?:="(?:[^\"]+)\")?)*)\s*>|<\/([A-Z][A-Z0-9]*)\s*>/i
|
||||
const attributes_regex = /([A-Z][A-Z0-9]*)(="[^\"]+\")?/ig
|
||||
|
||||
function sanitizeInline(html, options) {
|
||||
let match = tag_regex.exec(html)
|
||||
if (!match) {
|
||||
return ''
|
||||
}
|
||||
|
||||
const { allowedTags, allowedAttributes, allowedIframeHostnames, selfClosing, allowedSchemesAppliedToAttributes } = options
|
||||
|
||||
if (match[1] !== null) {
|
||||
// opening tag
|
||||
const tag = match[1].toLowerCase()
|
||||
if (allowedTags.indexOf(tag) === -1) {
|
||||
return ''
|
||||
}
|
||||
|
||||
const attributes = match[2]
|
||||
|
||||
let attrs = ''
|
||||
let name
|
||||
let value
|
||||
|
||||
while ((match = attributes_regex.exec(attributes))) {
|
||||
name = match[1].toLowerCase()
|
||||
value = match[2]
|
||||
|
||||
if (allowedAttributes['*'].indexOf(name) !== -1 || (allowedAttributes[tag] && allowedAttributes[tag].indexOf(name) !== -1)) {
|
||||
if (allowedSchemesAppliedToAttributes.indexOf(name) !== -1) {
|
||||
if (naughtyHRef(value) || (tag === 'iframe' && name === 'src' && naughtyIFrame(value))) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
attrs += ` ${name}${value}`
|
||||
}
|
||||
}
|
||||
|
||||
if (selfClosing.indexOf(tag)) {
|
||||
return '<' + tag + attrs + ' />'
|
||||
} else {
|
||||
return '<' + tag + attrs + '>'
|
||||
}
|
||||
} else {
|
||||
// closing tag
|
||||
if (allowedTags.indexOf(match[3].toLowerCase()) !== -1) {
|
||||
return html
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function naughtyHRef(name, href, options) {
|
||||
href = href.replace(/[\x00-\x20]+/g, '')
|
||||
href = href.replace(/<\!\-\-.*?\-\-\>/g, '')
|
||||
|
||||
const matches = href.match(/^([a-zA-Z]+)\:/)
|
||||
if (!matches) {
|
||||
if (href.match(/^[\/\\]{2}/)) {
|
||||
return !options.allowProtocolRelative
|
||||
}
|
||||
|
||||
// No scheme
|
||||
return false
|
||||
}
|
||||
|
||||
const scheme = matches[1].toLowerCase()
|
||||
|
||||
return options.allowedSchemes.indexOf(scheme) === -1
|
||||
}
|
||||
|
||||
function naughtyIFrame(src) {
|
||||
try {
|
||||
const parsed = url.parse(src, false, true)
|
||||
|
||||
return allowedIframeHostnames.index(parsed.hostname) === -1
|
||||
} catch (e) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -105,7 +105,11 @@ class Markdown {
|
||||
'iframe': ['src', 'width', 'height', 'frameborder', 'allowfullscreen'],
|
||||
'input': ['type', 'id', 'checked']
|
||||
},
|
||||
allowedIframeHostnames: ['www.youtube.com']
|
||||
allowedIframeHostnames: ['www.youtube.com'],
|
||||
selfClosing: [ 'img', 'br', 'hr', 'input' ],
|
||||
allowedSchemes: [ 'http', 'https', 'ftp', 'mailto' ],
|
||||
allowedSchemesAppliedToAttributes: [ 'href', 'src', 'cite' ],
|
||||
allowProtocolRelative: true
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user