Files
hedgedoc-hedgeagent/public/js/render.js
Erik Michelson 4030cbbd3c fix(renderer): update regex for supported link schemes
This commit updates the whitelist we're using for outgoing links from HedgeDoc. Previously, any URI scheme except javascript: could be used as long as it contains two slashes after the scheme (like https://). On the one hand this allowed linking to arbitrary and possibly unsafe URI schemes, on the other hand this breaks some schemes like xmpp: or geo:.
We're now using the list of schemes that can be registered by a browser to be opened. This restricts arbitrary scheme usage but on the other side fixes several other schemes.

Signed-off-by: Erik Michelson <github@erik.michelson.eu>
2025-11-21 21:37:46 +01:00

80 lines
3.0 KiB
JavaScript

// allow some attributes
const filterXSS = require('xss')
const whiteListAttr = ['id', 'class', 'style']
window.whiteListAttr = whiteListAttr
// allow links starting with '.', '/', '#', '?', 'http://', 'https://' and protocols supported by the navigator.registerProtocolHandler API
// These schemes can be considered safe-enough for linking to since these are the ones that can be opened using a browser.
// See: https://developer.mozilla.org/en-US/docs/Web/API/Navigator/registerProtocolHandler
const linkRegex = /^(?:\?|#|\.|\/|https?:\/\/|(?:web\+[a-z]+|bitcoin|ftp|ftps|geo|im|irc|ircs|magnet|mailto|matrix|mms|news|nntp|openpgp4fpr|sftp|sip|sms|smsto|ssh|tel|urn|webcal|wtai|xmpp):)/i
// allow data uri, from https://gist.github.com/bgrins/6194623
const dataUriRegex = /^\s*data:([a-z]+\/[a-z0-9-+.]+(;[a-z-]+=[a-z0-9-]+)?)?(;base64)?,([a-z0-9!$&',()*+;=\-._~:@/?%\s]*)\s*$/i
// custom white list
const whiteList = filterXSS.whiteList
// allow ol specify start number
whiteList.ol = ['start']
// allow li specify value number
whiteList.li = ['value']
// allow style tag
whiteList.style = []
// allow kbd tag
whiteList.kbd = []
// allow ifram tag with some safe attributes
whiteList.iframe = ['allowfullscreen', 'name', 'referrerpolicy', 'src', 'width', 'height']
// allow summary tag
whiteList.summary = []
// allow ruby tag
whiteList.ruby = []
// allow rp tag for ruby
whiteList.rp = []
// allow rt tag for ruby
whiteList.rt = []
// allow figure tag
whiteList.figure = []
// allow figcaption tag
whiteList.figcaption = []
const filterXSSOptions = {
allowCommentTag: true,
whiteList,
escapeHtml: function (html) {
// allow html comment in multiple lines
return html.replace(/<(?!!--)/g, '&lt;').replace(/-->/g, '__HTML_COMMENT_END__').replace(/>/g, '&gt;').replace(/__HTML_COMMENT_END__/g, '-->')
},
onIgnoreTag: function (tag, html, options) {
// allow comment tag
if (tag === '!--') {
// do not filter its attributes
return html.replace(/<(?!!--)/g, '&lt;').replace(/-->/g, '__HTML_COMMENT_END__').replace(/>/g, '&gt;').replace(/__HTML_COMMENT_END__/g, '-->')
}
},
onTagAttr: function (tag, name, value, isWhiteAttr) {
// allow href and src that match linkRegex
if (isWhiteAttr && (name === 'href' || name === 'src') && linkRegex.test(value)) {
return name + '="' + filterXSS.escapeAttrValue(value) + '"'
}
// allow data uri in img src
if (isWhiteAttr && (tag === 'img' && name === 'src') && dataUriRegex.test(value)) {
return name + '="' + filterXSS.escapeAttrValue(value) + '"'
}
},
onIgnoreTagAttr: function (tag, name, value, isWhiteAttr) {
// allow attr start with 'data-' or in the whiteListAttr
if (name.substr(0, 5) === 'data-' || window.whiteListAttr.indexOf(name) !== -1) {
// escape its value using built-in escapeAttrValue function
return name + '="' + filterXSS.escapeAttrValue(value) + '"'
}
}
}
function preventXSS (html) {
return filterXSS(html, filterXSSOptions)
}
window.preventXSS = preventXSS
module.exports = {
preventXSS,
escapeAttrValue: filterXSS.escapeAttrValue
}