fix: escape mustache template chars in content
This commit is contained in:
@@ -92,6 +92,7 @@
|
|||||||
"js-base64": "2.5.2",
|
"js-base64": "2.5.2",
|
||||||
"js-binary": "1.2.0",
|
"js-binary": "1.2.0",
|
||||||
"js-yaml": "3.13.1",
|
"js-yaml": "3.13.1",
|
||||||
|
"jsdom": "16.2.2",
|
||||||
"jsonwebtoken": "8.5.1",
|
"jsonwebtoken": "8.5.1",
|
||||||
"katex": "0.11.1",
|
"katex": "0.11.1",
|
||||||
"klaw": "3.0.0",
|
"klaw": "3.0.0",
|
||||||
|
|||||||
@@ -230,12 +230,31 @@ module.exports = {
|
|||||||
headers.push(headerSlug)
|
headers.push(headerSlug)
|
||||||
})
|
})
|
||||||
|
|
||||||
let output = decodeEscape($.html('body').replace('<body>', '').replace('</body>', ''))
|
// --------------------------------
|
||||||
|
// Escape mustache expresions
|
||||||
|
// --------------------------------
|
||||||
|
|
||||||
|
function iterateMustacheNode (node) {
|
||||||
|
const list = $(node).contents().toArray()
|
||||||
|
list.forEach(item => {
|
||||||
|
if (item.type === 'text') {
|
||||||
|
const rawText = $(item).text()
|
||||||
|
if (rawText.indexOf('{{') >= 0 && rawText.indexOf('}}') > 1) {
|
||||||
|
$(item).parent().attr('v-pre', true)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
iterateMustacheNode(item)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
iterateMustacheNode($.root())
|
||||||
|
|
||||||
// --------------------------------
|
// --------------------------------
|
||||||
// STEP: POST
|
// STEP: POST
|
||||||
// --------------------------------
|
// --------------------------------
|
||||||
|
|
||||||
|
let output = decodeEscape($.html('body').replace('<body>', '').replace('</body>', ''))
|
||||||
|
|
||||||
for (let child of _.sortBy(_.filter(this.children, ['step', 'post']), ['order'])) {
|
for (let child of _.sortBy(_.filter(this.children, ['step', 'post']), ['order'])) {
|
||||||
const renderer = require(`../${_.kebabCase(child.key)}/renderer.js`)
|
const renderer = require(`../${_.kebabCase(child.key)}/renderer.js`)
|
||||||
output = await renderer.init(output, child.config)
|
output = await renderer.init(output, child.config)
|
||||||
|
|||||||
@@ -13,3 +13,10 @@ props:
|
|||||||
title: Sanitize HTML
|
title: Sanitize HTML
|
||||||
default: true
|
default: true
|
||||||
hint: Sanitize HTML from unsafe attributes and tags that could lead to XSS attacks
|
hint: Sanitize HTML from unsafe attributes and tags that could lead to XSS attacks
|
||||||
|
order: 1
|
||||||
|
allowIFrames:
|
||||||
|
type: Boolean
|
||||||
|
title: Allow iframes
|
||||||
|
default: false
|
||||||
|
hint: iframes will not be stripped if enabled. (Not recommended)
|
||||||
|
order: 2
|
||||||
|
|||||||
@@ -1,55 +1,22 @@
|
|||||||
const xss = require('xss')
|
const { JSDOM } = require('jsdom')
|
||||||
|
const createDOMPurify = require('dompurify')
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
async init(input, config) {
|
async init(input, config) {
|
||||||
if (config.safeHTML) {
|
if (config.safeHTML) {
|
||||||
input = xss(input, {
|
const window = new JSDOM('').window
|
||||||
whiteList: {
|
const DOMPurify = createDOMPurify(window)
|
||||||
...xss.whiteList,
|
|
||||||
a: ['class', 'id', 'href', 'style', 'target', 'title', 'rel'],
|
const allowedAttrs = ['v-pre', 'v-slot:tabs', 'v-slot:content']
|
||||||
blockquote: ['class', 'id', 'style'],
|
const allowedTags = ['tabset', 'template']
|
||||||
code: ['class', 'style'],
|
|
||||||
details: ['class', 'style'],
|
if (config.allowIFrames) {
|
||||||
defs: ['stroke', 'fill', 'stroke-width', 'transform', 'id'],
|
allowedTags.push('iframe')
|
||||||
div: ['class', 'id', 'style'],
|
}
|
||||||
em: ['class', 'style'],
|
|
||||||
figcaption: ['class', 'style', 'id'],
|
input = DOMPurify.sanitize(input, {
|
||||||
figure: ['class', 'style', 'id'],
|
ADD_ATTR: allowedAttrs,
|
||||||
g: ['transform', 'stroke', 'stroke-width', 'fill'],
|
ADD_TAGS: allowedTags
|
||||||
h1: ['class', 'id', 'style'],
|
|
||||||
h2: ['class', 'id', 'style'],
|
|
||||||
h3: ['class', 'id', 'style'],
|
|
||||||
h4: ['class', 'id', 'style'],
|
|
||||||
h5: ['class', 'id', 'style'],
|
|
||||||
h6: ['class', 'id', 'style'],
|
|
||||||
i: ['class', 'id', 'style'],
|
|
||||||
img: ['alt', 'class', 'draggable', 'height', 'id', 'src', 'style', 'width'],
|
|
||||||
input: ['class', 'disabled', 'type', 'checked', 'id'],
|
|
||||||
kbd: ['class'],
|
|
||||||
label: ['class', 'id', 'for'],
|
|
||||||
li: ['class', 'id', 'style'],
|
|
||||||
mark: ['class', 'style'],
|
|
||||||
ol: ['class', 'id', 'style', 'start'],
|
|
||||||
p: ['class', 'id', 'style'],
|
|
||||||
path: ['d', 'style', 'id'],
|
|
||||||
pre: ['class', 'id', 'style'],
|
|
||||||
section: ['class', 'style'],
|
|
||||||
span: ['class', 'style', 'aria-hidden'],
|
|
||||||
strong: ['class', 'style'],
|
|
||||||
summary: ['class', 'id', 'style'],
|
|
||||||
svg: ['width', 'height', 'viewbox', 'preserveaspectratio', 'style'],
|
|
||||||
table: ['border', 'class', 'id', 'style', 'width'],
|
|
||||||
tabset: [],
|
|
||||||
tbody: ['class', 'style'],
|
|
||||||
td: ['align', 'class', 'colspan', 'rowspan', 'style', 'valign', 'id'],
|
|
||||||
template: ['v-slot:tabs', 'v-slot:content'],
|
|
||||||
th: ['align', 'class', 'colspan', 'rowspan', 'style', 'valign', 'id'],
|
|
||||||
thead: ['class', 'style'],
|
|
||||||
tr: ['class', 'rowspan', 'style', 'align', 'valign', 'id'],
|
|
||||||
ul: ['class', 'id', 'style'],
|
|
||||||
use: ['href', 'transform']
|
|
||||||
},
|
|
||||||
css: false
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return input
|
return input
|
||||||
|
|||||||
Reference in New Issue
Block a user