Skip to content

Commit

Permalink
Add htmlSanitizer resolved #609 (#622)
Browse files Browse the repository at this point in the history
* Add htmlSanitizer resolved #609
* Add some jsdoc
  • Loading branch information
김성호/FE개발랩/NE authored and seonim-ryu committed Jan 2, 2020
1 parent d29dffd commit 3e42a73
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 23 deletions.
21 changes: 4 additions & 17 deletions apps/core/src/js/convertor.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

'use strict';

var htmlSanitizer = require('./htmlSanitizer');

var markedCustomRenderer = require('./markedCustomRenderer');

var marked = window.marked,
Expand Down Expand Up @@ -84,7 +86,7 @@ Convertor.prototype.toHTMLWithCodeHightlight = function(markdown) {
var html = this._markdownToHtmlWithCodeHighlight(markdown);
html = this.eventManager.emitReduce('convertorAfterMarkdownToHtmlConverted', html);

return this._sanitizeScript(html);
return htmlSanitizer(html, true);
};

/**
Expand All @@ -100,7 +102,7 @@ Convertor.prototype.toHTML = function(markdown) {
var html = this._markdownToHtml(markdown);
html = this.eventManager.emitReduce('convertorAfterMarkdownToHtmlConverted', html);

return this._sanitizeScript(html);
return htmlSanitizer(html, true);
};

/**
Expand All @@ -119,21 +121,6 @@ Convertor.prototype.toMarkdown = function(html) {
return markdown;
};

/**
* _sanitizeScript
* Sanitize script tag
* @private
* @memberOf Convertor
* @param {string} html html text
* @returns {string}
*/
Convertor.prototype._sanitizeScript = function(html) {
html = html.replace(/<script.*?>/g, '&lt;script&gt;');
html = html.replace(/<\/script>/g, '&lt;/script&gt;');

return html;
};

/**
* factory
* Convertor factory
Expand Down
78 changes: 78 additions & 0 deletions apps/core/src/js/htmlSanitizer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/**
* @fileoverview Implements htmlSanitizer
* @author Sungho Kim([email protected]) FE Development Team/NHN Ent.
*/

'use strict';

var util = tui.util;

var ATTR_WHITE_LIST_RX = /^(src|href|title|data\-+|class|alt|align|style|type|checked)/g;

/**
* htmlSanitizer
* @api
* @exports htmlSanitizer
* @param {string|Node} html html or Node
* @param {boolean} [needHtmlText] pass true if need html text
* @returns {string|DocumentFragment} result
*/
function htmlSanitizer(html, needHtmlText) {
var $html = $('<div />');

$html.append(html);

removeUnnecessaryTags($html);
leaveOnlyWhitelistAttribute($html);

return finalizeHtml($html, needHtmlText);
}

/**
* Remove unnecessary tags
* @private
* @param {jQuery} $html jQuery instance
*/
function removeUnnecessaryTags($html) {
$html.find('script, iframe, textarea, form, button, select, .Apple-converted-space').remove();
}

/**
* Leave only white list attributes
* @private
* @param {jQuery} $html jQuery instance
*/
function leaveOnlyWhitelistAttribute($html) {
$html.find('*').each(function(index, node) {
var blacklist = util.toArray(node.attributes).filter(function(attr) {
return !attr.name.match(ATTR_WHITE_LIST_RX);
});

util.forEachArray(blacklist, function(attr) {
node.attributes.removeNamedItem(attr.name);
});
});
}

/**
* Finalize html result
* @private
* @param {jQuery} $html jQuery instance
* @param {boolean} needHtmlText pass true if need html text
* @returns {string|DocumentFragment} result
*/
function finalizeHtml($html, needHtmlText) {
var returnValue, frag;

if (needHtmlText) {
returnValue = $html[0].innerHTML;
} else {
frag = document.createDocumentFragment();
$(frag).append($html[0].innerHTML);
returnValue = frag;
}

return returnValue;
}

module.exports = htmlSanitizer;
8 changes: 6 additions & 2 deletions apps/core/src/js/wwPasteContentHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
'use strict';

var domUtils = require('./domUtils');
var htmlSanitizer = require('./htmlSanitizer');

var util = tui.util;

Expand All @@ -32,7 +33,7 @@ WwPasteContentHelper.prototype.preparePaste = function(pasteData) {
var firstBlockIsTaken = false;
var nodeName, node, childNodes;

this._pasteFirstAid(pasteData.fragment);
pasteData.fragment = this._pasteFirstAid(pasteData.fragment);

childNodes = util.toArray(pasteData.fragment.childNodes);

Expand Down Expand Up @@ -88,12 +89,13 @@ WwPasteContentHelper.prototype._wrapTextNodeWithDiv = function(fragment) {
* Processing paste data after paste
* @param {DocumentFragment} fragment Pasting data
* @memberOf WwPasteContentHelper
* @returns {DocumentFragment}
* @private
*/
WwPasteContentHelper.prototype._pasteFirstAid = function(fragment) {
var self = this;

$(fragment).find('iframe, script, select, form, button, .Apple-converted-space').remove();
fragment = htmlSanitizer(fragment);

this._removeUnnecessaryBlocks(fragment);
this._removeStyles(fragment);
Expand All @@ -108,6 +110,8 @@ WwPasteContentHelper.prototype._pasteFirstAid = function(fragment) {
$(fragment).find('*').each(function() {
self._removeStyles(this);
});

return fragment;
};

/**
Expand Down
8 changes: 4 additions & 4 deletions apps/core/test/convertor.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ describe('Convertor', function() {

describe('markdown to html', function() {
it('converting markdown to html', function() {
expect(convertor.toHTML('# HELLO WORLD')).toEqual('<h1 id="hello-world">HELLO WORLD</h1>\n');
expect(convertor.toHTMLWithCodeHightlight('# HELLO WORLD')).toEqual('<h1 id="hello-world">HELLO WORLD</h1>\n');
expect(convertor.toHTML('# HELLO WORLD')).toEqual('<h1>HELLO WORLD</h1>\n');
expect(convertor.toHTMLWithCodeHightlight('# HELLO WORLD')).toEqual('<h1>HELLO WORLD</h1>\n');
});

it('sanitize script tags', function() {
expect(convertor.toHTML('<script>alert("test");</script>')).toEqual('&lt;script&gt;alert("test");&lt;/script&gt;');
expect(convertor.toHTMLWithCodeHightlight('<script>alert("test");</script>')).toEqual('&lt;script&gt;alert("test");&lt;/script&gt;');
expect(convertor.toHTML('<script>alert("test");</script>')).toEqual('');
expect(convertor.toHTMLWithCodeHightlight('<script>alert("test");</script>')).toEqual('');
});

it('escape vertical bar', function() {
Expand Down
28 changes: 28 additions & 0 deletions apps/core/test/htmlSanitizer.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
'use strict';

var htmlSanitizer = require('../src/js/htmlSanitizer');

describe('htmlSanitizer', function() {
describe('tags', function() {
it('escape script tags to text', function() {
expect(htmlSanitizer('<script>alert("test");</script>', true)).toEqual('');
});
});

describe('attributes', function() {
it('Remove all attritube but white list', function() {
var $img;
var html = '<img class="V" title="V" data-custom="V" src="http://www.nhnent.com/renewal/img/ci_nhnent.png" onload="dd=1" />';
var dom = htmlSanitizer(html);

$img = $(dom).find('img');

expect($img.attr('src')).toBeTruthy();
expect($img.attr('class')).toBeTruthy();
expect($img.attr('title')).toBeTruthy();
expect($img.attr('data-custom')).toBeTruthy();

expect($img.attr('onload')).not.toBeDefined();
});
});
});

0 comments on commit 3e42a73

Please sign in to comment.