import xss from 'pretty-text/xss'; const _validIframes = []; function attr(name, value) { if (value) { return `${name}="${xss.escapeAttrValue(value)}"`; } return name; } const ESCAPE_REPLACEMENTS = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'", '`': '`' }; const BAD_CHARS = /[&<>"'`]/g; const POSSIBLE_CHARS = /[&<>"'`]/; function escapeChar(chr) { return ESCAPE_REPLACEMENTS[chr]; } export function escape(string) { // don't escape SafeStrings, since they're already safe if (string === null) { return ""; } else if (!string) { return string + ''; } // Force a string conversion as this will be done by the append regardless and // the regex test will do this transparently behind the scenes, causing issues if // an object's to string has escaped characters in it. string = "" + string; if (!POSSIBLE_CHARS.test(string)) { return string; } return string.replace(BAD_CHARS, escapeChar); } export function hrefAllowed(href, extraHrefMatchers) { // escape single quotes href = href.replace(/'/g, "%27"); // absolute urls if (/^(https?:)?\/\/[\w\.\-]+/i.test(href)) { return href; } // relative urls if (/^\/[\w\.\-]+/i.test(href)) { return href; } // anchors if (/^#[\w\.\-]+/i.test(href)) { return href; } // mailtos if (/^mailto:[\w\.\-@]+/i.test(href)) { return href; } if (extraHrefMatchers && extraHrefMatchers.length > 0) { for (let i=0; i 0) { extraHrefMatchers = [new RegExp('^(' + allowedHrefSchemes.join('|') + '):\/\/[\\w\\.\\-]+','i')]; } let result = xss(text, { whiteList: whiteList.tagList, stripIgnoreTag: true, stripIgnoreTagBody: ['script', 'table'], onIgnoreTagAttr(tag, name, value) { const forTag = whiteList.attrList[tag]; if (forTag) { const forAttr = forTag[name]; if ((forAttr && (forAttr.indexOf('*') !== -1 || forAttr.indexOf(value) !== -1)) || (name.indexOf('data-') === 0 && forTag['data-*']) || ((tag === 'a' && name === 'href') && hrefAllowed(value, extraHrefMatchers)) || (tag === 'img' && name === 'src' && (/^data:image.*$/i.test(value) || hrefAllowed(value, extraHrefMatchers))) || (tag === 'iframe' && name === 'src' && _validIframes.some(i => i.test(value)))) { return attr(name, value); } if (tag === 'iframe' && name === 'src') { return "-STRIP-"; } const custom = whiteLister.getCustom(); for (let i=0; i]+\-STRIP\-[^>]*>[^<]*<\/iframe>/g, '') .replace(/&(?![#\w]+;)/g, '&') .replace(/'/g, "'") .replace(/ \/>/g, '>'); }; export function whiteListIframe(regexp) { _validIframes.push(regexp); } whiteListIframe(/^(https?:)?\/\/www\.google\.com\/maps\/embed\?.+/i); whiteListIframe(/^(https?:)?\/\/www\.openstreetmap\.org\/export\/embed.html\?.+/i);