// Idea from http://stackoverflow.com/questions/295566/sanitize-rewrite-html-on-the-client-side
// https://quip.com/aO0pAZO9m9SG
'use strict';
export var Sanitizer = {
sanitize: (function() {
var PERMITTED_TAGS = {
"A": true,
"ABBR": true,
"ACRONYM": true,
"ADDRESS": true,
"AREA": true,
"B": true,
"BDO": true,
"BODY": true,
"BIG": true,
"BLOCKQUOTE": true,
"BR": true,
"BUTTON": true,
"CAPTION": true,
"CENTER": true,
"CITE": true,
"CODE": true,
"COL": true,
"COLGROUP": true,
"DD": true,
"DEL": true,
"DFN": true,
"DIR": true,
"DIV": true,
"DL": true,
"DT": true,
"EM": true,
"FIELDSET": true,
"FONT": true,
"FORM": true,
"H1": true,
"H2": true,
"H3": true,
"H4": true,
"H5": true,
"H6": true,
"HR": true,
"I": true,
"IMG": true,
"INPUT": true,
"INS": true,
"KBD": true,
"LABEL": true,
"LEGEND": true,
"LI": true,
"MAP": true,
"MENU": true,
"OL": true,
"OPTGROUP": true,
"OPTION": true,
"P": true,
"PRE": true,
"Q": true,
"S": true,
"SAMP": true,
"SELECT": true,
"SMALL": true,
"SPAN": true,
"STYLE": false, // explanation here
"STRIKE": true,
"STRONG": true,
"SUB": true,
"SUP": true,
"TABLE": true,
"TBODY": true,
"TD": true,
"TEXTAREA": true,
"TFOOT": true,
"TH": true,
"THEAD": true,
"TR": true,
"TT": true,
"U": true,
"UL": true,
"VAR": true
},
PERMITTED_STYLES = [
"direction",
"font",
"font-family",
"font-style",
"font-variant",
"font-size",
"font-weight",
"letter-spacing",
"line-height",
"text-align",
"text-decoration",
"text-indent",
"text-overflow",
"text-shadow",
"text-transform",
"white-space",
"word-spacing",
"word-wrap",
"vertical-align",
//
"color",
"background",
"background-color",
"background-image",
"background-repeat",
//
"border",
"border-color",
"border-radius",
"height",
"margin",
"padding",
"width",
"max-width",
"min-width",
//
"clear",
"float",
//
"border-collapse",
"border-spacing",
"caption-side",
"empty-cells",
"table-layout",
//
"list-style-type",
"list-style-position"
],
PERMITTED_ATTRIBUTES = {
"href": true,//sanitize
"src": true,//sanitize
"style": true,
"color": true,
"bgcolor": true,
"width": true,
"height" : true,
"colspan": true,
"rowspan": true,
"cellspacing": true,
"cellpadding": true,
"border": true,
"align": true,
"valign": true,
"dir": true,
// Migadu specific, added only after preprocessed
"data-proxy-src": true,
"data-proxy-style": true,
"target": true,
},
PROTOCOL_SEPARATOR = /:|(�*58)|(p)|(�*3a)|(%|%)3A/i,
PERMITTED_PROTOCOLS = {
'ftp': true,
'http': true,
'https': true,
'irc': true,
'mailto': true,
'news': true,
'gopher': true,
'nntp': true,
'telnet': true,
'webcal': true,
'xmpp': true,
'callto': true,
'feed': true
},
doSanitizeHTML = function(inputHTML, callbackFunc) {
var sandboxedIFrame,
makeSanitizedCopy = function(node) {
var newNode, i, l, attr, trimmedAttributeValue, subCopy;
if (node.nodeType === Node.TEXT_NODE)
return node.cloneNode(true);
if (!(node.nodeType === Node.ELEMENT_NODE &&
typeof PERMITTED_TAGS[node.tagName] !== "undefined"))
return document.createDocumentFragment();
newNode = sandboxedIFrame.contentDocument.createElement(node.tagName);
for (i = 0, l = node.attributes.length; i < l; i += 1) {
attr = node.attributes[i];
if (PERMITTED_ATTRIBUTES[attr.name])
switch (attr.name) {
case "style":
PERMITTED_STYLES.forEach(function(st) {
var elStyle = node.style[st];
if (elStyle.length)
newNode.style[st] = node.style[st];
});
break;
case "src":
case "href" :
trimmedAttributeValue = attr.value.trim();
if (typeof PERMITTED_PROTOCOLS[trimmedAttributeValue.split(PROTOCOL_SEPARATOR)[0]] !== "undefined")
newNode.setAttribute(attr.name, trimmedAttributeValue);
break;
default:
newNode.setAttribute(attr.name, attr.value);
}
}
for (i = 0, l = node.childNodes.length; i < l; i += 1) {
subCopy = makeSanitizedCopy(node.childNodes[i]);
newNode.appendChild(subCopy, false);
}
return newNode;
};
if (typeof callbackFunc === 'undefined')
return;
sandboxedIFrame = document.createElement("IFRAME");
if (typeof sandboxedIFrame["sandbox"] === "undefined") {
alert("Your browser does not support sandboxed iframes. Please upgrade to a modern browser.");
return;
}
sandboxedIFrame.setAttribute("src", "about:blank");
sandboxedIFrame.setAttribute("sandbox", "allow-same-origin");
sandboxedIFrame.style.display = "none";
sandboxedIFrame.addEventListener("load", function(ev) {
sandboxedIFrame.contentWindow.document.write(inputHTML);
callbackFunc(makeSanitizedCopy(sandboxedIFrame.contentDocument.body).innerHTML);
document.body.removeChild(sandboxedIFrame);
});
document.body.appendChild(sandboxedIFrame);
};
return doSanitizeHTML;
})()
};