Skip to content

Commit 7966fa2

Browse files
authored
WIP: Tags whitelisting (#506)
1 parent 1e76f9d commit 7966fa2

File tree

1 file changed

+157
-0
lines changed
  • ckanext/querytool/fanstatic/javascript

1 file changed

+157
-0
lines changed

ckanext/querytool/fanstatic/javascript/vitals.js

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1118,3 +1118,160 @@ function hideAxisMinMax(selected,chart_number,rangeEnabled){
11181118
$(`.axis_range_max_${chart_number}`).removeClass('hidden');
11191119
}
11201120
};
1121+
1122+
1123+
1124+
/* HTML Sanitizer */
1125+
1126+
var HtmlSanitizer = new (function () {
1127+
1128+
var tagWhitelist_ = {
1129+
'A': false, 'ABBR': true, 'B': true, 'BLOCKQUOTE': true, 'BODY': true, 'BR': true, 'CENTER': false, 'CODE': false, 'DIV': false, 'EM': true, 'FONT': false,
1130+
'H1': true, 'H2': true, 'H3': true, 'H4': true, 'H5': true, 'H6': true, 'HR': true, 'I': true, 'IMG': false, 'LABEL': true, 'LI': true, 'OL': true, 'P': true, 'PRE': false,
1131+
'SMALL': true, 'SOURCE': false, 'SPAN': true, 'STRONG': true, 'TABLE': false, 'TBODY': false, 'TR': false, 'TD': false, 'TH': false, 'THEAD': false, 'UL': true, 'U': true, 'VIDEO': false
1132+
};
1133+
1134+
var contentTagWhiteList_ = { 'FORM': true }; //tags that will be converted to DIVs
1135+
1136+
var attributeWhitelist_ = { 'align': true, 'color': true, 'controls': true, 'height': true, 'href': true, 'src': true, 'style': true, 'target': true, 'title': true, 'type': true, 'width': true };
1137+
1138+
var cssWhitelist_ = { 'color': true, 'background-color': true, 'font-size': true, 'text-align': true, 'text-decoration': true, 'font-weight': true };
1139+
1140+
var schemaWhiteList_ = [ 'http:', 'https:', 'data:', 'm-files:', 'file:', 'ftp:' ]; //which "protocols" are allowed in "href", "src" etc
1141+
1142+
var uriAttributes_ = { 'href': true, 'action': true };
1143+
1144+
this.SanitizeHtml = function(input) {
1145+
input = input.trim();
1146+
if (input == "") return ""; //to save performance and not create iframe
1147+
1148+
//firefox "bogus node" workaround
1149+
if (input == "<br>") return "";
1150+
1151+
var iframe = document.createElement('iframe');
1152+
if (iframe['sandbox'] === undefined) {
1153+
alert('Your browser does not support sandboxed iframes. Please upgrade to a modern browser.');
1154+
return '';
1155+
}
1156+
iframe['sandbox'] = 'allow-same-origin';
1157+
iframe.style.display = 'none';
1158+
document.body.appendChild(iframe); // necessary so the iframe contains a document
1159+
var iframedoc = iframe.contentDocument || iframe.contentWindow.document;
1160+
if (iframedoc.body == null) iframedoc.write("<body></body>"); // null in IE
1161+
iframedoc.body.innerHTML = input;
1162+
1163+
function makeSanitizedCopy(node) {
1164+
if (node.nodeType == Node.TEXT_NODE) {
1165+
var newNode = node.cloneNode(true);
1166+
} else if (node.nodeType == Node.ELEMENT_NODE && (tagWhitelist_[node.tagName] || contentTagWhiteList_[node.tagName])) {
1167+
1168+
//remove useless empty spans (lots of those when pasting from MS Outlook)
1169+
if ((node.tagName == "SPAN" || node.tagName == "B" || node.tagName == "I" || node.tagName == "U")
1170+
&& node.innerHTML.trim() == "") {
1171+
return document.createDocumentFragment();
1172+
}
1173+
1174+
if (contentTagWhiteList_[node.tagName])
1175+
newNode = iframedoc.createElement('DIV'); //convert to DIV
1176+
else
1177+
newNode = iframedoc.createElement(node.tagName);
1178+
1179+
for (var i = 0; i < node.attributes.length; i++) {
1180+
var attr = node.attributes[i];
1181+
if (attributeWhitelist_[attr.name]) {
1182+
if (attr.name == "style") {
1183+
for (s = 0; s < node.style.length; s++) {
1184+
var styleName = node.style[s];
1185+
if (cssWhitelist_[styleName])
1186+
newNode.style.setProperty(styleName, node.style.getPropertyValue(styleName));
1187+
}
1188+
}
1189+
else {
1190+
if (uriAttributes_[attr.name]) { //if this is a "uri" attribute, that can have "javascript:" or something
1191+
if (attr.value.indexOf(":") > -1 && !startsWithAny(attr.value, schemaWhiteList_))
1192+
continue;
1193+
}
1194+
newNode.setAttribute(attr.name, attr.value);
1195+
}
1196+
}
1197+
}
1198+
for (i = 0; i < node.childNodes.length; i++) {
1199+
var subCopy = makeSanitizedCopy(node.childNodes[i]);
1200+
newNode.appendChild(subCopy, false);
1201+
}
1202+
} else {
1203+
newNode = document.createDocumentFragment();
1204+
}
1205+
return newNode;
1206+
};
1207+
1208+
var resultElement = makeSanitizedCopy(iframedoc.body);
1209+
document.body.removeChild(iframe);
1210+
return resultElement.innerHTML
1211+
.replace(/<br[^>]*>(\S)/g, "<br>\n$1")
1212+
.replace(/div><div/g, "div>\n<div"); //replace is just for cleaner code
1213+
}
1214+
1215+
function startsWithAny(str, substrings) {
1216+
for (var i = 0; i < substrings.length; i++) {
1217+
if (str.indexOf(substrings[i]) == 0) {
1218+
return true;
1219+
}
1220+
}
1221+
return false;
1222+
}
1223+
1224+
this.AllowedTags = tagWhitelist_;
1225+
this.AllowedAttributes = attributeWhitelist_;
1226+
this.AllowedCssStyles = cssWhitelist_;
1227+
this.AllowedSchemas = schemaWhiteList_;
1228+
});
1229+
1230+
1231+
/* Sanitize HTML */
1232+
$("body").on('change', '.textbox_desc', function(){
1233+
var content = $(this).val();
1234+
var html = HtmlSanitizer.SanitizeHtml(content);
1235+
$(this).val(html);
1236+
});
1237+
1238+
1239+
$("body").on('change', '[id^=table_field_title_]', function(){
1240+
var content = $(this).val();
1241+
var html = HtmlSanitizer.SanitizeHtml(content);
1242+
$(this).val(html);
1243+
});
1244+
1245+
$("body").on('change', '[id^=chart_field_title_]', function(){
1246+
var content = $(this).val();
1247+
var html = HtmlSanitizer.SanitizeHtml(content);
1248+
$(this).val(html);
1249+
});
1250+
1251+
$("body").on('change', '[id^=chart_field_desc_]', function(){
1252+
var content = $(this).val();
1253+
var html = HtmlSanitizer.SanitizeHtml(content);
1254+
$(this).val(html);
1255+
});
1256+
1257+
$("body").on('change', '[id^=map_custom_title_field_]', function(){
1258+
var content = $(this).val();
1259+
var html = HtmlSanitizer.SanitizeHtml(content);
1260+
$(this).val(html);
1261+
});
1262+
1263+
$("body").on('change', '[id^=field-description]', function(){
1264+
var content = $(this).val();
1265+
var html = HtmlSanitizer.SanitizeHtml(content);
1266+
$(this).val(html);
1267+
});
1268+
1269+
$("body").on('change', '[id^=field-additional_description]', function(){
1270+
var content = $(this).val();
1271+
var html = HtmlSanitizer.SanitizeHtml(content);
1272+
$(this).val(html);
1273+
});
1274+
1275+
1276+
1277+

0 commit comments

Comments
 (0)