From c2195a625722c1208632eccd52242bc17310c14d Mon Sep 17 00:00:00 2001 From: Tony Spataro Date: Thu, 28 Apr 2022 09:37:36 -0700 Subject: [PATCH 1/2] Allow early termination to limit execution time with degenerate cases (e.g. enormous line diffs) --- src/diff/base.js | 7 +++---- src/patch/create.js | 4 ++++ test/diff/line.js | 18 ++++++++++++++++++ test/patch/create.js | 13 +++++++++++++ 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/src/diff/base.js b/src/diff/base.js index b709585d4..9d5fd76ab 100644 --- a/src/diff/base.js +++ b/src/diff/base.js @@ -29,7 +29,7 @@ Diff.prototype = { let newLen = newString.length, oldLen = oldString.length; let editLength = 1; - let maxEditLength = newLen + oldLen; + let maxEditLength = options.maxEditLength || newLen + oldLen; let bestPath = [{ newPos: -1, components: [] }]; // Seed editLength = 0, i.e. the content starts with the same values @@ -87,12 +87,11 @@ Diff.prototype = { // Performs the length of edit iteration. Is a bit fugly as this has to support the // sync and async mode which is never fun. Loops over execEditLength until a value - // is produced. + // is produced, or until the edit length exceeds options.maxEditLength (if given), + // in which case it will return undefined. if (callback) { (function exec() { setTimeout(function() { - // This should not happen, but we want to be safe. - /* istanbul ignore next */ if (editLength > maxEditLength) { return callback(); } diff --git a/src/patch/create.js b/src/patch/create.js index a48e1615c..384865457 100644 --- a/src/patch/create.js +++ b/src/patch/create.js @@ -9,6 +9,10 @@ export function structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHea } const diff = diffLines(oldStr, newStr, options); + if(!diff) { + return; + } + diff.push({value: '', lines: []}); // Append an empty value to make cleanup easier function contextLines(lines) { diff --git a/test/diff/line.js b/test/diff/line.js index 5c2c76578..6bc1e1832 100644 --- a/test/diff/line.js +++ b/test/diff/line.js @@ -46,6 +46,24 @@ describe('diff/line', function() { ''); expect(convertChangesToXML(diffResult)).to.equal('line\n\nold value \n\nline'); }); + + describe('given options.maxEditLength', function() { + it('terminates early', function() { + const diffResult = diffLines( + 'line\nold value\nline', + 'line\nnew value\nline', { maxEditLength: 1 }); + expect(diffResult).to.be.undefined; + }); + it('terminates early - async', function(done) { + function callback(diffResult) { + expect(diffResult).to.be.undefined; + done(); + } + diffLines( + 'line\nold value\nline', + 'line\nnew value\nline', { callback, maxEditLength: 1 }); + }); + }); }); // Trimmed Line Diff diff --git a/test/patch/create.js b/test/patch/create.js index 1315df564..5870b654b 100644 --- a/test/patch/create.js +++ b/test/patch/create.js @@ -649,6 +649,19 @@ describe('patch/create', function() { }] }); }); + + describe('given options.maxEditLength', function() { + const options = { maxEditLength: 1 }; + + it('terminates early', function() { + const res = structuredPatch( + 'oldfile', 'newfile', + 'line2\nline3\nline4\n', 'line2\nline3\nline5', + 'header1', 'header2', options + ); + expect(res).to.be.undefined; + }); + }); }); describe('#formatPatch', function() { From 845e92b9f0791e7f0daa55e540855f602f1e8885 Mon Sep 17 00:00:00 2001 From: Tony Spataro Date: Fri, 6 May 2022 14:03:32 -0700 Subject: [PATCH 2/2] Cap maxEditLength to maximum useful size --- src/diff/base.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/diff/base.js b/src/diff/base.js index 9d5fd76ab..b11e7e523 100644 --- a/src/diff/base.js +++ b/src/diff/base.js @@ -29,7 +29,11 @@ Diff.prototype = { let newLen = newString.length, oldLen = oldString.length; let editLength = 1; - let maxEditLength = options.maxEditLength || newLen + oldLen; + let maxEditLength = newLen + oldLen; + if(options.maxEditLength) { + maxEditLength = Math.min(maxEditLength, options.maxEditLength); + } + let bestPath = [{ newPos: -1, components: [] }]; // Seed editLength = 0, i.e. the content starts with the same values