diff --git a/test/patch/parse.js b/test/patch/parse.js index a1a09c9c..2a6bf986 100644 --- a/test/patch/parse.js +++ b/test/patch/parse.js @@ -425,5 +425,103 @@ Index: test2 } ]); }); + + it('should tolerate patches with extra trailing newlines after hunks', () => { + // Regression test for https://github.com/kpdecker/jsdiff/issues/524 + // Not only are these considered valid by GNU patch, but jsdiff's own formatPatch method + // emits patches like this, which jsdiff used to then be unable to parse! + const patchStr = `--- foo 2024-06-14 22:16:31.444276792 +0100 ++++ bar 2024-06-14 22:17:14.910611219 +0100 +@@ -1,7 +1,7 @@ + first + second + third +-fourth +-fifth ++vierte ++fünfte + sixth + seventh + +`; + expect(parsePatch(patchStr)).to.eql([{ + oldFileName: 'foo', + oldHeader: '2024-06-14 22:16:31.444276792 +0100', + newFileName: 'bar', + newHeader: '2024-06-14 22:17:14.910611219 +0100', + hunks: [ + { + oldStart: 1, + oldLines: 7, + newStart: 1, + newLines: 7, + lines: [ + ' first', + ' second', + ' third', + '-fourth', + '-fifth', + '+vierte', + '+fünfte', + ' sixth', + ' seventh' + ] + } + ] + }]); + }); + + it("shouldn't be caught out by removal/addition of lines starting with -- or ++", () => { + // The patch below is a valid patch generated by diffing this file, foo: + + // first + // second + // third + // -- bla + // fifth + // sixth + + // against this file, bar: + + // first + // second + // third + // ++ bla + // fifth + // sixth + // seventh + + // with the command `diff -u0 foo bar`. (All lines in `foo` and `bar` have no leading + // whitespace and a trailing LF.) + + // This is effectively an adversarial example meant to catch out a parser that tries to + // detect the end of a file in a multi-file diff by looking for lines starting with '---', + // '+++', and then '@@'. jsdiff used to do this. However, as this example illustrates, it is + // unsound, since the '---' and '+++' lines might actually just represent the deletion and + // insertion of lines starting with '--' and '++'. The only way to disambiguate these + // interpretations is to heed the line counts in the @@ hunk headers; you *cannot* reliably + // determine where a hunk or file ends in a unified diff patch without heeding those line + // counts. + + const patchStr = `--- foo 2024-06-14 21:57:04.341065736 +0100 ++++ bar 2024-06-14 22:00:57.988080321 +0100 +@@ -4 +4 @@ +--- bla ++++ bla +@@ -6,0 +7 @@ ++seventh +`; + + expect(parsePatch(patchStr)).to.eql([{ + oldFileName: 'foo 2024-06-14 21:57:04.341065736 +0100', + oldHeader: '', + newFileName: 'bar 2024-06-14 22:00:57.988080321 +0100', + newHeader: '', + hunks: [ + { oldStart: 4, oldLines: 1, newStart: 4, newLines: 1, lines: ['--- bla', '+++ bla'] }, + { oldStart: 7, oldLines: 0, newStart: 7, newLines: 1, lines: ['+seventh'] } + ] + }]); + }); }); });