Skip to content

Commit

Permalink
Release 21.1.11
Browse files Browse the repository at this point in the history
Cherry-picked changesets:
  d552252 Anton Kuznetsov - HtmlEditor: prevent XSS vulnerability (script tag and inline handlers) (#22907) (#22917)
  • Loading branch information
alexslavr committed Oct 21, 2022
1 parent a0107e4 commit 48f2cb4
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 59 deletions.
3 changes: 0 additions & 3 deletions .vscode/extensions.json

This file was deleted.

39 changes: 0 additions & 39 deletions .vscode/launch.json

This file was deleted.

5 changes: 0 additions & 5 deletions .vscode/settings.json

This file was deleted.

51 changes: 50 additions & 1 deletion js/ui/html_editor/ui.html_editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,54 @@ const HtmlEditor = Editor.inherit({
return this._$submitElement;
},

_removeXSSVulnerableHtml: function(value) {
// NOTE: Script tags and inline handlers are removed to prevent XSS attacks.
// "Blocked script execution in 'about:blank' because the document's frame is sandboxed and the 'allow-scripts' permission is not set."
// error can be logged to the console if the html value is XSS vulnerable.

const $frame = $('<iframe>')
.css('display', 'none')
.attr({
id: 'xss-frame',
sandbox: 'allow-same-origin'
})
.appendTo('body');

const frame = $frame.get(0);
const frameWindow = frame.contentWindow;
const frameDocument = frameWindow.document;
const frameDocumentBody = frameDocument.body;

frameDocumentBody.innerHTML = value;

const removeInlineHandlers = (element) => {
if(element.attributes) {
for(let i = 0; i < element.attributes.length; i++) {
const name = element.attributes[i].name;
if(name.indexOf('on') === 0) {
element.removeAttribute(name);
}
}
}
if(element.childNodes) {
for(let i = 0; i < element.childNodes.length; i++) {
removeInlineHandlers(element.childNodes[i]);
}
}
};

removeInlineHandlers(frameDocumentBody);

$(frameDocumentBody)
.find('script')
.remove();

const sanitizedHtml = frameDocumentBody.innerHTML;

$frame.remove();
return sanitizedHtml;
},

_updateContainerMarkup: function() {
let markup = this.option('value');

Expand All @@ -160,7 +208,8 @@ const HtmlEditor = Editor.inherit({
}

if(markup) {
this._$htmlContainer.html(markup);
const sanitizedMarkup = this._removeXSSVulnerableHtml(markup);
this._$htmlContainer.html(sanitizedMarkup);
}
},

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,69 @@ export default function() {
});
});

testModule('xss security', {
beforeEach: function() {
window._isScriptExecuted = false;
window._isInlineHandlerExecuted = false;

this.htmlWithScript = '<script>window._isScriptExecuted = true;</script>';
this.htmlWithInlineHandler = '<img src="undefined" onerror="window._isInlineHandlerExecuted = true;"/>';
},
afterEach: function() {
delete window._isScriptExecuted;
delete window._isInlineHandlerExecuted;
}
}, () => {
test('script embedded in html value should not be executed on init', function(assert) {
const done = assert.async();

$('#htmlEditor').dxHtmlEditor({
value: this.htmlWithScript
});

setTimeout(() => {
assert.strictEqual(window._isScriptExecuted, false, 'script was not executed');
done();
}, 100);
});

test('inline handler embedded in html value should not be executed on init', function(assert) {
const done = assert.async();

$('#htmlEditor').dxHtmlEditor({
value: this.htmlWithInlineHandler
});

setTimeout(() => {
assert.strictEqual(window._isInlineHandlerExecuted, false, 'inline handler was not executed');
done();
}, 100);
});

test('value change to html with embedded script should not execute the script', function(assert) {
const done = assert.async();

const htmlEditor = $('#htmlEditor').dxHtmlEditor({}).dxHtmlEditor('instance');
htmlEditor.option('value', this.htmlWithScript);

setTimeout(() => {
assert.strictEqual(window._isScriptExecuted, false, 'script was not executed');
done();
}, 100);
});

test('value change to html with embedded inline handler should not execute the handler', function(assert) {
const done = assert.async();

const htmlEditor = $('#htmlEditor').dxHtmlEditor({}).dxHtmlEditor('instance');
htmlEditor.option('value', this.htmlWithInlineHandler);

setTimeout(() => {
assert.strictEqual(window._isInlineHandlerExecuted, false, 'inline handler was not executed');
done();
}, 100);
});
});

testModule('Value as Markdown markup', {
beforeEach: function() {
Expand Down
11 changes: 0 additions & 11 deletions themebuilder-scss/dart-compiler/.vscode/launch.json

This file was deleted.

0 comments on commit 48f2cb4

Please sign in to comment.