Improve Froala sanitization of pasted content.
This commit is contained in:
parent
0cdda52eb8
commit
b384954a29
|
|
@ -6,7 +6,7 @@
|
|||
* - data-option="value" - an option with a value
|
||||
*
|
||||
* JavaScript API:
|
||||
* $('a#someElement').recordFinder({ option: 'value' })
|
||||
* $('a#someElement').mediaFinder({ option: 'value' })
|
||||
*
|
||||
* Dependences:
|
||||
* - Some other plugin (filename.js)
|
||||
|
|
@ -71,7 +71,7 @@
|
|||
this.$findValue = null
|
||||
this.$el = null
|
||||
|
||||
// In some cases options could contain callbacks,
|
||||
// In some cases options could contain callbacks,
|
||||
// so it's better to clean them up too.
|
||||
this.options = null
|
||||
|
||||
|
|
|
|||
|
|
@ -194,6 +194,7 @@ this.$textarea.on('froalaEditor.initialized',this.proxy(this.build))
|
|||
this.$textarea.on('froalaEditor.contentChanged',this.proxy(this.onChange))
|
||||
this.$textarea.on('froalaEditor.html.get',this.proxy(this.onSyncContent))
|
||||
this.$textarea.on('froalaEditor.html.set',this.proxy(this.onSetContent))
|
||||
this.$textarea.on('froalaEditor.paste.beforeCleanup',this.proxy(this.beforeCleanupPaste))
|
||||
this.$form.on('oc.beforeRequest',this.proxy(this.onFormBeforeRequest))
|
||||
this.$textarea.froalaEditor(froalaOptions)
|
||||
this.editor=this.$textarea.data('froala.editor')
|
||||
|
|
@ -213,6 +214,7 @@ this.$textarea.off('froalaEditor.initialized',this.proxy(this.build))
|
|||
this.$textarea.off('froalaEditor.contentChanged',this.proxy(this.onChange))
|
||||
this.$textarea.off('froalaEditor.html.get',this.proxy(this.onSyncContent))
|
||||
this.$textarea.off('froalaEditor.html.set',this.proxy(this.onSetContent))
|
||||
this.$textarea.off('froalaEditor.paste.beforeCleanup',this.proxy(this.beforeCleanupPaste))
|
||||
this.$form.off('oc.beforeRequest',this.proxy(this.onFormBeforeRequest))
|
||||
$(window).off('resize',this.proxy(this.updateLayout))
|
||||
$(window).off('oc.updateUi',this.proxy(this.updateLayout))
|
||||
|
|
@ -243,6 +245,7 @@ RichEditor.prototype.insertUiBlock=function($node){this.$textarea.froalaEditor('
|
|||
RichEditor.prototype.insertVideo=function(url,title){this.$textarea.froalaEditor('figures.insertVideo',url,title)}
|
||||
RichEditor.prototype.insertAudio=function(url,title){this.$textarea.froalaEditor('figures.insertAudio',url,title)}
|
||||
RichEditor.prototype.onSetContent=function(ev,editor){this.$textarea.trigger('setContent.oc.richeditor',[this])}
|
||||
RichEditor.prototype.beforeCleanupPaste=function(ev,editor,clipboard_html){return ocSanitize(clipboard_html)}
|
||||
RichEditor.prototype.onSyncContent=function(ev,editor,html){if(editor.codeBeautifier){html=editor.codeBeautifier.run(html,editor.opts.codeBeautifierOptions)}
|
||||
var container={html:html}
|
||||
this.$textarea.trigger('syncContent.oc.richeditor',[this,container])
|
||||
|
|
|
|||
|
|
@ -209,6 +209,7 @@
|
|||
this.$textarea.on('froalaEditor.contentChanged', this.proxy(this.onChange))
|
||||
this.$textarea.on('froalaEditor.html.get', this.proxy(this.onSyncContent))
|
||||
this.$textarea.on('froalaEditor.html.set', this.proxy(this.onSetContent))
|
||||
this.$textarea.on('froalaEditor.paste.beforeCleanup', this.proxy(this.beforeCleanupPaste))
|
||||
this.$form.on('oc.beforeRequest', this.proxy(this.onFormBeforeRequest))
|
||||
|
||||
this.$textarea.froalaEditor(froalaOptions)
|
||||
|
|
@ -245,6 +246,7 @@
|
|||
this.$textarea.off('froalaEditor.contentChanged', this.proxy(this.onChange))
|
||||
this.$textarea.off('froalaEditor.html.get', this.proxy(this.onSyncContent))
|
||||
this.$textarea.off('froalaEditor.html.set', this.proxy(this.onSetContent))
|
||||
this.$textarea.off('froalaEditor.paste.beforeCleanup', this.proxy(this.beforeCleanupPaste))
|
||||
this.$form.off('oc.beforeRequest', this.proxy(this.onFormBeforeRequest))
|
||||
|
||||
$(window).off('resize', this.proxy(this.updateLayout))
|
||||
|
|
@ -344,6 +346,10 @@
|
|||
this.$textarea.trigger('setContent.oc.richeditor', [this])
|
||||
}
|
||||
|
||||
RichEditor.prototype.beforeCleanupPaste = function (ev, editor, clipboard_html) {
|
||||
return ocSanitize(clipboard_html)
|
||||
}
|
||||
|
||||
RichEditor.prototype.onSyncContent = function(ev, editor, html) {
|
||||
// Beautify HTML.
|
||||
if (editor.codeBeautifier) {
|
||||
|
|
|
|||
|
|
@ -185,4 +185,6 @@ if(str[0]==="["){var result="[";var type="needBody";for(var i=1;i<str.length;i++
|
|||
if(str[i]==="]"&&i===str.length-1){if(result[result.length-1]===",")result=result.substr(0,result.length-1);result+="]";return result;}
|
||||
var body=getBody(str,i);i=i+body.originLength-1;result+=parse(body.body);type="afterBody";}else if(type==="afterBody"){if(str[i]===","){result+=",";type="needBody";while(str[i+1]===","||isBlankChar(str[i+1])){if(str[i+1]===",")result+="null,";i++;}}else if(str[i]==="]"&&i===str.length-1){result+="]";return result;}}}
|
||||
throw new Error("Broken JSON array near "+result);}}
|
||||
window.ocJSON=function(json){var jsonString=parse(json);return JSON.parse(jsonString);};}(window);
|
||||
window.ocJSON=function(json){var jsonString=parse(json);return JSON.parse(jsonString);};}(window);+function(window){"use strict";function trimAttributes(node){$.each(node.attributes,function(){var attrName=this.name;var attrValue=this.value;if(attrName.indexOf('on')==0||attrValue.indexOf('javascript:')==0){$(node).removeAttr(attrName);}});}
|
||||
function sanitize(html){var output=$($.parseHTML('<div>'+html+'</div>',null,false));output.find('*').each(function(){trimAttributes(this);});return output.html();}
|
||||
window.ocSanitize=function(html){return sanitize(html)};}(window);
|
||||
|
|
@ -185,7 +185,9 @@ if(str[0]==="["){var result="[";var type="needBody";for(var i=1;i<str.length;i++
|
|||
if(str[i]==="]"&&i===str.length-1){if(result[result.length-1]===",")result=result.substr(0,result.length-1);result+="]";return result;}
|
||||
var body=getBody(str,i);i=i+body.originLength-1;result+=parse(body.body);type="afterBody";}else if(type==="afterBody"){if(str[i]===","){result+=",";type="needBody";while(str[i+1]===","||isBlankChar(str[i+1])){if(str[i+1]===",")result+="null,";i++;}}else if(str[i]==="]"&&i===str.length-1){result+="]";return result;}}}
|
||||
throw new Error("Broken JSON array near "+result);}}
|
||||
window.ocJSON=function(json){var jsonString=parse(json);return JSON.parse(jsonString);};}(window);+function($){"use strict";if($.oc===undefined)
|
||||
window.ocJSON=function(json){var jsonString=parse(json);return JSON.parse(jsonString);};}(window);+function(window){"use strict";function trimAttributes(node){$.each(node.attributes,function(){var attrName=this.name;var attrValue=this.value;if(attrName.indexOf('on')==0||attrValue.indexOf('javascript:')==0){$(node).removeAttr(attrName);}});}
|
||||
function sanitize(html){var output=$($.parseHTML('<div>'+html+'</div>',null,false));output.find('*').each(function(){trimAttributes(this);});return output.html();}
|
||||
window.ocSanitize=function(html){return sanitize(html)};}(window);+function($){"use strict";if($.oc===undefined)
|
||||
$.oc={}
|
||||
var LOADER_CLASS='oc-loading';$(document).on('ajaxSetup','[data-request][data-request-flash]',function(event,context){context.options.handleErrorMessage=function(message){$.oc.flashMsg({text:message,class:'error'})}
|
||||
context.options.handleFlashMessage=function(message,type){$.oc.flashMsg({text:message,class:type})}})
|
||||
|
|
|
|||
|
|
@ -907,3 +907,71 @@ if (window.jQuery.request !== undefined) {
|
|||
};
|
||||
|
||||
}(window);
|
||||
|
||||
/*
|
||||
* October CMS jQuery HTML Sanitizer
|
||||
* @see https://gist.github.com/ufologist/5a0da51b2b9ef1b861c30254172ac3c9
|
||||
*/
|
||||
+function(window) { "use strict";
|
||||
|
||||
function trimAttributes(node) {
|
||||
$.each(node.attributes, function() {
|
||||
var attrName = this.name;
|
||||
var attrValue = this.value;
|
||||
|
||||
/*
|
||||
* remove attributes where the names start with "on" (for example: onload, onerror...)
|
||||
* remove attributes where the value starts with the "javascript:" pseudo protocol (for example href="javascript:alert(1)")
|
||||
*/
|
||||
if (attrName.indexOf('on') == 0 || attrValue.indexOf('javascript:') == 0) {
|
||||
$(node).removeAttr(attrName);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function sanitize(html) {
|
||||
/*
|
||||
* [jQuery.parseHTML(data [, context ] [, keepScripts ])](http://api.jquery.com/jQuery.parseHTML/) added: 1.8
|
||||
* Parses a string into an array of DOM nodes.
|
||||
*
|
||||
* By default, the context is the current document if not specified or given as null or undefined. If the HTML was to be used
|
||||
* in another document such as an iframe, that frame's document could be used.
|
||||
*
|
||||
* As of 3.0 the default behavior is changed.
|
||||
*
|
||||
* If the context is not specified or given as null or undefined, a new document is used.
|
||||
* This can potentially improve security because inline events will not execute when the HTML is parsed. Once the parsed HTML
|
||||
* is injected into a document it does execute, but this gives tools a chance to traverse the created DOM and remove anything
|
||||
* deemed unsafe. This improvement does not apply to internal uses of jQuery.parseHTML as they usually pass in the current
|
||||
* document. Therefore, a statement like $( "#log" ).append( $( htmlString ) ) is still subject to the injection of malicious code.
|
||||
*
|
||||
* without context do not execute script
|
||||
* $.parseHTML('<div><img src=1 onerror=alert(1)></div>');
|
||||
* $.parseHTML('<div><img src=1 onerror=alert(2)></div>', null);
|
||||
*
|
||||
* with context document execute script!
|
||||
* $.parseHTML('<div><img src=1 onerror=alert(3)></div>', document);
|
||||
*
|
||||
* Most jQuery APIs that accept HTML strings will run scripts that are included in the HTML. jQuery.parseHTML does not run scripts
|
||||
* in the parsed HTML unless keepScripts is explicitly true. However, it is still possible in most environments to execute scripts
|
||||
* indirectly, for example via the <img onerror> attribute.
|
||||
*
|
||||
* will return []
|
||||
* $.parseHTML('<script>alert(1)<\/script>', null, false);
|
||||
*
|
||||
* will return [script DOM element]
|
||||
* $.parseHTML('<script>alert(1)<\/script>', null, true);
|
||||
*/
|
||||
var output = $($.parseHTML('<div>' + html + '</div>', null, false));
|
||||
output.find('*').each(function() {
|
||||
trimAttributes(this);
|
||||
});
|
||||
return output.html();
|
||||
}
|
||||
|
||||
// Global function
|
||||
window.ocSanitize = function(html) {
|
||||
return sanitize(html)
|
||||
};
|
||||
|
||||
}(window);
|
||||
Loading…
Reference in New Issue