forked from lxm_front/Mergely
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b717abdd1e | ||
|
|
d2b38249ae | ||
|
|
217674cd07 | ||
|
|
3812dd6026 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
/node_modules
|
/node_modules
|
||||||
/lib
|
/lib
|
||||||
mergely-*.tgz
|
mergely-*.tgz
|
||||||
|
package-lock.json
|
||||||
|
|||||||
12
CHANGES.md
12
CHANGES.md
@@ -1,5 +1,17 @@
|
|||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
|
## 4.0.6
|
||||||
|
|
||||||
|
* #89: fixes missing merge buttons
|
||||||
|
|
||||||
|
## 4.0.5
|
||||||
|
|
||||||
|
* #85: fixes XSS vulnerability with DOM id
|
||||||
|
|
||||||
|
## 4.0.2
|
||||||
|
|
||||||
|
* #83: fixes poor rendering performance
|
||||||
|
|
||||||
## 4.0.0
|
## 4.0.0
|
||||||
|
|
||||||
### Breaking changes
|
### Breaking changes
|
||||||
|
|||||||
2
package.json
Normal file → Executable file
2
package.json
Normal file → Executable file
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mergely",
|
"name": "mergely",
|
||||||
"version": "4.0.3",
|
"version": "4.0.6",
|
||||||
"description": "A javascript UI for diff/merge",
|
"description": "A javascript UI for diff/merge",
|
||||||
"directories": {
|
"directories": {
|
||||||
"doc": "doc",
|
"doc": "doc",
|
||||||
|
|||||||
@@ -461,11 +461,11 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, {
|
|||||||
lineNumbers: this.settings.line_numbers
|
lineNumbers: this.settings.line_numbers
|
||||||
};
|
};
|
||||||
var lhs_gutters = [];
|
var lhs_gutters = [];
|
||||||
if (this.lhs_cmsettings.line_numbers) {
|
if (this.lhs_cmsettings.lineNumbers) {
|
||||||
lhs_gutters = ['merge', 'CodeMirror-linenumbers']
|
lhs_gutters = ['merge', 'CodeMirror-linenumbers']
|
||||||
}
|
}
|
||||||
var rhs_gutters = [];
|
var rhs_gutters = [];
|
||||||
if (this.rhs_cmsettings.line_numbers) {
|
if (this.rhs_cmsettings.lineNumbers) {
|
||||||
rhs_gutters = ['merge', 'CodeMirror-linenumbers']
|
rhs_gutters = ['merge', 'CodeMirror-linenumbers']
|
||||||
}
|
}
|
||||||
jQuery.extend(true, this.lhs_cmsettings, this.settings.cmsettings, { gutters: lhs_gutters }, this.settings);
|
jQuery.extend(true, this.lhs_cmsettings, this.settings.cmsettings, { gutters: lhs_gutters }, this.settings);
|
||||||
@@ -659,6 +659,13 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, {
|
|||||||
bind: function(el) {
|
bind: function(el) {
|
||||||
this.element.hide();
|
this.element.hide();
|
||||||
this.id = jQuery(el).attr('id');
|
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.changed_timeout = null;
|
||||||
this.chfns = {};
|
this.chfns = {};
|
||||||
this.chfns[this.id + '-lhs'] = [];
|
this.chfns[this.id + '-lhs'] = [];
|
||||||
@@ -676,7 +683,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// homebrew
|
// homebrew
|
||||||
var style = 'opacity:0.4;width:10px;height:15px;background-color:#888;cursor:pointer;text-align:center;color:#eee;border:1px solid #222;margin-right:5px;margin-top: -2px;';
|
var style = 'opacity:0.6;height:16px;background-color:#bfbfbf;cursor:pointer;text-align:center;color:#eee;border:1px solid #848484;margin-right:5px;margin-top:-2px;';
|
||||||
merge_lhs_button = '<div style="' + style + '" title="Merge left"><</div>';
|
merge_lhs_button = '<div style="' + style + '" title="Merge left"><</div>';
|
||||||
merge_rhs_button = '<div style="' + style + '" title="Merge right">></div>';
|
merge_rhs_button = '<div style="' + style + '" title="Merge right">></div>';
|
||||||
}
|
}
|
||||||
@@ -686,17 +693,32 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, {
|
|||||||
// create the textarea and canvas elements
|
// create the textarea and canvas elements
|
||||||
var height = '10px';
|
var height = '10px';
|
||||||
var width = '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>'));
|
var splash = jQuery('<div id="mergely-splash">');
|
||||||
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>'));
|
var canvasLhs = jQuery(`<div class="mergely-margin" style="height: '${height}'"><canvas id="lhs-margin" width="8px" height="'${height}'"></canvas></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>'));
|
canvasLhs.find('#lhs-margin').attr('id', `${this.id}-lhs-margin`);
|
||||||
var rmargin = jQuery('<div class="mergely-margin" style="height: ' + height + '"><canvas id="' + this.id + '-rhs-margin" width="8px" height="' + height + '"></canvas></div>');
|
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') {
|
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') {
|
if (this.settings.rhs_margin != 'left') {
|
||||||
this.element.append(rmargin);
|
this.element.append(canvasRhs);
|
||||||
}
|
}
|
||||||
if (!this.settings.sidebar) {
|
if (!this.settings.sidebar) {
|
||||||
this.element.find('.mergely-margin').css({display: 'none'});
|
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
|
// get current diff border color
|
||||||
var color = jQuery('<div style="display:none" class="mergely current start" />').appendTo('body').css('border-top-color');
|
var color = jQuery('<div style="display:none" class="mergely current start" />').appendTo('body').css('border-top-color');
|
||||||
this.current_diff_color = color;
|
this.current_diff_color = color;
|
||||||
|
|
||||||
// codemirror
|
// codemirror
|
||||||
var cmstyle = '#' + this.id + ' .CodeMirror-gutter-text { padding: 5px 0 0 0; }' +
|
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; }' +
|
'#${this.id} .CodeMirror-lines pre, #${this.id} .CodeMirror-gutter-text pre { line-height: 18px; }
|
||||||
'.CodeMirror-linewidget { overflow: hidden; };';
|
'.CodeMirror-linewidget { overflow: hidden; };`;
|
||||||
if (this.settings.autoresize) {
|
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
|
// adjust the margin line height
|
||||||
cmstyle += '\n.CodeMirror { line-height: 18px; }';
|
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
|
// 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;
|
|
||||||
}
|
|
||||||
var self = this;
|
var self = this;
|
||||||
this.editor = [];
|
this.editor = [];
|
||||||
this.editor[this.id + '-lhs'] = CodeMirror.fromTextArea(lhstx, this.lhs_cmsettings);
|
this.editor[this.id + '-lhs'] = CodeMirror.fromTextArea(lhstx, this.lhs_cmsettings);
|
||||||
|
|||||||
@@ -20,9 +20,10 @@ describe('mergely', function () {
|
|||||||
};
|
};
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
$('#mergely').mergely('unbind');
|
const mergely = $('#mergely');
|
||||||
$('#mergely').mergelyUnregister();
|
mergely.mergely('unbind');
|
||||||
$('#mergely').remove();
|
mergely.mergelyUnregister();
|
||||||
|
mergely.remove();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('initialization', () => {
|
describe('initialization', () => {
|
||||||
@@ -401,6 +402,7 @@ describe('mergely', function () {
|
|||||||
for (let i = 0; i < opt.next; ++i) {
|
for (let i = 0; i < opt.next; ++i) {
|
||||||
$('#mergely').mergely('scrollToDiff', 'next');
|
$('#mergely').mergely('scrollToDiff', 'next');
|
||||||
}
|
}
|
||||||
|
expect($('.merge-button').length > 0).to.be.true;
|
||||||
$('#mergely').mergely('mergeCurrentChange', opt.dir);
|
$('#mergely').mergely('mergeCurrentChange', opt.dir);
|
||||||
if (opt.dir === 'lhs') {
|
if (opt.dir === 'lhs') {
|
||||||
expect($('#mergely').mergely('get', 'lhs')).to.equal(opt.expect || opt.rhs);
|
expect($('#mergely').mergely('get', 'lhs')).to.equal(opt.expect || opt.rhs);
|
||||||
@@ -524,5 +526,30 @@ describe('mergely', function () {
|
|||||||
done();
|
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();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user