Compare commits

..

2 Commits
4.0.3 ... 4.0.5

Author SHA1 Message Date
Jamie Peabody
217674cd07 patch(fix #85): better XSS fix (#87)
* patch(fix #85): fixes xss vulnerability

* patch(fix #85): better XSS fix
2018-06-23 03:36:38 -07:00
Jamie Peabody
3812dd6026 patch(fix #85): fixes xss vulnerability (#86) 2018-06-21 14:04:36 -07:00
3 changed files with 86 additions and 28 deletions

2
package.json Normal file → Executable file
View File

@@ -1,6 +1,6 @@
{
"name": "mergely",
"version": "4.0.3",
"version": "4.0.5",
"description": "A javascript UI for diff/merge",
"directories": {
"doc": "doc",

View File

@@ -659,6 +659,13 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, {
bind: function(el) {
this.element.hide();
this.id = jQuery(el).attr('id');
try {
// ensure the id is valid for jQuery
jQuery(`#${this.id}`);
} catch (ex) {
console.error(`jQuery failed to find mergely: #${this.id}`);
return;
}
this.changed_timeout = null;
this.chfns = {};
this.chfns[this.id + '-lhs'] = [];
@@ -686,17 +693,32 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, {
// create the textarea and canvas elements
var height = '10px';
var width = '10px';
this.element.append(jQuery('<div id="mergely-splash">'));
this.element.append(jQuery('<div class="mergely-margin" style="height: ' + height + '"><canvas id="' + this.id + '-lhs-margin" width="8px" height="' + height + '"></canvas></div>'));
this.element.append(jQuery('<div style="position:relative;width:' + width + '; height:' + height + '" id="' + this.id + '-editor-lhs" class="mergely-column"><textarea style="" id="' + this.id + '-lhs"></textarea></div>'));
this.element.append(jQuery('<div class="mergely-canvas" style="height: ' + height + '"><canvas id="' + this.id + '-lhs-' + this.id + '-rhs-canvas" style="width:28px" width="28px" height="' + height + '"></canvas></div>'));
var rmargin = jQuery('<div class="mergely-margin" style="height: ' + height + '"><canvas id="' + this.id + '-rhs-margin" width="8px" height="' + height + '"></canvas></div>');
var splash = jQuery('<div id="mergely-splash">');
var canvasLhs = jQuery(`<div class="mergely-margin" style="height: '${height}'"><canvas id="lhs-margin" width="8px" height="'${height}'"></canvas></div>`);
canvasLhs.find('#lhs-margin').attr('id', `${this.id}-lhs-margin`);
var editorLhs = jQuery(`<div style="position:relative;width:'${width}'; height:'${height}'" id="editor-lhs" class="mergely-column"><textarea id="text-lhs"></textarea></div>`);
editorLhs.eq(0).attr('id', `${this.id}-editor-lhs`);
editorLhs.find('#text-lhs').attr('id', `${this.id}-lhs`);
var canvasMid = jQuery(`<div class="mergely-canvas" style="height: '${height}'"><canvas id="lhs-rhs-canvas" style="width:28px" width="28px" height="'${height}'"></canvas></div>`);
canvasMid.find('#mergely-canvas').attr('id', `${this.id}-mergely-canvas`);
canvasMid.find('#lhs-rhs-canvas').attr('id', `${this.id}-lhs-${this.id}-rhs-canvas`);
this.element.append(splash);
this.element.append(canvasLhs);
this.element.append(editorLhs);
this.element.append(canvasMid);
var canvasRhs = jQuery(`<div class="mergely-margin" style="height: '${height}'"><canvas id="rhs-margin" width="8px" height="'${height}'"></canvas></div>`);
canvasRhs.find('#rhs-margin').attr('id', `${this.id}-rhs-margin`);
if (this.settings.rhs_margin == 'left') {
this.element.append(rmargin);
this.element.append(canvasRhs);
}
this.element.append(jQuery('<div style="width:' + width + '; height:' + height + '" id="' + this.id + '-editor-rhs" class="mergely-column"><textarea style="" id="' + this.id + '-rhs"></textarea></div>'));
var editorRhs = jQuery(`<div style="width:'${width}'; height:'${height}'" id="editor-rhs" class="mergely-column"><textarea id="text-rhs"></textarea></div>`);
editorRhs.eq(0).attr('id', `${this.id}-editor-rhs`);
editorRhs.find('#text-rhs').attr('id', `${this.id}-rhs`);
this.element.append(editorRhs);
if (this.settings.rhs_margin != 'left') {
this.element.append(rmargin);
this.element.append(canvasRhs);
}
if (!this.settings.sidebar) {
this.element.find('.mergely-margin').css({display: 'none'});
@@ -734,32 +756,42 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, {
});
}
// check initialization
var rhstx;
try {
rhstx = this.element.find(`#${this.id}-rhs`).get(0);
} catch (ex) {
}
if (!rhstx) {
console.error('rhs textarea not defined - Mergely not initialized properly');
return;
}
var lhstx;
try {
lhstx = this.element.find(`#${this.id}-lhs`).get(0);
} catch (ex) {
}
if (!lhstx) {
console.error('lhs textarea not defined - Mergely not initialized properly');
return;
}
// get current diff border color
var color = jQuery('<div style="display:none" class="mergely current start" />').appendTo('body').css('border-top-color');
this.current_diff_color = color;
// codemirror
var cmstyle = '#' + this.id + ' .CodeMirror-gutter-text { padding: 5px 0 0 0; }' +
'#' + this.id + ' .CodeMirror-lines pre, ' + '#' + this.id + ' .CodeMirror-gutter-text pre { line-height: 18px; }' +
'.CodeMirror-linewidget { overflow: hidden; };';
var cmstyle = `#${this.id} .CodeMirror-gutter-text { padding: 5px 0 0 0; }
'#${this.id} .CodeMirror-lines pre, #${this.id} .CodeMirror-gutter-text pre { line-height: 18px; }
'.CodeMirror-linewidget { overflow: hidden; };`;
if (this.settings.autoresize) {
cmstyle += this.id + ' .CodeMirror-scroll { height: 100%; overflow: auto; }';
cmstyle += `${this.id} .CodeMirror-scroll { height: 100%; overflow: auto; }`;
}
// adjust the margin line height
cmstyle += '\n.CodeMirror { line-height: 18px; }';
jQuery('<style type="text/css">' + cmstyle + '</style>').appendTo('head');
jQuery(`<style type="text/css">${cmstyle}</style>`).appendTo('head');
//bind
var rhstx = this.element.find('#' + this.id + '-rhs').get(0);
if (!rhstx) {
console.error('rhs textarea not defined - Mergely not initialized properly');
return;
}
var lhstx = this.element.find('#' + this.id + '-lhs').get(0);
if (!rhstx) {
console.error('lhs textarea not defined - Mergely not initialized properly');
return;
}
// bind
var self = this;
this.editor = [];
this.editor[this.id + '-lhs'] = CodeMirror.fromTextArea(lhstx, this.lhs_cmsettings);

View File

@@ -20,9 +20,10 @@ describe('mergely', function () {
};
afterEach(() => {
$('#mergely').mergely('unbind');
$('#mergely').mergelyUnregister();
$('#mergely').remove();
const mergely = $('#mergely');
mergely.mergely('unbind');
mergely.mergelyUnregister();
mergely.remove();
});
describe('initialization', () => {
@@ -524,5 +525,30 @@ describe('mergely', function () {
done();
});
});
it('should not be vulnerable to XSS', function (done) {
function initXSS(options) {
$('body').get(0).innerHTML = "<!DOCTYPE html><html lang=\"en\"><body><div id='mergely<script id=\"injected\">alert(123)</script>'></div></body></html>";
const divs = document.getElementsByTagName('div');
editor = $(divs[0]);
editor.mergely(options);
return editor;
};
$(document).ready(() => {
const editor = initXSS({
height: 100,
viewport: true,
license: 'lgpl-separate-notice',
lhs: (setValue) => setValue(macbeth),
rhs: (setValue) => setValue(macbeth)
});
expect($('body').find('#injected')).to.have.length(0, 'expected no div with id injected');
const divs = document.getElementsByTagName('div');
expect(divs).to.have.length(1);
expect(divs[0].id).to.equal('mergely<script id="injected">alert(123)</script>');
done();
});
});
});
});